A mi modo de ver, los ataques de inyección SQL se pueden prevenir mediante:
- Selección, filtrado y codificación de entrada cuidadosamente (antes de la inserción en SQL)
- Uso de declaraciones preparadas / consultas parametrizadas
Supongo que hay pros y contras para cada uno, pero ¿por qué el n. ° 2 despegó y se consideró que era más o menos la forma de facto para prevenir los ataques de inyección? ¿Es más seguro y menos propenso a errores o hubo otros factores?
Según tengo entendido, si el # 1 se usa correctamente y se atienden todas las advertencias, puede ser tan efectivo como el # 2.
Desinfectante, filtrado y codificación
Hubo cierta confusión de mi parte entre lo que significaba desinfección , filtrado y codificación . Diré que para mis propósitos, todo lo anterior se puede considerar para la opción 1. En este caso, entiendo que la desinfección y el filtrado tienen el potencial de modificar o descartar los datos de entrada, mientras que la codificación conserva los datos tal cual , pero los codifica adecuadamente para evitar ataques de inyección. Creo que el escape de datos puede considerarse como una forma de codificarlo.
Consultas parametrizadas vs Biblioteca de codificación
Hay respuestas donde los conceptos de parameterized queries
y encoding libraries
que se tratan indistintamente. Corrígeme si me equivoco, pero tengo la impresión de que son diferentes.
Tengo entendido que encoding libraries
, no importa cuán buenos sean, siempre tienen el potencial de modificar el "Programa" SQL, porque están haciendo cambios en el propio SQL, antes de enviarlo al RDBMS.
Parameterized queries
por otro lado, envíe el programa SQL al RDBMS, que luego optimiza la consulta, define el plan de ejecución de la consulta, selecciona los índices que se utilizarán, etc., y luego conecta los datos, como el último paso dentro del RDBMS sí mismo.
Biblioteca de codificación
data -> (encoding library)
|
v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement
Consulta parametrizada
data
|
v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement
Significado historal
Algunas respuestas mencionan que históricamente, las consultas parametrizadas (PQ) se crearon por razones de rendimiento, y antes de los ataques de inyección que se volvieron populares en los problemas de codificación. En algún momento se hizo evidente que la PQ también era bastante efectiva contra los ataques de inyección. Para mantener el espíritu de mi pregunta, ¿por qué PQ siguió siendo el método de elección y por qué floreció por encima de la mayoría de los otros métodos cuando se trata de prevenir ataques de inyección SQL?
Respuestas:
El problema es que el n. ° 1 requiere que analice e interprete de manera efectiva la totalidad de la variante SQL con la que está trabajando para que sepa si está haciendo algo que no debería. Y mantenga ese código actualizado mientras actualiza su base de datos. En todas partes acepta entradas para sus consultas. Y no arruinarlo.
Entonces, sí, ese tipo de cosas detendría los ataques de inyección SQL, pero es absurdamente más costoso de implementar.
fuente
null
una cadena o un número y actuar en consecuencia. Esto es muy bueno para la seguridad. E incluso si ejecuta la consulta una vez, el motor DB ya la tendrá optimizada para usted. Mejor aún si está en caché!Porque la opción 1 no es una solución. La detección y el filtrado significan rechazar o eliminar entradas no válidas. Pero cualquier entrada puede ser válida. Por ejemplo, el apóstrofe es un carácter válido en el nombre "O'Malley". Solo tiene que estar codificado correctamente antes de usarse en SQL, que es lo que hacen las declaraciones preparadas.
Después de agregar la nota, parece que básicamente se pregunta por qué usar una función de biblioteca estándar en lugar de escribir su propio código funcionalmente similar desde cero. Usted debe siempre prefieren soluciones biblioteca estándar para escribir su propio código. Es menos trabajo y más fácil de mantener. Este es el caso de cualquier funcionalidad, pero especialmente para algo que es sensible a la seguridad, no tiene ningún sentido reinventar la rueda por su cuenta.
fuente
O\'Malley
está usando la barra oblicua para escapar de la cita para una inserción adecuada (al menos en algunas bases de datos). En MS SQL o Access, se puede escapar con una cotización adicionalO''Malley
. No es muy portátil si tiene que hacerlo usted mismo.Si está tratando de hacer un procesamiento de cadenas, entonces realmente no está generando una consulta SQL. Estás generando una cadena que puede generar una consulta SQL. Hay un nivel de indirección que abre mucho espacio para errores y errores. Es algo realmente sorprendente, dado que en la mayoría de los contextos estamos felices de interactuar con algo mediante programación. Por ejemplo, si tenemos alguna estructura de lista y queremos agregar un elemento, generalmente no hacemos:
Si alguien sugiere hacer eso, respondería con razón que es bastante ridículo, y que uno debería hacer:
Eso interactúa con la estructura de datos en su nivel conceptual. No introduce ninguna dependencia de cómo se podría imprimir o analizar esa estructura. Esas son decisiones completamente ortogonales.
Su primer enfoque es como la primera muestra (solo un poco peor): está asumiendo que puede construir programáticamente la cadena que se analizará correctamente como la consulta que desea. Eso depende del analizador y de un montón de lógica de procesamiento de cadenas.
El segundo enfoque de usar consultas preparadas es mucho más parecido a la segunda muestra. Cuando usa una consulta preparada, básicamente analiza una pseudoconsulta que es legal pero tiene algunos marcadores de posición, y luego usa una API para sustituir correctamente algunos valores allí. Ya no involucra el proceso de análisis y no tiene que preocuparse por el procesamiento de cadenas.
En general, es mucho más fácil, y mucho menos propenso a errores, interactuar con las cosas en su nivel conceptual. Una consulta no es una cadena, una consulta es lo que obtienes cuando analizas una cadena o construyes una mediante programación (o cualquier otro método que te permita crear una).
Aquí hay una buena analogía entre las macros de estilo C que reemplazan el texto de manera simple y las macros de estilo Lisp que generan código arbitrario. Con las macros de estilo C, puede reemplazar el texto en el código fuente, y eso significa que tiene la capacidad de introducir errores sintácticos o comportamientos engañosos. Con las macros Lisp, está generando código en la forma en que el compilador lo procesa (es decir, está devolviendo las estructuras de datos reales que procesa el compilador, no el texto que el lector tiene que procesar antes de que el compilador pueda acceder a él) . Sin embargo, con una macro Lisp, no puede generar algo que sería un error de análisis. Por ejemplo, no puede generar (let ((ab) a .
Sin embargo, incluso con las macros de Lisp, aún puede generar código incorrecto, porque no necesariamente tiene que estar consciente de la estructura que se supone que debe estar allí. Por ejemplo, en Lisp, (let ((ab)) a) significa "establecer una nueva unión léxica de la variable a al valor de la variable b, y luego devolver el valor de a", y (let (ab) a) significa "establecer nuevos enlaces léxicos de las variables a y b e inicializarlos a ambos a cero, y luego devolver el valor de a". Ambos son sintácticamente correctos, pero significan cosas diferentes. Para evitar este problema, puede usar funciones más conscientes semánticamente y hacer algo como:
Con algo así, es imposible devolver algo que es sintácticamente inválido, y es mucho más difícil devolver algo que accidentalmente no es lo que quería.
fuente
Ayuda a que la opción # 2 generalmente se considere una mejor práctica porque la base de datos puede almacenar en caché la versión no parametrizada de la consulta. Las consultas parametrizadas son anteriores a la cuestión de la inyección de SQL por varios años (creo), resulta que puedes matar dos pájaros de un tiro.
fuente
Simplemente dijo: no lo hicieron. Su declaración:
es fundamentalmente defectuoso Las consultas parametrizadas han existido mucho más tiempo de lo que la inyección SQL es al menos ampliamente conocida. En general, se desarrollaron como una forma de evitar la ocultación de cadenas en la funcionalidad habitual de "formulario de búsqueda" que tienen las aplicaciones LOB (Line of Business). Muchos, MUCHOS años después, alguien encontró un problema de seguridad con dicha manipulación de cadenas.
Recuerdo haber hecho SQL hace 25 años (cuando Internet NO se usaba ampliamente, solo estaba comenzando) y recuerdo haber hecho SQL vs. IBM DB5 IIRC versión 5, y eso ya tenía consultas parametrizadas.
fuente
Además de todas las otras buenas respuestas:
La razón por la cual # 2 es mejor es porque separa sus datos de su código. En el n. ° 1, sus datos son parte de su código y de ahí provienen todas las cosas malas. Con el n. ° 1 obtiene su consulta y necesita realizar pasos adicionales para asegurarse de que su consulta comprende sus datos como datos, mientras que en el n. ° 2 obtiene su código y su código y sus datos son datos.
fuente
Las consultas parametrizadas, además de proporcionar defensa de inyección SQL, a menudo tienen un beneficio adicional de ser compiladas solo una vez, luego ejecutadas múltiples veces con diferentes parámetros.
Desde el punto de vista de base de datos SQL
select * from employees where last_name = 'Smith'
yselect * from employees where last_name = 'Fisher'
son claramente diferentes y por lo tanto requieren de análisis por separado, compilación y optimización. También ocuparán ranuras separadas en el área de memoria dedicada al almacenamiento de declaraciones compiladas. En un sistema muy cargado con una gran cantidad de consultas similares que tienen diferentes parámetros, el cálculo y la sobrecarga de memoria pueden ser sustanciales.Posteriormente, el uso de consultas parametrizadas a menudo proporciona importantes ventajas de rendimiento.
fuente
prepare
menudo es bastante diferente de un nivel SQL realprepare
).SELECT * FROM employees WHERE last_name IN (?, ?)
ySELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?)
.Espera pero porque?
La opción 1 significa que debe escribir rutinas de desinfección para cada tipo de entrada, mientras que la opción 2 es menos propensa a errores y tiene menos código para escribir / probar / mantener.
Es casi seguro que "atender todas las advertencias" puede ser más complejo de lo que crees, y tu lenguaje (por ejemplo, Java PreparedStatement) tiene más de lo que crees.
Las declaraciones preparadas o las consultas parametrizadas se compilan previamente en el servidor de la base de datos, por lo que, cuando se establecen los parámetros, no se realiza la concatenación de SQL porque la consulta ya no es una cadena SQL. Una ventaja adicional es que el RDBMS almacena en caché la consulta y las llamadas posteriores se consideran el mismo SQL incluso cuando los valores de los parámetros varían, mientras que con el SQL concatenado cada vez que la consulta se ejecuta con diferentes valores, la consulta es diferente y el RDBMS tiene que analizarla. , cree el plan de ejecución nuevamente, etc.
fuente
Imaginemos cómo sería un enfoque ideal de "desinfectar, filtrar y codificar".
La desinfección y el filtrado pueden tener sentido en el contexto de una aplicación en particular, pero en última instancia, ambos se reducen a decir "no se pueden poner estos datos en la base de datos". Para su aplicación, puede ser una buena idea, pero no es algo que pueda recomendar como solución general, ya que habrá aplicaciones que deberán poder almacenar caracteres arbitrarios en la base de datos.
Entonces eso deja la codificación. Puede comenzar por tener una función que codifique cadenas agregando caracteres de escape, de modo que pueda sustituirlos en usted mismo. Desde diferentes bases de datos necesitan diferentes caracteres escape (en algunas bases de datos, tanto
\'
y''
son secuencias de escape válidas para'
, pero no en otros), esta función debe ser proporcionada por el proveedor de base de datos.Pero no todas las variables son cadenas. Algunas veces necesitas sustituir un entero o una fecha. Estos están representados de manera diferente a las cadenas, por lo que necesita diferentes métodos de codificación (de nuevo, estos deberían ser específicos para el proveedor de la base de datos) y debe sustituirlos en la consulta de diferentes maneras.
Entonces, tal vez las cosas serían más fáciles si la base de datos manejara la sustitución también para usted: ya sabe qué tipos espera la consulta, y cómo codificar datos de manera segura y cómo sustituirlos en su consulta de manera segura, por lo que no necesita preocuparse por en tu código
En este punto, acabamos de reinventar las consultas parametrizadas.
Y una vez que las consultas se parametrizan, abre nuevas oportunidades, como optimizaciones de rendimiento y monitoreo simplificado.
La codificación es difícil de hacer bien, y la codificación bien hecha es indistinguible de la parametrización.
Si realmente te gusta la interpolación de cadenas como una forma de construir consultas, hay un par de idiomas (Scala y ES2015 vienen a la mente) que tienen la interpolación de cadenas enchufable, por lo que no son bibliotecas que le permiten escribir consultas parametrizados que se parecen a la interpolación de cadenas, pero están a salvo de la inyección de SQL, así que en la sintaxis ES2015:
fuente
En la opción 1, está trabajando con un conjunto de entrada de tamaño = infinito que está intentando asignar a un tamaño de salida muy grande. En la opción 2, ha limitado su entrada a lo que elija. En otras palabras:
Según otras respuestas, también parece haber algunos beneficios de rendimiento al limitar su alcance lejos del infinito y hacia algo manejable.
fuente
Un modelo mental útil de SQL (especialmente los dialectos modernos) es que cada instrucción o consulta SQL es un programa. En un programa ejecutable binario nativo, los tipos más peligrosos de vulnerabilidades de seguridad son desbordamientos en los que un atacante puede sobrescribir o modificar el código del programa con diferentes instrucciones.
Una vulnerabilidad de inyección SQL es isomórfica a un desbordamiento del búfer en un lenguaje como C. La historia ha demostrado que los desbordamientos del búfer son extremadamente difíciles de prevenir, incluso el código extremadamente crítico sujeto a revisión abierta a menudo ha contenido tales vulnerabilidades.
Un aspecto importante del enfoque moderno para resolver vulnerabilidades de desbordamiento es el uso de mecanismos de hardware y sistema operativo para marcar partes particulares de la memoria como no ejecutables y para marcar otras partes de la memoria como de solo lectura. (Consulte el artículo de Wikipedia sobre Protección de espacio ejecutable , por ejemplo). De esa manera, incluso si un atacante pudiera modificar datos, el atacante no puede hacer que sus datos inyectados sean tratados como código.
Entonces, si una vulnerabilidad de inyección SQL es equivalente a un desbordamiento del búfer, ¿cuál es el equivalente SQL a un bit NX o a páginas de memoria de solo lectura? La respuesta es: declaraciones preparadas , que incluyen consultas parametrizadas más mecanismos similares para solicitudes sin consulta. La declaración preparada se compila con ciertas partes marcadas como de solo lectura, por lo que un atacante no puede cambiar esas partes del programa y otras partes marcadas como datos no ejecutables (los parámetros de la declaración preparada), en los que el atacante podría inyectar datos pero que nunca será tratado como código de programa, eliminando así la mayor parte del potencial de abuso.
Ciertamente, desinfectar la entrada del usuario es bueno, pero para estar realmente seguro necesita ser paranoico (o, equivalentemente, pensar como un atacante). Una superficie de control fuera del texto del programa es la forma de hacerlo, y las declaraciones preparadas proporcionan esa superficie de control para SQL. Por lo tanto, no debería sorprendernos que las declaraciones preparadas y, por lo tanto, las consultas parametrizadas, sean el enfoque que recomiendan la gran mayoría de los profesionales de seguridad.
fuente
Ya escribí sobre esto aquí: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576
Pero, para que sea simple:
La forma en que funcionan las consultas parametrizadas es que sqlQuery se envía como una consulta, y la base de datos sabe exactamente qué hará esta consulta, y solo entonces insertará el nombre de usuario y las contraseñas simplemente como valores. Esto significa que no pueden efectuar la consulta, porque la base de datos ya sabe lo que hará la consulta. Entonces, en este caso, buscaría un nombre de usuario de "Nobody OR 1 = 1 '-" y una contraseña en blanco, que debería aparecer como falsa.
Sin embargo, esta no es una solución completa, y aún será necesario validar la entrada, ya que esto no afectará otros problemas, como los ataques XSS, ya que aún podría poner JavaScript en la base de datos. Luego, si esto se lee en una página, lo mostrará como javascript normal, dependiendo de cualquier validación de salida. Entonces, lo mejor es seguir usando la validación de entrada, pero usando consultas parametrizadas o procedimientos almacenados para detener cualquier ataque SQL
fuente
Nunca he usado SQL. Pero obviamente escuchas sobre los problemas que tiene la gente, y los desarrolladores de SQL tuvieron problemas con esta cosa de "inyección SQL". Durante mucho tiempo no pude entenderlo. Y luego me di cuenta de que las personas creaban sentencias SQL, sentencias de fuente SQL textuales reales, concatenando cadenas, algunas de las cuales fueron ingresadas por un usuario. Y mi primer pensamiento al darme cuenta fue un shock. Choque total. Pensé: ¿cómo puede alguien ser tan ridículamente estúpido y crear declaraciones en un lenguaje de programación como ese? Para un desarrollador de C, C ++, Java o Swift, esto es una locura total.
Dicho esto, no es muy difícil escribir una función C que tome una cadena C como argumento y produzca una cadena diferente que se vea exactamente como un literal de cadena en el código fuente de C que representa la misma cadena. Por ejemplo, esa función traduciría abc a "abc" y "abc" a "\" abc \ "" y "\" abc \ "" a "\" \\ "abc \\" \ "". (Bueno, si esto te parece mal, eso es html. Fue correcto cuando lo escribí, pero no cuando se muestra) Y una vez que se escribe esa función C, no es difícil generar código fuente C donde El texto de un campo de entrada proporcionado por el usuario se convierte en un literal de cadena C. Eso no es difícil de hacer seguro. No sé por qué los desarrolladores de SQL no usarían ese enfoque como una forma de evitar las inyecciones de SQL.
"Desinfectar" es un enfoque totalmente erróneo. La falla fatal es que hace que ciertas entradas del usuario sean ilegales. Terminas con una base de datos donde un campo de texto genérico no puede contener texto como; Drop Table o lo que sea que usaría en una inyección SQL para causar daños. Me parece bastante inaceptable. Si una base de datos almacena texto, debería poder almacenar cualquier texto. Y la falla práctica es que el desinfectante parece no poder hacerlo bien :-(
Por supuesto, las consultas parametrizadas son lo que esperaría cualquier programador que use un lenguaje compilado. Hace la vida mucho más fácil: tiene alguna entrada de cadena y nunca se molesta en traducirla a una cadena SQL, sino que simplemente la pasa como parámetro, sin posibilidad de que ningún carácter en esa cadena cause ningún daño.
Entonces, desde el punto de vista de un desarrollador que usa lenguajes compilados, desinfectar es algo que nunca se me ocurriría. La necesidad de desinfectar es una locura. Las consultas parametrizadas son la solución obvia al problema.
(La respuesta de Josip me pareció interesante. Básicamente dice que con consultas parametrizadas puedes detener cualquier ataque contra SQL, pero luego puedes tener texto en tu base de datos que se usa para crear una inyección de JavaScript :-( Bueno, tenemos el mismo problema nuevamente , y no sé si Javascript tiene una solución para eso.
fuente
El principal problema es que los piratas informáticos encontraron formas de rodear el saneamiento, mientras que las consultas parametrizadas eran un procedimiento existente que funcionaba perfectamente con los beneficios adicionales del rendimiento y la memoria.
Algunas personas simplifican el problema ya que "es solo una comilla simple y una comilla doble", pero los hackers encontraron formas inteligentes de evitar la detección, como usar diferentes codificaciones o hacer uso de las funciones de la base de datos.
De todos modos, solo tenía que olvidar una sola cadena para crear una violación de datos catastrófica. Los piratas informáticos pudieron automatizar scripts para descargar la base de datos completa con una serie o consultas. Si el software es bien conocido como un paquete de código abierto o un paquete de negocios famoso, simplemente puede adjuntar la tabla de usuarios y contraseñas.
Por otro lado, solo usar consultas concatenadas era solo una cuestión de aprender a usar y acostumbrarse a ellas.
fuente