sql >> Base de Datos >  >> RDS >> Mysql

¿Son mysql_real_escape_string() y mysql_escape_string() suficientes para la seguridad de la aplicación?

¡@Charles tiene mucha razón!

Te pones en riesgo de múltiples tipos de conocido Ataques SQL, incluidos, como mencionaste

  • Inyección SQL:¡Sí! Mysql_Escape_String probablemente TODAVÍA lo mantenga susceptible a las inyecciones de SQL, dependiendo de dónde use las variables de PHP en sus consultas.

Considere esto:

$sql = "SELECT number FROM PhoneNumbers " .
       "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);  

¿Se puede escapar de esa manera de manera segura y precisa? ¡NO! ¿Por qué? porque un hacker podría muy bien hacer esto:

Repite después de mí:

mysql_real_escape_string() solo está destinado a escapar de los datos variables, NO nombres de tablas, nombres de columnas y especialmente campos no LIMIT.

  • LIKE explota:LIKE "$data%" donde $data podría ser "%" lo que devolvería TODOS los registros... que pueden ser muy bien una vulnerabilidad de seguridad... imagínese una búsqueda de los últimos cuatro dígitos de una tarjeta de crédito... ¡OOP! ¡Ahora los piratas informáticos pueden potencialmente recibir todos los números de tarjetas de crédito en su sistema! (Por cierto:¡casi nunca se recomienda almacenar tarjetas de crédito completas!)

  • Exploits de Charset:no importa lo que digan los enemigos, Internet Explorer todavía , en 2011, vulnerable a Character Set Exploits, y eso es si has diseñado tu página HTML correctamente, con el equivalente a <meta name="charset" value="UTF-8"/> ! Estos ataques son MUY desagradables ya que le dan al hacker tanto control como las inyecciones SQL directas:p. lleno.

Aquí hay un código de ejemplo para demostrar todo esto:

// Contains class DBConfig; database information.
require_once('../.dbcreds');                       

$dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
mysql_select_db(DBConfig::$db);
//print_r($argv);

$sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
               mysql_real_escape_string($argv[1]),
               mysql_real_escape_string($argv[2]),
               mysql_real_escape_string($argv[3]));
echo "SQL: $sql\n";
$qq = mysql_query($sql);
while (($data = mysql_fetch_array($qq)))
{
        print_r($data);
}

Estos son los resultados de este código cuando se pasan varias entradas:

$ php sql_exploits.php url http://www.reddit.com id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://www.reddit.com%'
               ORDER BY id;
Returns: Just URLs beginning w/ "http://www.reddit.com"

$ php sql_exploits.php url % id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE '%%' 
               ORDER BY id;
Results: Returns every result Not what you programmed, ergo an exploit --

$ php sql_exploits.php 1=1'http://www.reddit.com ' id Results:Devuelve todas las columnas y todos los resultados.

Luego están los exploits LIMIT REALMENTE desagradables:

$ php sql_exploits.php url 
> 'http://www.reddit.com'
> "UNION SELECT name FROM CachedDomains"
Generated SQL: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://reddit.com%' 
               LIMIT 1 
               UNION
               SELECT name FROM CachedDomains;
Returns:  An entirely unexpected, potentially (probably) unauthorized query
          from another, completely different table. 

Si entiende el SQL en los ataques o no, es irrelevante. Lo que esto ha demostrado es que mysql_real_escape_string() es fácil eludido incluso por los piratas informáticos más inmaduros. Eso es porque es un mecanismo de defensa REACTIVO. Solo corrige exploits muy limitados y CONOCIDOS en la base de datos.

Todos los escapes NUNCA serán suficientes para asegurar las bases de datos. De hecho, puede REACCIONAR explícitamente a cada explotación CONOCIDA y, en el futuro, lo más probable es que su código se vuelva vulnerable a los ataques que se descubran en el futuro.

La defensa adecuada, y única (realmente), es PROACTIVA:Usar declaraciones preparadas. Las declaraciones preparadas están diseñadas con especial cuidado para que SÓLO se ejecute SQL válido y PROGRAMADO. Esto significa que, cuando se hace correctamente, las probabilidades de que se pueda ejecutar un SQL inesperado se reducen drásticamente.

Teóricamente, las declaraciones preparadas que se implementan perfectamente serían impermeables a TODOS los ataques, conocidos y desconocidos, ya que son una técnica del LADO DEL SERVIDOR, manejada por los SERVIDORES DE BASE DE DATOS MISMOS y las bibliotecas que interactúan con el lenguaje de programación. Por lo tanto, SIEMPRE tiene la garantía de estar protegido contra CADA HACK CONOCIDO, como mínimo.

Y es menos código:

$pdo = new PDO($dsn);

$column = 'url';
$value = 'http://www.stackoverflow.com/';
$limit = 1;

$validColumns = array('url', 'last_fetched');

// Make sure to validate whether $column is a valid search parameter.
// Default to 'id' if it's an invalid column.
if (!in_array($column, $validColumns) { $column = 'id'; }


$statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
                           'WHERE ' . $column . '=? ' .
                           'LIMIT ' . intval($limit));
$statement->execute(array($value));
while (($data = $statement->fetch())) { }

Ahora, eso no fue tan difícil, ¿verdad? Y es cuarenta y siete por ciento menos de código (195 caracteres (PDO) frente a 375 caracteres (mysql_). Eso es lo que yo llamo, "lleno de victoria".

EDITAR:Para abordar toda la controversia que suscitó esta respuesta, permítanme reiterar lo que ya he dicho:

El uso de declaraciones preparadas le permite a uno aprovechar las medidas de protección del propio servidor SQL y, por lo tanto, está protegido de las cosas que la gente del servidor SQL conoce. Debido a este nivel adicional de protección, está mucho más seguro que simplemente usando escapes, sin importar qué tan completo sea.