¿Por qué el mecanismo de prevención de inyección SQL evolucionó en la dirección del uso de consultas parametrizadas?


59

A mi modo de ver, los ataques de inyección SQL se pueden prevenir mediante:

  1. Selección, filtrado y codificación de entrada cuidadosamente (antes de la inserción en SQL)
  2. 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 queriesy encoding librariesque 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?


1
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
maple_shaft

23
Las declaraciones preparadas no son el resultado de la evolución de los ataques de inyección SQL. Estuvieron allí desde el principio. Su pregunta se basa en una premisa falsa.
user207421

44
Si crees que eres más inteligente que los malos, entonces busca el # 1
paparazzo el

1
"¿Por qué PQ siguió siendo el método de elección"? Porque es el más fácil y robusto. Además de las ventajas de rendimiento mencionadas anteriormente para PQ's. Realmente no hay un inconveniente.
Paul Draper

1
Porque es la solución correcta al problema de cómo hacer consultas, incluso si no fuera por el tema de la inyección SQL en un contexto de seguridad . Los formularios que requieren escapar y usar datos dentro de banda con comandos siempre son un error de diseño porque son propensos a errores, son contra-intuitivos y se rompen mal cuando se usan incorrectamente. Ver también: scripting de shell.
R ..

Respuestas:


147

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.


6060
@dennis - Bueno, ¿qué es una cita en tu variante SQL? "? '?”? U + 2018? \ U2018? ¿Hay trucos para separar las expresiones? ¿Pueden sus subconsultas realizar actualizaciones? Hay muchas cosas que considerar.
Telastyn

77
@Dennis cada motor de base de datos tiene su propia forma de hacer cosas como escapar caracteres en cadenas. Hay muchos agujeros que tapar, especialmente si una aplicación necesita trabajar con múltiples motores de base de datos o ser compatible con futuras versiones del mismo motor que podrían cambiar alguna sintaxis de consulta menor que podría ser explotable.

12
Otro beneficio de las declaraciones preparadas es la ganancia de rendimiento que obtiene cuando tiene que volver a ejecutar la misma consulta, con valores diferentes. Además, las declaraciones preparadas pueden saber si un valor se entiende realmente como nulluna 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é!
Ismael Miguel

8
@Dennis El Sr. Henry Null le agradecerá por hacer esto de la manera correcta.
Mathieu Guindon

14
@Dennis el primer nombre es irrelevante. El problema es con el apellido. Vea Stack Overflow , Programmers.SE , Fox Sports , Wired , BBC y cualquier otra cosa que pueda encontrar en una búsqueda rápida en Google ;-)
Mathieu Guindon,

80

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.


2
Eso es todo (y esa fue la parte que faltaba en otras dos respuestas, entonces +1). Dada la forma en que se formula la pregunta, no se trata de desinfectar la entrada del usuario, sino, y cito la pregunta: "filtrar la entrada (antes de la inserción)". Si la pregunta ahora es sobre desinfectar la entrada, ¿por qué lo haría usted mismo en lugar de dejar que la biblioteca lo haga (mientras, por cierto, también pierde la oportunidad de tener planes de ejecución en caché)?
Arseni Mourzenko

8
@Dennis: desinfectar o filtrar significa eliminar información. Codificar significa transformar la representación de datos sin perder información.
JacquesB

99
@Dennis: el filtrado significa aceptar o rechazar la entrada del usuario. Por ejemplo, "Jeff" se filtraría como entrada del campo "Edad del usuario", porque el valor es obviamente inválido. Si, en lugar de filtrar la entrada, comienza a transformarla, por ejemplo, reemplazando el carácter de comillas simples, entonces está haciendo exactamente lo mismo que las bibliotecas de la base de datos donde usan consultas parametrizadas; en este caso, su pregunta es simplemente "¿Por qué usaría algo que existe y fue escrito por expertos en el campo, cuando puedo reinventar la rueda en cada proyecto?"
Arseni Mourzenko

