2008-09-21 9 views

Respuesta

227

Cuando se trata de consultas a bases de datos, siempre intente utilizar consultas con parámetros preparados. Las bibliotecas mysqli y PDO son compatibles. Esto es infinitamente más seguro que usar funciones de escape como mysql_real_escape_string.

Sí, mysql_real_escape_string es efectivamente solo una función de escape de cadenas. No es una bala mágica. Todo lo que hará es escapar de los caracteres peligrosos para que puedan ser seguros de usar en una sola cadena de consulta. Sin embargo, si no desinfecta sus entradas de antemano, entonces será vulnerable a ciertos vectores de ataque.

Imagínese el siguiente SQL:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']); 

usted debería ser capaz de ver que esto es vulnerable a explotar.
Imagínese el parámetro id contenía el vector de ataque común:

1 OR 1=1 

No hay caracteres de riesgo en que hay que codificar, por lo que pasará directamente a través del filtro de escape. dejándonos:

SELECT fields FROM table WHERE id= 1 OR 1=1 

que es un vector de inyección SQL precioso y permitiría al atacante para devolver todas las filas. O

1 or is_admin=1 order by id limit 1 

que produce

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1 

que permite que el atacante se devuelven detalles de la primera de administrador en este ejemplo completamente ficticia.

Aunque estas funciones son útiles, deben utilizarse con cuidado. Debe asegurarse de que todas las entradas web estén validadas hasta cierto punto. En este caso, vemos que podemos ser explotados porque no verificamos que una variable que utilizamos como número sea en realidad numérica. En PHP se debe utilizar ampliamente un conjunto de funciones para comprobar que las entradas son números enteros, flotadores, etc. alfanumérica Pero cuando se trata de SQL, prestaron atención más el valor de la declaración preparada. El código anterior habría sido seguro si fuera una declaración preparada ya que las funciones de la base de datos habrían sabido que 1 OR 1=1 no es un literal válido.

En cuanto a htmlspecialchars(). Ese es un campo de minas propio.

Hay un problema real en PHP, ya que tiene toda una selección de diferentes funciones de escape asociados con HTML, y sin una orientación clara sobre exactamente qué funciones lo hacen.

En primer lugar, si se encuentra dentro de una etiqueta HTML, que está en problemas reales.Miramos

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />'; 

Ya estamos dentro de una etiqueta HTML, por lo que no necesitamos < o> para hacer algo peligroso. Nuestro vector de ataque podría ser sólo javascript:alert(document.cookie)

HTML resultante Ahora parece

<img src= "javascript:alert(document.cookie)" /> 

El ataque va directamente a través.

Se pone peor. ¿Por qué? porque htmlspecialchars (cuando se llama de esta manera) solo codifica comillas dobles y no solo. Así que si tuviéramos

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />"; 

Nuestro atacante mal que ahora puede inyectar parámetros enteros nuevos

pic.png' onclick='location.href=xxx' onmouseover='... 

nos da

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' /> 

En estos casos, no hay una varita mágica, sólo hay que santise la entrada usted mismo. Si intentas filtrar los caracteres incorrectos, seguramente fallarás. Tome un enfoque de lista blanca y solo deje pasar los caracteres que son buenos. Mire XSS cheat sheet para ver ejemplos de cómo diversos vectores pueden ser

Incluso si usa htmlspecialchars($string) fuera de las etiquetas HTML, sigue siendo vulnerable a los vectores de ataque de juego de caracteres de varios bytes.

Lo más eficaz que puede ser es utilizar una combinación de mb_convert_encoding y htmlentities de la siguiente manera.

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8'); 
$str = htmlentities($str, ENT_QUOTES, 'UTF-8'); 

Incluso esto deja IE6 vulnerable, debido a la forma en que maneja UTF. Sin embargo, podría recurrir a una codificación más limitada, como ISO-8859-1, hasta que el uso de IE6 disminuya.

Para un estudio más en profundidad de los problemas de varios bytes, ver https://stackoverflow.com/a/12118602/1820

+23

Lo único que se echa de menos aquí, es que el primer ejemplo para la consulta DB ... un simple intval() resolvería la inyección. Siempre use intval() en lugar de mysqlescape ...() cuando necesite un número y no una cadena. –

+10

y recuerde que el uso de consultas parametrizadas le permitirá tener siempre datos tratados como datos y no como código. Use una biblioteca como PDO y use consultas parametrizadas siempre que sea posible. – Cheekysoft

+1

¡Excelente respuesta! – joedevon

9

Además de una excelente respuesta de Cheekysoft:

  • Sí, van a mantenerse a salvo, pero sólo si se utilizan absolutamente correcto Úselos de forma incorrecta y seguirá siendo vulnerable, y puede tener otros problemas (por ejemplo, corrupción de datos)
  • Utilice las consultas parametrizadas en su lugar (como se indicó anteriormente). Puedes usarlos a través de, por ejemplo, PDO o mediante un contenedor como PEAR DB
  • Asegúrese de que magic_quotes_gpc y magic_quotes_runtime estén desactivados en todo momento y que nunca se enciendan accidentalmente, ni siquiera brevemente. Estos son un intento temprano y profundamente equivocada por los desarrolladores de PHP para evitar problemas de seguridad (que destruye datos)

En realidad no hay una bala de plata para la prevención de la inyección HTML (por ejemplo, cross site scripting), pero es posible que pueda para lograrlo más fácilmente si está utilizando una biblioteca o sistema de plantillas para generar HTML. Lea la documentación de cómo escapar de las cosas de manera apropiada.

En HTML, las cosas deben escaparse de manera diferente según el contexto. Esto es especialmente cierto de las cadenas que se colocan en Javascript.

3

