2010-05-30 15 views
7
function sanitizeString($var) 
{ 
    $var = stripslashes($var); 
    $var = htmlentities($var); 
    $var = strip_tags($var); 
    return $var; 
} 

function sanitizeMySQL($var) 
{ 
    $var = mysql_real_escape_string($var); 
    $var = sanitizeString($var); 
    return $var; 
} 

Obtuve estas dos funciones de un libro y el autor dice que al usar estas dos, puedo ser más seguro contra XSS (la primera función) y las inyecciones sql (segundo func). ¿Son necesarios todos?¿Son estas dos funciones excesivas para la desinfección?

También para desinfectar, utilizo declaraciones preparadas para prevenir las inyecciones de sql.

lo usaría como esto:

$variable = sanitizeString($_POST['user_input']); 
$variable = sanitizeMySQL($_POST['user_input']); 

EDIT: Se puede olvidarse de strip_tags para la primera función, ya que no hace nada. ¿Sería suficiente usar estas dos funciones para evitar la mayoría de los ataques y estar bien para un sitio público?

+1

¿De qué libro es esto? – Gumbo

+0

Aprendiendo PHP MySQL y Javascript – jpjp

+2

@jpjp: ... y pensé que O'Reilly es solo para autores sofisticados. – Gumbo

Respuesta

5

Es cierto, pero este nivel de escape puede no ser apropiado en todos los casos. ¿Qué sucede si quiere almacenar HTML en una base de datos?

La mejor práctica dicta que, en lugar de escapar al recibir valores, debe evitarlos cuando los muestre. Esto le permite representar tanto el HTML de la base de datos como el no HTML de la base de datos, y de todos modos es realmente a donde corresponde este tipo de código. Otra ventaja de desinfectar el HTML saliente es que se puede descubrir un nuevo vector de ataque, en cuyo caso desinfectar el HTML entrante no hará nada para valores que ya están en la base de datos, mientras que la sanitización saliente se aplicará retroactivamente. nada especial

Además, cabe destacar que strip_tags en su primera función es probable que no tienen ningún efecto, si toda la < y se han convertido en >&lt; y &gt;.

+4

Otra ventaja de desinfectar HTML saliente es que se puede descubrir un nuevo vector de ataque, en cuyo caso desinfectar el HTML entrante no hará nada para los valores que ya están en la base de datos, mientras que la sanitización saliente se aplicará retroactivamente sin tener que hacer nada especial. –

+0

@Dave Sherohman: Traté de expresar ese concepto, pero lo explicaste mejor. Editaré en tu redacción. – Matchu

+0

¿Y qué hay de la vulnerabilidad de inyección sql? – rook

0

Bueno, si no quiere reinventar la rueda, puede usar HTMLPurifier. Se le permite decidir exactamente lo que quiere y lo que no quiere y previene los ataques XSS y tal

3

Usted está haciendo htmlentities (que se convierte en toda >&gt;) y luego llamar strip_tags que en este momento no va a lograr nada más, ya que hay son sin etiquetas.

+1

Sí, creo que debería deshacerme de strip_tags. Un poco inútil – jpjp

+1

O llámalo antes htmlentities – stagas

10

Para ser sincero, creo que el autor de estas funciones no tiene ni idea de qué son las inyecciones XSS y SQL o qué función cumplen exactamente.

sólo para nombrar dos singularidades:

Además: En general, las funciones que protegen contra XSS no son adecuadas para proteger contra las inyecciones de SQL y viceversa. Porque cada idioma y contexto tiene sus propios caracteres especiales que deben ser atendidos.

Mi consejo es saber por qué y cómo es posible la inyección de código y cómo protegerse de él. Aprenda los idiomas con los que está trabajando, especialmente los personajes especiales y cómo escapar de estos.


Editar He aquí algunos ejemplos (probablemente raro): Imagínese que usted permite que sus usuarios para introducir un valor que debe ser utilizado como un segmento de trazado en un URI que se utiliza en algún código JavaScript en un valor onclick atributo . Por lo que el contexto de lenguaje se parece a esto:

  • valor del atributo HTML cadena
    • JavaScript
      • segmento de ruta URI

Y para que sea más divertido : Está almacenando este valor de entrada en anuncios atabase.

Ahora, para almacenar este valor de entrada correctamente en su base de datos, solo necesita utilizar una codificación adecuada para el contexto en el que va a insertar ese valor en el lenguaje de la base de datos (es decir, SQL); el resto no importa (todavía). Dado que desea insertarlo en una declaración de cadena SQL, los caracteres especiales contextuales son los caracteres que le permiten cambiar ese contexto. En cuanto a las declaraciones de cadenas, estos caracteres son (especialmente) los caracteres ", ' y \ que deben escaparse. Pero como ya se dijo, las declaraciones preparadas hacen todo eso para usted, entonces úselas.

