¿htmlspecialchars y mysql real escape string mantienen mi código PHP a salvo de la inyección?


El día de hoy se hizo una pregunta con respecto a estrategias de validación de entrada en aplicaciones web.

La respuesta superior, al momento de escribir, sugiere en PHP solo usar htmlspecialchars y mysql_real_escape_string.

Mi pregunta es: ¿Es esto siempre suficiente? ¿Hay algo más que debamos saber? ¿Dónde se descomponen estas funciones?

Author: Community, 2008-09-21

6 answers

Cuando se trata de consultas de base de datos, siempre intente usar consultas parametrizadas preparadas. Las bibliotecas mysqli y PDO soportan esto. 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 cadena. No es una solución mágica. Todo lo que hará es escapar de caracteres peligrosos para que puedan ser seguros de usar en una sola cadena de consulta. Sin embargo, si usted no desinfecta sus entradas de antemano, entonces usted será vulnerable a ciertos vectores de ataque.

Imagine el siguiente SQL:

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

Debería ser capaz de ver que esto es vulnerable a explotar.
Imagine que el parámetro id contiene el vector de ataque común:

1 OR 1=1

No hay caracteres riesgosos para 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 encantador y permitiría al atacante devolver todos los 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 al atacante devolver los detalles del primer administrador en este ejemplo completamente ficticio.

Si bien estas funciones son útiles, deben usarse con cuidado. Es necesario asegurarse de que todas las entradas web se validan hasta cierto punto. En este caso, vemos que podemos ser explotados porque no comprobamos que una variable que estábamos usando como un número, era realmente numérico. En PHP se debe utilizar ampliamente un conjunto de funciones para comprobar que las entradas son enteros, flotadores, alfanuméricos, etc. Pero cuando se trata de SQL, prestar atención a la mayoría del valor de la declaración preparada. El código anterior habría sido seguro si fuera una instrucció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.

Como para htmlspecialchars(). Es un campo de minas propio.

Existe un problema real en PHP en que tiene una selección completa de diferentes funciones de escape relacionadas con html, y no hay una guía clara exactamente qué funciones hacen qué.

En primer lugar, si estás dentro de una etiqueta HTML, estás en problemas reales. Mira

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

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

Ahora el HTML resultante se parece a

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

El ataque llega directamente.

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

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

Nuestro malvado atacante ahora puede inyectar parámetros completamente 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 solución mágica, solo tienes que santificar la entrada tú mismo. Si intenta filtrar los personajes malos, seguramente fallará. Tome un enfoque de lista blanca y solo deje pasar los caracteres que son buenos. Mira la hoja de trucos de XSS para ver ejemplos sobre cómo pueden ser diversos vectores

Incluso si usas htmlspecialchars($string) fuera de las etiquetas HTML, estás aún vulnerable a vectores de ataque de charset multi-byte.

Lo más efectivo que puede ser es usar la 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 a 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 se elimine el uso de IE6.

Para un estudio más profundo de los problemas multibyte, ver https://stackoverflow.com/a/12118602/1820

 231
Author: Cheekysoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:32

Además de la excelente respuesta de Cheekysoft:

  • Sí, te mantendrán a salvo, pero solo si se usan correctamente. Si los usa incorrectamente, seguirá siendo vulnerable y puede tener otros problemas (por ejemplo, corrupción de datos)
  • Por favor, utilice consultas parametrizadas en su lugar (como se indicó anteriormente). Puede usarlos a través, por ejemplo, de PDO o a través de una envoltura como PEAR DB
  • Asegúrese de que magic_quotes_gpc y magic_quotes_runtime estén desactivados en todo momento, y nunca accidentalmente se encendió, ni siquiera brevemente. Estos son un intento temprano y profundamente equivocado por los desarrolladores de PHP para evitar problemas de seguridad (que destruye los datos)

