La inyección de SQL es un problema de seguridad muy serio, en gran parte porque es muy fácil equivocarse: la forma obvia e intuitiva de crear una consulta que incorpore la entrada del usuario lo deja vulnerable, y la forma correcta de mitigarlo requiere que conozca los parámetros consultas e inyección SQL primero.
Me parece que la forma obvia de solucionar esto sería cerrar la opción obvia (pero incorrecta): arreglar el motor de la base de datos para que cualquier consulta recibida que use valores codificados en su cláusula WHERE en lugar de parámetros devuelva una descripción agradable y descriptiva mensaje de error que le indica que use parámetros en su lugar. Obviamente, esto debería tener una opción de exclusión para que cosas como las consultas ad-hoc de las herramientas administrativas aún se ejecuten fácilmente, pero deberían habilitarse de manera predeterminada.
Tener esto cerraría la inyección de SQL en frío, casi de la noche a la mañana, pero que yo sepa, ningún RDBMS realmente hace esto. ¿Hay alguna buena razón por la que no?
bad_ideas_sql = 'SELECT title FROM idea WHERE idea.status == "bad" AND idea.user == :mwheeler'
tendría valores codificados y parametrizados en una sola consulta. ¡Intente captar eso! Creo que hay casos de uso válidos para tales consultas mixtas.SELECT * FROM jokes WHERE date > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY score DESC;
"bad"
es realmente literal o si es el resultado de la concatenación de cadenas. Las dos soluciones que veo son deshacerse de SQL y otros DSL incrustados en cadenas (sí, por favor), o promover lenguajes donde la concatenación de cadenas es más molesta que usar consultas parametrizadas (umm, no).Respuestas:
Hay demasiados casos en los que usar un literal es el enfoque correcto.
Desde el punto de vista del rendimiento, hay veces que desea literales en sus consultas. Imagine que tengo un rastreador de errores donde una vez que sea lo suficientemente grande como para preocuparse por el rendimiento, espero que el 70% de los errores en el sistema estén "cerrados", el 20% estarán "abiertos", el 5% estarán "activos" y 5 % estará en algún otro estado. Es razonable que desee tener la consulta que devuelve todos los errores activos
en lugar de pasar el
status
como una variable de enlace. Quiero un plan de consulta diferente según el valor que se haya pasadostatus
: me gustaría hacer un escaneo de tabla para devolver los errores cerrados y un escaneo de índice en elstatus
columna para devolver los préstamos activos. Ahora, diferentes bases de datos y diferentes versiones tienen diferentes enfoques para (más o menos exitosamente) permitir que la misma consulta use un plan de consulta diferente dependiendo del valor de la variable de enlace. Pero eso tiende a introducir una cantidad decente de complejidad que debe gestionarse para equilibrar la decisión de si molestarse en volver a analizar una consulta o si reutilizar un plan existente para un nuevo valor de variable de enlace. Para un desarrollador, puede tener sentido lidiar con esta complejidad. O puede tener sentido forzar una ruta diferente cuando tengo más información sobre cómo se verán mis datos que el optimizador.Desde el punto de vista de la complejidad del código, también hay muchas veces que tiene mucho sentido tener literales en las declaraciones SQL. Por ejemplo, si tiene una
zip_code
columna que tiene un código postal de 5 caracteres y a veces tiene 4 dígitos adicionales, tiene mucho sentido hacer algo comoen lugar de pasar 4 parámetros separados para los valores numéricos. Estas no son cosas que cambiarán alguna vez, por lo que convertirlas en variables de enlace solo sirve para hacer que el código sea potencialmente más difícil de leer y para crear el potencial de que alguien enlace parámetros en el orden incorrecto y termine con un error.
fuente
La inyección de SQL ocurre cuando una consulta se construye concatenando texto de una fuente no confiable y no validada con otras partes de una consulta. Si bien tal cosa ocurriría con mayor frecuencia con los literales de cadena, esa no sería la única forma en que podría ocurrir. Una consulta de valores numéricos puede tomar una cadena ingresada por el usuario (que se supone que solo contiene dígitos) y concatenarse con otro material para formar una consulta sin las comillas normalmente asociadas con literales de cadena; El código que confía demasiado en la validación del lado del cliente puede tener cosas como nombres de campo que provienen de una cadena de consulta HTML. No hay forma de que el código que mira una cadena de consulta SQL pueda ver cómo se ensambló.
Lo importante no es si una instrucción SQL contiene literales de cadena, sino más bien si una cadena contiene secuencias de caracteres de fuentes no confiables , y la validación para eso se manejaría mejor en la biblioteca que genera consultas. En general, no hay forma en C # de escribir código que permita un literal de cadena pero no permita otros tipos de expresión de cadena, pero uno podría tener una regla de prácticas de codificación que requiera que las consultas se construyan usando una clase de construcción de consultas en lugar de concatenación de cadenas, y cualquiera que pase una cadena no literal al generador de consultas debe justificar dicha acción.
fuente
Si desea poner los resultados de estos en el pie de página de su foro, necesitará agregar un parámetro ficticio solo para decir falso cada vez. O el ingenuo programador web busca cómo deshabilitar esa advertencia y luego continúa.
Ahora puede decir que agregaría una excepción para las enumeraciones, pero eso solo abre el agujero nuevamente (aunque más pequeño). Sin mencionar que las personas primero deben ser educadas para no usarlas
varchars
.El verdadero problema de la inyección es construir mediante programación la cadena de consulta. La solución para eso es un mecanismo de procedimiento almacenado y hacer cumplir su uso o una lista blanca de consultas permitidas.
fuente
deleted = false
conNOT deleted
, lo que evita el literal. Pero el punto es válido en general.TL; DR : Tendría que restringir todos los literales, no solo los de las
WHERE
cláusulas. Por razones que no lo hacen, permite que la base de datos permanezca desacoplada de otros sistemas.En primer lugar, su premisa es defectuosa. Desea restringir solo las
WHERE
cláusulas, pero ese no es el único lugar al que puede ir la entrada del usuario. Por ejemplo,Esto es igualmente vulnerable a la inyección SQL:
Por lo tanto, no puede limitar los literales en la
WHERE
cláusula. Tienes que restringir todos los literales.Ahora nos queda la pregunta: "¿Por qué permitir literales?" Tenga esto en cuenta: si bien las bases de datos relacionales se usan debajo de una aplicación escrita en otro idioma la mayor parte del tiempo, no es necesario que use el código de la aplicación para usar la base de datos. Y aquí tenemos una respuesta: necesitas literales para escribir código. La única otra alternativa sería requerir que todo el código se escriba en un idioma independiente de la base de datos. Así que tenerlos le da la capacidad de escribir "código" (SQL) directamente en la base de datos. Este es un desacoplamiento valioso, y sería imposible sin literales. (Intente escribir en su idioma favorito en algún momento sin literales. Estoy seguro de que puede imaginar lo difícil que sería).
Como ejemplo común, los literales a menudo se usan en la población de tablas de lista de valores / búsqueda:
Sin ellos, necesitaría escribir código en otro lenguaje de programación solo para completar esta tabla. La capacidad de hacerlo directamente en SQL es valiosa .
Luego nos queda una pregunta más: ¿por qué no lo hacen las bibliotecas cliente de lenguaje de programación? Y aquí tenemos una respuesta muy simple: habrían vuelto a implementar todo el analizador de la base de datos para cada versión compatible de la base de datos . ¿Por qué? Porque no hay otra manera de garantizar que hayas encontrado todos los literales. Las expresiones regulares no son suficientes. Por ejemplo: esto contiene 4 literales separados en PostgreSQL:
Intentar hacerlo sería una pesadilla de mantenimiento, especialmente porque la sintaxis válida a menudo cambia entre las principales versiones de las bases de datos.
fuente