3
@Dennis: O\'Malleyestá 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 adicional O''Malley. No es muy portátil si tiene que hacerlo usted mismo.
AbraCadaver

55
No puedo decirte cuántas veces mi nombre ha sido rechazado por un sistema. A veces incluso he visto errores causados ​​por la inyección de SQL solo por usar mi nombre. Diablos, una vez me pidieron que cambiara mi nombre de usuario porque en realidad estaba rompiendo algo en el backend.
Alexander O'Mara

60

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:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Si alguien sugiere hacer eso, respondería con razón que es bastante ridículo, y que uno debería hacer:

List<Integer> list = /* ... */;
list.add(5, position=2);

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:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

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.


¡Buena explicación!
Mike Partridge

2
Me perdiste en "buena analogía" pero voté en base a la explicación anterior. :)
Comodín el

1
Excelente ejemplo! - Y podría agregar: Dependiendo del tipo de datos, a veces ni siquiera es posible o factible crear una cadena analizable. - ¿Qué sucede si uno de mis parámetros es un campo de texto libre que contiene un borrador de historia (~ 10.000 caracteres)? o qué pasa si un parámetro es una imagen JPG ? - La única forma es una consulta parametrizada
Falco

En realidad no, es una descripción bastante mala de por qué las declaraciones preparadas evolucionaron como una defensa a la inyección SQL. Particularmente dado el ejemplo de código está en java, que no existía cuando las consultas parametrizadas se desarrollaban probablemente en el marco de tiempo donde C / C ++ se consideraba un estado de la técnica. Las bases de datos SQL comenzaron a usarse en los primeros años del período de 1970-1980. MUCHO antes de que los idiomas de nivel superior sean populares. Diablos, diría que muchos de ellos vinieron para hacer que trabajar con bases de datos sea más fácil (¿PowerBuilder, alguien?)
TomTom

@TomTom en realidad, estoy de acuerdo con la mayoría de tu contenido. Solo he tocado implícitamente el aspecto de seguridad aquí. En SO, respondo muchas preguntas SPARQL (el lenguaje de consulta RDF, con algunas similitudes con SQL) y mucha gente se topa con problemas porque concatenan cadenas en lugar de usar consultas parametrizadas. Incluso sin ataques de inyección, las consultas parametrizadas ayudan a evitar errores / bloqueos, y los errores / bloqueos también pueden ser problemas de seguridad, incluso si no son ataques de inyección. Entonces diría cada vez menos: las consultas parametrizadas son buenas, incluso si la inyección SQL no fue un problema, y ​​son buenas ...
Joshua Taylor

21

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.


10
La inyección de SQL ha sido un problema desde que SQL se inventó por primera vez. No se convirtió en un problema más tarde.
Servy

99
@Servy teóricamente sí. Prácticamente solo se convirtió en un problema real cuando nuestros mecanismos de entrada se pusieron en línea, presentando una superficie de ataque masivo para que cualquiera pueda martillar.
Jan Doggen

8
Little Bobby Tables no estaría de acuerdo en que necesita Internet ni una gran base de usuarios para aprovechar la inyección SQL. Y, por supuesto, las redes son anteriores a SQL, por lo que no es necesario esperar a las redes una vez que salió SQL. Sí, las vulnerabilidades de seguridad son menos vulnerables cuando la aplicación tiene una pequeña base de usuarios, pero siguen siendo las vulnerabilidades de seguridad, y la gente no les explotar cuando la propia base de datos tiene datos valiosos (y muchos muy primera base de datos tenía datos muy valiosos, ya que sólo las personas con valiosas bases de datos podría permitirse la tecnología) ..
Servy

55
@Servy, que yo sepa, el SQL dinámico fue una característica relativamente tardía; El uso inicial de SQL se precompiló / preprocesó principalmente con parámetros para valores (tanto dentro como fuera), por lo que los parámetros en las consultas podrían ser anteriores a la inyección de SQL en el software (tal vez no en consultas ad-hoc / CLI).
Mark Rotteveel

