Prevención de inyección SQL más rápida

10

Miré por todas partes y no puedo encontrar la respuesta a mi pregunta.

Estoy usando el último PHP para scripting del lado del servidor y MySQL para mi base de datos. El conjunto de caracteres es utf8mb4 si eso marca una diferencia.

Hasta ahora, he usado declaraciones preparadas para protegerme de la inyección de SQL. Sin embargo, dado que la mayoría de mis consultas solo se ejecutan una vez, el código es realmente lento . Cuando uso $conn->query() en su lugar, y $conn->real_escape_string() para todas mis variables, es más rápido.

Sin embargo, $conn->real_escape_string() también es lento porque hay un viaje de ida y vuelta en cada llamada. Pero no entiendo por qué es necesario un viaje de ida y vuelta porque creo que esa función de escape solo puede programarse en PHP.

Desde los documentos, una función se vería así:

  

Los caracteres codificados son NUL (ASCII 0), \ n, \ r, \, ', "y Control-Z.

function sanitize($str)
{
    str_replace(array('\', "
$sql = "UPDATE ipsum SET lorem=$str WHERE id=1337";
", "\n", "\r", "'", '"', "\x1a"), array('\\', '\0', '\n', '\r', "\'", '\"', '\Z'), $str); }

Suponiendo que me asegure de que esté en la codificación correcta y que no esté vacía.

Esto se publicó como un comentario en uno de los documentos. Por cierto, str_replace funciona con Unicode.

Sin embargo, pensé en algo que tiene un poco más de sentido. Mirando esta página , puedo estirar esto un lote más lejos. Para una declaración como esta:

function sanitize($str)
{
    return "'" .str_replace(array("\", "'"), array("\\", "\'"), $str) . "'";
}

No puedo hacerlo (y estoy comprobando de antemano para asegurarme de que es un UTF-8):

function sanitize($str)
{
    str_replace(array('\', "
$sql = "UPDATE ipsum SET lorem=$str WHERE id=1337";
", "\n", "\r", "'", '"', "\x1a"), array('\\', '\0', '\n', '\r', "\'", '\"', '\Z'), $str); }

¿Y eso evitará las inyecciones SQL? Por qué no? No puedo pensar en una entrada para la cual fallará.

Si no, ¿existe una mejor función de PHP que pueda proteger mejor contra la inyección de SQL?

Gracias de antemano! Puede probar SQL aquí .

ACTUALIZACIÓN: recibió las respuestas que esperaba. Para aclarar, no te estoy pidiendo que pronuncies tus predicaciones clásicas de "las declaraciones preparadas son lo mejor que hay que hacer o morir". Si quisiera eso, lo pediría en Yahoo! Respuestas y no aquí.

Me gustaría saber por qué mi método (el segundo) no es seguro (o si lo es). También sería bueno incluir una discusión de lo que real_escape_string o declaraciones preparadas realmente hacen para sanear los datos. Tanto MySQL como PHP son de código abierto, y estoy seguro de que alguien sabe qué sucede cuando se llaman estas funciones.

Simplemente no entiendo por qué se necesita un viaje de ida y vuelta para desinfectar las cadenas. ¿Cómo puede un método así no ser implementado en PHP?

ACTUALIZACIÓN 2: He iterado a través de todos los caracteres Unicode (0x0000 a 0x1F77F, encontré esto en Wikipedia) y noté que, si real_escape_string() solo escapa caracteres individuales y no frases (lo que de acuerdo con los documentos que hace), debajo de utf8mb4 charset, los caracteres que cambian son:

  

unicode 0 = > \ 0

     

unicode 10 = > \ n

     

unicode 13 = > \ r

     

unicode 26 = > \ Z

     

unicode 34 = > \ "

     

unicode 39 = > \ '

     

unicode 92 = > \\

Entonces, a pesar de que es utf8mb4 , no es diferente de lo que dicen los documentos (adivina porque UTF-8 es estándar). Entonces, ¿por qué esto no es implementable en PHP?

Aquí hay un secuencia de comandos PHP que combina todos estos Unicodes y los desinfecta. Aquí hay un violín de SQL .

    
pregunta Shahar 31.08.2014 - 21:42
fuente

6 respuestas

13

Con respecto a su función de desinfección, aunque no puedo producir un exploit para el ejemplo exacto que proporcionó, si cambiamos su ejemplo a algo como esto:

$sql = "UPDATE ipsum SET price=$str WHERE id=1337";

Si se pasa un valor de 10, otherColumn=1234 o quizás 10;-- para $str , podría ver problemas. Si implementa su propia función de saneamiento y comienza a usarla en su código, parece que solo será cuestión de tiempo antes de que aparezca una vulnerabilidad como esta. Seguiré jugando para ver si puedo encontrar un exploit para el ejemplo exacto que proporcionó en su pregunta.

Dicho esto, no recomendaría rodar su propia función de desinfección. Las personas más inteligentes que usted y yo hemos puesto un gran esfuerzo en probar la efectividad del estándar de la industria actualmente aceptado de usar declaraciones preparadas para evitar la inyección SQL, de modo que pueda (de manera bastante segura) asegurarse de que sea seguro. Rodar el tuyo es casi siempre un error.

También: apostaría a que hay personas que utilizan declaraciones preparadas en entornos que manejan más tráfico y requieren más rendimiento que su proyecto actual.

Si está experimentando problemas de rendimiento al acceder a su capa de datos, le recomendaría que dedique algo de tiempo a la optimización del rendimiento en lugar de crear su propia función de desinfección.

El acceso de ajuste de rendimiento a su capa de datos es una parte esencial y normal del desarrollo. Rodar su propio saneamiento no lo es.

    
respondido por el Abe Miessler 01.09.2014 - 04:51
fuente
6

En primer lugar, PHP ya tiene su segunda función y se llama addlashes () .

Los documentos dicen explícitamente:

  

Para escapar de los parámetros de la base de datos, la función de escape específica de DBMS (por ejemplo,   mysqli_real_escape_string () para MySQL o pg_escape_literal (),   pg_escape_string () para PostgreSQL) debe usarse para seguridad   razones.

     

Si su DBMS no tiene una función de escape y el DBMS usa \ para   para escapar de caracteres especiales, es posible que pueda usar esta función solo cuando   Este método de escape es adecuado para su base de datos. Tenga en cuenta que el uso de addlashes () para el escape de parámetros de la base de datos puede ser causa de problemas de seguridad en la mayoría de las bases de datos.

También falta tu primera función varios caracteres que son importantes para la sintaxis de MySQL.

Otras razones importantes por las que no deberías recurrir a esto:

  • Digamos que puede salirse con un saneamiento más eficiente pero menos completo para consultas específicas. ¿Desea tener que probar y considerar completamente todos los posibles vectores de ataque contra cada consulta que escriba? ¿Cuánto tiempo pasará hasta que cometa un error?

  • Una buena razón para usar declaraciones preparadas sobre mysql_real_escape_string es que algún día, inevitablemente, olvidará escapar de algo. Muchas inyecciones de SQL en proyectos de PHP bien conocidos han sido causados por alguien que simplemente olvidó escapar de algo, le sucede a todos. Al menos con declaraciones preparadas, si olvida llamar a bindParam () su consulta no funciona.

Si desea mejorar el rendimiento, implemente el almacenamiento en caché y evite las consultas de MySQL por completo.

Además, asegúrate de estar usando una versión actualizada de MySQL:

  

Antes de 5.1.17, el caché de consulta no se usa para las declaraciones preparadas.    enlace

No hagas rodar la tuya cuando se trata de seguridad.

    
respondido por el thexacre 01.09.2014 - 01:00
fuente
4

Tu código falla:

  • si el conjunto de caracteres de conexión predeterminado en el servidor MySQL no es un superconjunto ASCII estricto, por ejemplo, si es Shift-JIS (que puede contrabandear el byte para la barra invertida en el segundo byte de una secuencia multibyte);

  • si el servidor MySQL está configurado para usar la opción no_backslash_escape sql_mode (como puede ser para la interoperabilidad porque los escapes de barra invertida no son estándar ANSI).

En ambos casos, hay entradas que pueden escapar de un literal de cadena con posibles consecuencias de seguridad / desastre.

Por lo tanto, podría estar bien para tu configuración actual, pero es bastante frágil para cualquier otra persona que se ejecute en cualquier otro lugar.

    
respondido por el bobince 01.09.2014 - 14:46
fuente
4

¿Puede quitar los frenos de su automóvil para ahorrar peso? Bueno, si solo manejas en un sitio aislado y mantén la velocidad a un mínimo y asegúrate de que nunca suceda nada extraordinario (como una persona no informada que usa el auto), probablemente .

Su método "funciona" siempre que controle cada aspecto del sistema y no pase por alto nada. Ya no cumpliste con el segundo requisito (no sabías la configuración de NO_BACKSLASH_ESCAPE ), así que, ¿qué te hace pensar que todo está bien ahora? ¿Seguro que no has pasado por alto otra configuración? ¿Puede prometer que cualquiera que trabaje en el sistema ahora y en el futuro conoce las extrañas restricciones y nunca cometerá un error?

Personalmente, no apostaría mi dinero en ello. Cuando escribimos aplicaciones para el mundo real, queremos que funcionen en todos , incluso si no controlamos todos los ajustes, incluso si alguien comete un error, incluso si pasamos por alto algunos detalles. En otras palabras, la solución debería ser robusta , porque muchas décadas de desarrollo de software han demostrado que los humanos son, de hecho, falibles. Las declaraciones preparadas son muy robustas, mysql_real_escape_string() también es bastante robusto. Tu función no es. En absoluto.

También me pregunto cómo ha llegado a la conclusión de que tanto las declaraciones preparadas como el mysql_real_escape_string() son "demasiado lentas". Si tiene tantos usuarios que necesita preocuparse por algunos viajes de ida y vuelta adicionales, lo último que desea es una herramienta de seguridad hecha en casa. Y si no tienes tantos usuarios, creo que simplemente estás exagerando.

    
respondido por el Fleche 01.09.2014 - 18:14
fuente
2

El escape en general tiene tuvo problemas .

Utilice declaraciones preparadas , en lugar de escapar de cualquier cosa. Es la única forma sensata de pasar parámetros a las consultas SQL. Estoy bastante seguro de que también es el más rápido, ya que solo pasa los valores al servidor SQL.

    
respondido por el kpcyrd 01.09.2014 - 17:20
fuente
-1

Usar un desinfectante propio no es una buena opción cuando existe una función predeterminada para eso. En segundo lugar, para evitar la inyección de SQL, puede utilizar un firewall o filtro a nivel de aplicación. por ejemplo, puede cargar mod_security en el servidor Apache para prevenir ataques sqli.

    
respondido por el user2116018 01.09.2014 - 14:49
fuente

Lea otras preguntas en las etiquetas