Ahora que tiene el valor en su base de datos, queremos imprimirlos correctamente. Aquí partimos de la más interior al contexto más externa y aplicar la codificación adecuada en cada contexto:

  • Para el URI segmento de trazado contexto tenemos que escapar (al menos) todos esos personajes que vamos a cambiar ese contexto ; en este caso / (deje el segmento de ruta actual), ? y # (ambos dejan el contexto de ruta de URI). Podemos usar rawurlencode para esto.
  • Para cadena de JavaScript contexto tenemos que ocuparnos de ", ' y \. Podemos usar json_encode para esto (si está disponible).
  • Para el valor del atributo HTML tenemos que cuidar de &, ", ' y <. Podemos usar htmlspecialchars para esto.

Ahora todo junto:

'… onclick="'.htmlspecialchars('window.open("http://example.com/'.json_encode(rawurlencode($row['user-input'])).'")').'" …' 

Ahora bien, si es $row['user-input']"bar/baz" la salida es:

… onclick="window.open(&quot;http://example.com/&quot;%22bar%2Fbaz%22&quot;&quot;)" … 

Pero el uso de todas estas funciones en estos contextos no es una exageración.Porque aunque los contextos pueden tener caracteres especiales similares, tienen diferentes secuencias de escape. URI tiene el llamado porcentaje de codificación, JavaScript tiene secuencias de escape como \" y HTML tiene referencias de caracteres como &quot;. Y no usar solo una de estas funciones permitirá romper el contexto.

+0

Creo que me voy a quedar con mysql_real_escape_string y preparé declaraciones para inyecciones sql y htmlentities para xss. – jpjp

+0

@jpjp: ¡Es una buena forma de hacerlo! Y si está utilizando una codificación de caracteres que puede codificar Unicode (como UTF-8), incluso puede usar 'htmlspecialchars' en lugar de' htmlentities'. – Gumbo

+1

¿Se requiere mysql_real_escape_string para las declaraciones preparadas? – stagas

2

Si está utilizando declaraciones preparadas y marcadores de posición SQL y nunca interpolando la entrada del usuario directamente en sus cadenas de SQL, puede omitir completamente la desinfección de SQL.

Cuando utiliza marcadores de posición, la estructura de la declaración SQL (SELECT foo, bar, baz FROM my_table WHERE id = ?) se envía al motor de base de datos por separado de los valores de datos que (eventualmente) están vinculados a los marcadores de posición. Esto significa que, salvo errores importantes en el motor de la base de datos, es absolutamente imposible para que los valores de los datos se malinterpreten como instrucciones SQL, por lo que brinda protección completa contra ataques de inyección SQL sin necesidad de modificar sus datos para su almacenamiento.

+0

Entonces, después de que mysql_real_escape_string la variable (digamos $ nombre), sería simplemente insertar en el db por declaraciones preparadas? – jpjp

+2

@jpjp: Si usa declaraciones preparadas, no necesita hacer nada con la cadena, no mysql_real_escape_string, sin desinfección de ningún tipo. Para más información, google PHP PDO. – TheMagician

2

No, esto no es excesivo, esto es una vulnerabilidad.

Este código es completamente vulnerable a la inyección de SQL. Estás haciendo mysql_real_escape_string() y luego estás haciendo stripslashes(). Por lo tanto, " se convertiría en \" después de mysql_real_escape_string() y luego volvería a " después de las stripslashes(). mysql_real_escape_string() solo es mejor detener la inyección sql. Las bibliotecas de consultas parametrizadas como PDO y ADODB lo utilizan, y las consultas parametrizadas hacen que sea muy fácil detener por completo la inyección de sql.

Vaya por delante probar el código:

$variable = sanitizeString($_POST['user_input']); 
$variable = sanitizeMySQL($_POST['user_input']); 
mysql_query("select * from mysql.user where Host='".$variable."'"); 

¿Qué pasa si:

$_POST['user_input'] = 1' or 1=1 /* 

parcheado:

mysql_query("select * from mysql.user where Host='".mysql_real_escape_string($variable)."'"); 

Este código también es vulnerable a algunos tipos XSS:

$variable = sanitizeString($_POST['user_input']); 
$variable = sanitizeMySQL($_POST['user_input']); 
print("<body background='http://localhost/image.php?".$variable."' >"); 

¿Qué pasa si:

$_POST['user_input']="' onload=alert(/xss/)"; 

parcheado:

$variable=htmlspecialchars($variable,ENT_QUOTES); 
print("<body background='http://localhost/image.php?".$variable."' >"); 

htmlspeicalchars se codifican comillas simples y dobles, asegúrese de que la variable que va a imprimir también está encerrado entre comillas, esto hace que sea imposible "salir" y ejecutar el código.

+0

la desinfección manual es la raíz de todo mal. –

+0