No hay realmente una bala de plata para prevenir la inyección de HTML (por ejemplo, cross site scripting), pero es posible que pueda lograrlo más fácilmente si está utilizando una biblioteca o un sistema de plantillas para generar HTML. Lea la documentación para eso para saber cómo escapar de las cosas apropiadamente.

En HTML, las cosas deben ser escapó de manera diferente dependiendo del contexto. Esto es especialmente cierto de las cadenas que se colocan en Javascript.

 9
Author: MarkR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-21 09:10:00

Definitivamente estaría de acuerdo con los mensajes anteriores, pero tengo una pequeña cosa que agregar en respuesta a la respuesta de Cheekysoft, específicamente:

Cuando se trata de consultas de base de datos, siempre trate de usar preparado consultas parametrizadas. El mysqli y Las bibliotecas PDO soportan esto. Esto es infinitamente más seguro que usar escape funciones tales como mysql_real_escape_string.

Sí, mysql_real_escape_string es efectivamente solo una cadena de escape función. No es una magia bala. Todo lo que hará es escapar peligroso personajes para que puedan ser seguro de usar en una sola cadena de consulta. Sin embargo, si usted no desinfecta su entradas de antemano, entonces usted será vulnerable a ciertos vectores de ataque.

Imagine el siguiente SQL:

Result result = " SELECCIONAR campos DE LA tabla DONDE id = ".mysql_real_escape_string($_POST['id']);

Usted debe ser capaz de ver que esto es vulnerable a la explotación. Imagine la identificación parámetro contener el ataque común vector:

1 O 1=1

No hay caracteres riesgosos allí para codificar, por lo que pasará recto a través del filtro de escape. Dejar nosotros:

SELECCIONE campos DE LA tabla DONDE id = 1 O 1 = 1

Codifiqué una pequeña función rápida que puse en mi clase de base de datos que eliminará cualquier cosa que no sea un número. Utiliza preg_replace, por lo que hay prob un poco más función optimizada, pero funciona en un pizca...

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

Así que en lugar de usar

Result result = "SELECCIONAR campos DE LA tabla DONDE id = ".mysqlrealescapestring("1 or 1=1");

Usaría

Result result = "SELECCIONAR campos DE LA tabla DONDE id = ".Números ("1 O 1=1");

Y ejecutaría con seguridad la consulta

SELECCIONE campos DE LA tabla DONDE id = 111

Seguro, que sólo dejó de mostrar la fila correcta, pero no creo que es un gran problema para quienquiera que esté tratando de inyectar sql en su sitio;)

 3
Author: BrilliantWinter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-22 17:26:11

Una pieza importante de este rompecabezas son los contextos. Alguien que envía "1 O 1=1" como ID no es un problema si cita cada argumento en su consulta:

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

Que resulta en:

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

Que es ineficaz. Dado que está escapando la cadena, la entrada no puede salir del contexto de la 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.

 2
Author: Lucas Oman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-22 17:38:54
$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 sus sistemas para abordar grandes números, pero para los ID de base de datos esto funciona muy bien el 99% del tiempo.

También debe usar una sola función/método para limpiar sus valores. Incluso si esta función es solo una envoltura 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 un sistema en todo el sistema buscar y reemplazar.

 2
Author: cnizzardini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-04 21:32:26

¿Por qué, oh POR QUÉ, usted no incluiría comillas alrededor de la entrada del usuario en su sentencia sql? ¡parece una tontería no hacerlo! incluir comillas en su sentencia sql haría que "1 o 1 = 1" fuera un intento infructuoso, ¿no?

Así que ahora, dirás, "¿qué pasa si el usuario incluye una comilla (o comillas dobles) en la entrada?"

Bueno, solución fácil para eso: simplemente elimine las comillas de entrada del usuario. eg: input =~ s/'//g;. ahora, me parece de todos modos, que la entrada del usuario estaría asegurada...

 -3
Author: Jarett L,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-17 19:02:46