Sin duda, de acuerdo con los puestos de arriba, pero tengo una pequeña cosa que añadir en respuesta a la respuesta de Cheekysoft, específicamente:

Cuando se trata de consultas de bases de datos, siempre tratar de utilizar prepara parametrizar consultas. Las bibliotecas de PDO mysqli y son compatibles. Esta es infinitamente más seguro que usar escapar funciones tales como mysql_real_escape_string.

Sí, mysql_real_escape_string es efectivamente solo una cadena que escapa de la función . No es una bala mágica. Todo lo que hará es una fuga peligrosa personajes con el fin de que puedan ser seguro de usar en una sola cadena de consulta. Sin embargo, si no se desinfectan sus entradas de antemano, entonces usted será vulnerables a ciertos tipos de ataque.

Imagínese el siguiente SQL:

$ resultado = "SELECT campos FROM tabla donde id = " .mysql_real_escape_string ($ _POST [ 'id']);

Usted debe ser capaz de ver que este es vulnerables a la explotación. Imagínese el parámetro id contenía el común ataque vector:

1 OR 1 = 1

No hay caracteres de riesgo en allí para codificar, por lo que pasar directamente a través del filtro escape. Dejando nosotros:

campos SELECT FROM tabla WHERE id = 1 O 1 = 1

codifiqué una pequeña función rápida que puse en mi clase de base de datos que se tira a cabo cualquier cosa que tampoco un número . Utiliza preg_replace, por lo que no es prob una función poco más optimizado, pero funciona en un apuro ...

function Numbers($input) { 
    $input = preg_replace("/[^0-9]/","", $input); 
    if($input == '') $input = 0; 
    return $input; 
} 

Así que en lugar de utilizar

$ resultado = "SELECT campos FROM tabla WHERE ID = ".mysqlrealescapestring (" 1 OR 1 = 1 ");

me gustaría utilizar

$ resultado = "SELECT campos FROM tabla WHERE id =".Números ("1 OR 1 = 1");

y sería ejecutar de forma segura la consulta

campos SELECT FROM tabla WHERE id = 111

Claro, que sólo se detuvo se muestre la fila correcta, pero yo no creo que es un gran problema para quien intente inyectar sql en su sitio;)

+1

¡Perfecto! Este es exactamente el tipo de desinfección que necesita. El código inicial falló porque no validaba que un número fuera numérico. Tu código hace esto. debe llamar a Numbers() en todos los vars de uso de enteros cuyos valores se originen fuera de la base de código. – Cheekysoft

+1

Vale la pena mencionar que intval() funcionará perfectamente bien para esto, ya que PHP coacciona automáticamente enteros a las cadenas por usted. –

+11

Prefiero intval. Gira 1abc2 a 1, no a 12. – jmucchiello

2

Una pieza importante de este rompecabezas son los contextos. enviar "1 OR 1 = 1" como el ID de alguien no es un problema si usted cita todos los argumentos en su consulta:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'" 

que se traduce en:

SELECT fields FROM table WHERE id='1 OR 1=1' 

que es ineficaz. Como está escapando de la cadena, la entrada no puede salir del contexto de cadena. He probado esto hasta la versión 5.0.45 de MySQL, y usar un contexto de cadena para una columna entera no causa ningún problema.

+15

y luego comenzaré mi vector de ataque con el byte de múltiples bytes 0xbf27 que en la base de datos de latin1 se convertirá mediante el filtro fuction como 0xbf5c27, que es un único carácter multibyte seguido de una comilla simple. – Cheekysoft

+8

Intenta no proteger contra un único vector de ataque conocido. Terminarás persiguiendo tu cola hasta el final del tiempo aplicando parche tras parche a tu código. Retroceder y observar los casos generales se enfocará en un código más seguro y una mentalidad enfocada en la seguridad. – Cheekysoft

+0

Estoy de acuerdo; idealmente, OP usará declaraciones preparadas. –

1
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id']; 

Funciona bien, incluso mejor en sistemas de 64 bits. Sin embargo, tenga cuidado con las limitaciones de su sistema para tratar grandes números, pero para las ID de las bases de datos, esto funciona muy bien el 99% del tiempo.

Debe utilizar una única función/método para limpiar sus valores también. Incluso si esta función es solo un contenedor para mysql_real_escape_string(). ¿Por qué? Porque un día, cuando se encuentra un exploit para su método preferido de limpieza de datos, solo tiene que actualizarlo en un lugar, en lugar de buscarlo y reemplazarlo en todo el sistema.

-3

¿por qué, oh POR QUÉ, no incluiría citas alrededor de la entrada del usuario en su declaración sql? parece bastante tonto no hacerlo! incluir citas en su declaración sql haría "1 o 1 = 1" un intento infructuoso, ¿no?

así que ahora, dirá, "¿y si el usuario incluye una cita (o comillas dobles) en la entrada?"

bien, solución fácil para eso: simplemente elimine las citas ingresadas por el usuario. por ejemplo: . ahora, me parece de todos modos, que la entrada del usuario estaría asegurada ...

+0

"¿por qué, oh POR QUÉ, no incluirías comillas alrededor de la entrada del usuario en tu declaración sql?" - La pregunta no dice nada sobre no citar la entrada del usuario. – Quentin

+1

"bien, solución fácil para eso" - Arreglo terrible para eso. Eso arroja datos. La solución mencionada en la pregunta en sí es un mejor enfoque. – Quentin

+0

Aunque estoy de acuerdo en que la pregunta no se refiere a la cotización del usuario, todavía parece que no se debe citar la entrada. y, preferiría arrojar datos que ingresar datos incorrectos. En general, en un ataque de inyección, NO quieres esos datos de todos modos ... ¿verdad? –

Cuestiones relacionadas