Pero el '" 'se convertiría en' " 'después de' htmlentities'. La razón por la cual su ejemplo funciona es simplemente porque usted usa '' 'en lugar de' "' en el valor del atributo HTML y la declaración de cadena MySQL. Y el '' 'solo se reemplaza por' htmlentities' utilizando el estilo de cotización * ENT \ _QUOTES *: '$ var = htmlentities ($ var, ENT_QUOTES);' también lo arreglaría. La declaración de MySQL daría como resultado 'select * from mysql.usuario donde Host = '1 ' o 1 = 1/*' 'y el ejemplo HTML en' '. – Gumbo

+0

No, mysql_real_escape_string() solo es * no * mejor para detener la inyección de SQL. La forma de obtener protección * absoluta * es mediante el uso de consultas parametrizadas, que envían los comandos y los datos al motor de la base de datos a través de canales separados, lo que hace que la inyección SQL * sea imposible *. No se escape de las consultas. No desinfecte las consultas. Use declaraciones preparadas y marcadores de posición. (Como, dicho sea de paso, el OP dijo que lo está haciendo) –

-1

Me pregunto sobre el concepto de sanitización. Le está diciendo a Mysql que haga exactamente lo que quiere que haga: ejecutar una declaración de consulta creada en parte por el usuario del sitio web. Ya estás construyendo la oración de forma dinámica utilizando la entrada del usuario, concatenando cadenas con datos proporcionados por el usuario. Obtienes lo que pides.

De todos modos, aquí hay algo más métodos de desinfección ...

1) Para valores numéricos, siempre echados manualmente al menos en algún lugar antes o mientras se construye la cadena de consulta: "SELECT campo1 DE DONDE tblTest (id =". (int) $ val.")";

2) Para las fechas, primero convierta la variable a la marca de tiempo de Unix. Luego, use la función Mysql FROM_UNIXTIME() para convertirla a una fecha. "SELECCIONAR field1 FROM tblTest WHERE (date_field> = FROM_UNIXTIME (". Strtotime ($ val). ")" ;. Esto en realidad es necesario a veces para tratar cómo Mysql interpreta y almacena fechas diferentes de las secuencias de comandos o del sistema operativo.

3) Para cadenas cortas y predecibles que deben seguir un cierto estándar (nombre de usuario, correo electrónico, número de teléfono, etc.), puede a) hacer declaraciones preparadas; o b) regex u otra validación de datos.

4) Para las cadenas que no seguirían ningún estándar real y que pueden o no tener código previo o doblemente escapado y ejecutable por todas partes (texto, notas, marcado wiki, enlaces, etc.), usted puede a) hacer declaraciones preparadas; o b) almacenar ay convertir de forma binaria/blob - convirtiendo cada carácter a representación binaria, hexadecimal o decimal antes de pasar el valor a la cadena de consulta y convertir al extraer. De esta forma, puede centrarse más en la validación html solo cuando escupir el valor almacenado de nuevo.

+1

-1: no concatenar ni interpolar los datos proporcionados por el usuario directamente en las cadenas de consulta. Período. Usar consultas parametrizadas (también conocidas como sentencias preparadas/marcadores de posición) e inyección SQL serán cosa del pasado. Tenga en cuenta que OP declaró "También para desinfectar, utilizo declaraciones preparadas para evitar inyecciones de sql". Como ya está utilizando consultas parametrizadas, no se necesita ninguna de estas técnicas de sanitización a medias, por lo tanto, no las presente como un medio apropiado de protección contra la inyección de SQL. –

+0

@Dave. Admito que me dejé llevar, pero estoy en desacuerdo contigo sobre la inutilidad de mis sugerencias. Las declaraciones preparadas son una forma de evitar la inyección de sql, sí. Pero no si se usa una interfaz db diferente, lo que podría dejar a todos los usuarios compartiendo los datos abiertos ya sea por agujeros explotables o sin darse cuenta, volviendo a comprometer el texto cargado con sql-injection que anteriormente se pensaba que estaba almacenado de forma segura. Además, no presta nada para garantizar la integridad de la base de datos, ni para anular los ataques xss, ni para eliminar html, lo que podría redibujar de manera efectiva todo el sitio. –

+0

Además, no garantiza la integridad de la base de datos Para aclarar, aparentemente está limitado a marcar una variable como "idsb". No sé si esto forzará el tipo. Todavía esto no es suficiente a veces para la validación e integridad de los datos. Algunos usuarios tienen problemas con blobs incluso cuando ajusta max_allowed_packet para compensar. Algunos usuarios encuentran mysqli con errores. Y, algunos hosts son estrictos con conexiones y consultas de DB, por lo que una simple consulta de una sola vez distribuida en dos llamadas a través de PS que utilizan PS es a veces un desperdicio. Entonces, PS todavía no es la solución definitiva. –

Cuestiones relacionadas