66
Podrían ser anteriores al conocimiento de la inyección SQL.
user253751

20

Simplemente dijo: no lo hicieron. Su declaración:

¿Por qué el mecanismo de prevención de inyección SQL evolucionó en la dirección del uso de consultas parametrizadas?

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.


Gracias. ¿Por qué era necesario evitar la concatenación de cadenas? Me parece que sería una característica útil. ¿Alguien tuvo un problema con eso?
Dennis

3
Dos en realidad. Primero, no siempre es totalmente trivial: ¿por qué lidiar con la asignación de memoria, etc., cuando no es necesario? Pero en segundo lugar, en la antigüedad, el almacenamiento en caché de la base de datos SQL no era exactamente tan bueno: la compilación de SQL era costosa. Como efecto secundario del uso de declaraciones preparadas de un sql (que es de donde provienen los parámetros), los planes de exención podrían reutilizarse. SQL Server introdujo la parametrización automática (para reutilizar los planes de consulta incluso sin parámetros, se deducen e implican). Creo que 2000 o 2007, en algún punto intermedio, IIRC.
TomTom

2
Tener consultas parametrizadas no elimina la capacidad de realizar concatenación de cadenas. Puede hacer una concatenación de cadenas para generar una consulta parametrizada. El hecho de que una función sea útil no significa que siempre sea una buena opción para un problema determinado.
JimmyJames

Sí, pero como dije, para cuando se inventaron, el SQL dinámico llegó con un rendimiento bastante decente;) Incluso hoy en día la gente te dice que los planes de consulta dinámica de SQL en el servidor SQL no se reutilizan (lo cual es incorrecto ya que - hm - as Dije algún punto entre 2000 y 2007, muy largo). En el pasado, realmente quería declaraciones PREPARADAS si ejecuta sql varias veces;)
TomTom

El almacenamiento en caché del plan para SQL dinámico se agregó de hecho a SQL Server 7.0, en 1998 - sqlmag.com/database-performance-tuning/…
Mike Dimmick el

13

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.


3
Separar el código y los datos también significa que sus defensas contra la inyección de código hostil son escritas y probadas por el proveedor de la base de datos. Por lo tanto, si algo que se pasa como parámetro junto con una consulta inofensiva termina destruyendo o subvirtiendo su base de datos, la reputación de la compañía de bases de datos está en juego, y su organización podría incluso demandarlos y ganar. También significa que si ese código contiene un error explotable, las probabilidades son bastante buenas de que es el sitio de otra persona donde se libera todo, en lugar del tuyo. (¡Simplemente no ignore las correcciones de errores de seguridad!)
nigel222

11

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'y select * 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.


Creo que esa es la teoría (basada en declaraciones preparadas usadas para consultas parametrizadas). En la práctica, dudo que este sea realmente el caso, ya que la mayoría de las implementaciones solo prepararán, vincularán y ejecutarán en una llamada, por lo tanto, use una declaración preparada diferente para cada consulta parametrizada, a menos que tome medidas explícitas para preparar declaraciones (y una biblioteca -nivel a preparemenudo es bastante diferente de un nivel SQL real prepare).
jcaron

Las siguientes consultas también son diferentes al analizador SQL: SELECT * FROM employees WHERE last_name IN (?, ?)y SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick

Sí, tienen. Es por eso que MS agregó el almacenamiento en caché del plan de consultas en 1998 a SQL Server 7. Como en: su información es una generación anterior.
TomTom

1
@TomTom: el almacenamiento en caché del plan de consulta no es lo mismo que la parametrización automática, en la que parece estar insinuando. Como en, lea antes de publicar.
mustaccio

@mustaccio En realidad, al menos MS introdujo ambos al mismo tiempo.
TomTom

5

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.


1
JDBC no desinfecta anithing. El protocolo tiene una parte específica para el parámetro y la base de datos simplemente no interpreta esos parámetros. Es por eso que puede establecer el nombre de la tabla desde el parámetro.
talex

1
¿Por qué? Si el parámetro no se analiza o interpreta, no hay razón para escapar de algo.
talex

11
Creo que tiene una imagen incorrecta de cómo funciona una consulta parametrizada. No se trata solo de que los parámetros se sustituyan más tarde, nunca se sustituyen . Un DBMS convierte cualquier consulta en un "plan", un conjunto de pasos que ejecutará para obtener su resultado; en una consulta parametrizada, ese plan es como una función: tiene una serie de variables que deben proporcionarse cuando se ejecuta. Para el momento en que se suministran las variables, la cadena SQL se ha olvidado por completo y el plan se ejecuta con los valores proporcionados.
IMSoP

2
@IMSoP Esa fue una idea errónea mía. Aunque creo que es común, como puede ver en las dos respuestas más votadas a esta pregunta en SO stackoverflow.com/questions/3271249/… . Lo leí y tienes razón. Edité la respuesta.
Tulains Córdova

3
@TomTom Eso es excelente para el rendimiento , pero no hace nada por la seguridad . Para cuando se compila y almacena en caché una pieza comprometida de SQL dinámico, el programa ya ha sido modificado . Crear un plan a partir de SQL parametrizado no dinámico y luego pasar elementos de datos sigue siendo fundamentalmente diferente de un DBMS que abstrae la similitud entre dos consultas que se le presentan como cadenas SQL completas.
IMSoP

1

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:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)

1
"Codificar es difícil de hacer bien" - jajaja. No lo es. Un día o dos, todo está documentado. Escribí un codificador hace muchos años para un ORM (porque el servidor sql tiene un límite en los parámetros y, por lo tanto, es problemático insertar 5000-10000 filas en una declaración (hace 15 años atrás). No recuerdo que sea un gran problema.
TomTom

1
Quizás SQL Server es lo suficientemente regular como para que no sea un problema, pero he encontrado problemas en otras bases de datos: casos de esquina con codificaciones de caracteres no coincidentes, opciones de configuración oscuras, fecha específica de la localidad y problemas de números. Todo solucionable, pero necesita al menos una comprensión superficial de las peculiaridades de la base de datos (te estoy mirando, MySQL y Oracle).
James_pic

3
@TomTom Encoding es realmente muy difícil de acertar una vez que tiene en cuenta el tiempo. ¿Qué hace cuando su proveedor de base de datos decide crear un nuevo estilo de comentario en la próxima versión o cuando una palabra clave se convierte en una nueva palabra clave en una actualización? Teóricamente, podría obtener la codificación correcta para una versión de su RDBMS y estar equivocado en la próxima revisión. Ni siquiera comiences con lo que sucede cuando cambias de proveedores a uno que tiene comentarios condicionales usando una sintaxis no estándar
Eric

@ Eric, eso es francamente horrible. (Uso Postgres; si tiene verrugas extrañas aún no las he encontrado).
Comodín el

0

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:

  1. Selección y filtrado cuidadosos [ infinito ] de [ todas las consultas SQL seguras ]
  2. Uso de [ escenarios preconsiderados limitados a su alcance ]

Según otras respuestas, también parece haber algunos beneficios de rendimiento al limitar su alcance lejos del infinito y hacia algo manejable.


0

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.


Todo esto es agradable y elegante, pero no aborda la pregunta según el título en absoluto.
TomTom

1
@TomTom: ¿Qué quieres decir? La pregunta es exactamente por qué las consultas parametrizadas son el mecanismo preferido para prevenir la inyección de SQL; mi respuesta explica por qué las consultas parametrizadas son más seguras y robustas que desinfectar la entrada del usuario.
Daniel Pryden

Lo siento, pero MI pregunta dice "¿Por qué el mecanismo de prevención de inyección SQL evolucionó en la dirección del uso de consultas parametrizadas?". Ellos no. No se trata del ahora, se trata de la historia.
TomTom

0

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


0

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.


-2

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.