prevenir - sql injection ejemplos reales
¿Puede la instrucción parametrizada detener todas las inyecciones de SQL? (11)
¿Puede la instrucción parametrizada detener todas las inyecciones de SQL?
Sí, siempre que su controlador de base de datos ofrezca un marcador de posición para cada posible literal de SQL. La mayoría de los controladores de declaraciones preparadas no. Supongamos que nunca encontrará un marcador de posición para un nombre de campo o una matriz de valores. Lo que hará que un desarrollador recurra a la personalización de una consulta, utilizando la concatenación y el formateo manual. Con el resultado previsto.
Es por eso que hice mi contenedor Mysql para PHP que admite la mayoría de los literales que se pueden agregar dinámicamente a la consulta, incluidas las matrices y los identificadores.
En caso afirmativo, ¿por qué hay tantas inyecciones SQL exitosas? ¿Solo porque algunos desarrolladores son demasiado tontos para usar declaraciones parametrizadas?
Como puede ver, en realidad es simplemente imposible tener todas sus consultas parametrizadas, incluso si no es tonto.
En caso afirmativo, ¿por qué hay tantas inyecciones SQL exitosas? ¿Solo porque algunos desarrolladores son demasiado tontos para usar declaraciones parametrizadas?
Bien, buena pregunta. La respuesta es más estocástica que determinista y trataré de explicar mi punto de vista, usando un pequeño ejemplo.
Hay muchas referencias en la red que sugieren que usemos parámetros en nuestras consultas o que usemos procedimientos almacenados con parámetros para evitar la Inyección de SQL (SQLi). Le mostraré que los procedimientos almacenados (por ejemplo) no son el bastón mágico contra SQLi. La responsabilidad aún permanece en el programador.
Considere el siguiente procedimiento almacenado de SQL Server que obtendrá la fila del usuario de una tabla ''Usuarios'':
create procedure getUser
@name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = ''select usrID, usrUName, usrFullName, usrRoleID ''+
''from Users ''+
''where usrUName = ''''''+@name+'''''' and usrPass = ''''''+@pass+''''''''
execute(@sql)
Puede obtener los resultados pasando como parámetros el nombre de usuario y la contraseña. Suponiendo que la contraseña está en texto libre (solo por simplicidad de este ejemplo) una llamada normal sería:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = ''admin''
,@pass = ''!@Th1siSTheP@ssw0rd!!''
GO
Pero aquí tenemos una mala técnica de programación utilizada por el programador dentro del procedimiento almacenado, por lo que un atacante puede ejecutar lo siguiente:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = ''admin''
,@pass = ''any'''' OR 1=1 --''
GO
Los parámetros anteriores se pasarán como argumentos al procedimiento almacenado y el comando SQL que finalmente se ejecutará es:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = ''admin'' and usrPass = ''any'' OR 1=1 --''
..que recuperará todas las filas de los usuarios
El problema aquí es que incluso seguimos el principio "Crear un procedimiento almacenado y pasar los campos para buscar como parámetros", aún se realiza el SQLi. Esto es porque simplemente copiamos nuestra mala práctica de programación dentro del procedimiento almacenado. La solución al problema es volver a escribir nuestro Procedimiento almacenado de la siguiente manera:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
Lo que estoy tratando de decir es que los desarrolladores deben aprender primero qué es un ataque SQLi y cómo se puede realizar y luego proteger su código en consecuencia. Seguir ciegamente las ''mejores prácticas'' no siempre es la manera más segura ... y tal vez es por eso que tenemos tantas ''mejores prácticas'', ¡falla!
Cuando los artículos hablan sobre consultas parametrizadas que detienen los ataques SQL, en realidad no explican por qué, a menudo es un caso de "Sí, así que no preguntes por qué", posiblemente porque no se conocen a sí mismos. Una señal segura de que un mal educador es alguien que no puede admitir que no sabe algo. Pero yo divago. Cuando digo que me resulta totalmente comprensible estar confundido es simple. Imagine una consulta SQL dinámica
sqlQuery=''SELECT * FROM custTable WHERE User='' + Username + '' AND Pass='' + password
así que una simple inyección sql sería simplemente poner el nombre de usuario como ''O 1 = 1-- Esto haría efectivamente la consulta sql:
sqlQuery=''SELECT * FROM custTable WHERE User='''' OR 1=1-- '' AND PASS='' + password
Esto indica que seleccione todos los clientes cuyo nombre de usuario esté en blanco ('''') o 1 = 1, que es un booleano, lo que equivale a verdadero. Luego usa - para comentar el resto de la consulta. Así que esto solo imprimirá toda la tabla de clientes, o hará lo que quiera con ella, si inicia sesión, iniciará sesión con los privilegios del primer usuario, que a menudo puede ser el administrador.
Ahora las consultas parametrizadas lo hacen de manera diferente, con un código como:
sqlQuery=''SELECT * FROM custTable WHERE User=? AND Pass=?''
parameters.add("User", username)
parameters.add("Pass", password)
donde el nombre de usuario y la contraseña son variables que apuntan al nombre de usuario y contraseña ingresados asociados
Ahora en este punto, puedes estar pensando, esto no cambia nada en absoluto. Seguramente podrías simplemente poner en el campo de nombre de usuario algo así como Nadie O 1 = 1 ''-, haciendo la consulta de manera efectiva:
sqlQuery=''SELECT * FROM custTable WHERE User=Nobody OR 1=1''-- AND Pass=?''
Y esto parecería un argumento válido. Pero, estarías equivocado.
La forma en que funcionan las consultas parametrizadas es que sqlQuery se envía como una consulta, y la base de datos sabe exactamente lo que hará esta consulta, y solo entonces insertará el nombre de usuario y las contraseñas simplemente como valores. Esto significa que no pueden afectar 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 "Nadie O 1 = 1 ''-" y una contraseña en blanco, que debería aparecer como falsa.
Sin embargo, esta no es una solución completa, y la validación de entrada aún deberá realizarse, ya que esto no afectará a otros problemas, como los ataques XSS, ya que aún se puede poner javascript en la base de datos. Luego, si esto se lee en una página, se mostraría como javascript normal, dependiendo de cualquier validación de salida. Entonces, realmente, lo mejor que se puede hacer es usar validación de entrada, pero usando consultas parametrizadas o procedimientos almacenados para detener cualquier ataque SQL.
Debido a que la mayoría del código no está escrito teniendo en cuenta la seguridad, y la administración, dada la opción entre agregar funciones (especialmente algo visible que se puede vender) y seguridad / estabilidad / confiabilidad (que es mucho más difícil de vender), casi invariablemente elegirán ex. La seguridad es solo una preocupación cuando se convierte en un problema.
Evito los absolutos en la programación; siempre hay una excepción Recomiendo procedimientos almacenados y objetos de comando. La mayoría de mi respaldo es con SQL Server, pero juego de vez en cuando con MySql. Hay muchas ventajas para los procedimientos almacenados, incluidos los planes de consulta en caché; sí, esto se puede lograr con parámetros y SQL en línea, pero eso abre más posibilidades para los ataques de inyección y no ayuda con la separación de las preocupaciones. Para mí, también es mucho más fácil proteger una base de datos ya que mis aplicaciones generalmente solo tienen permiso de ejecución para dichos procedimientos almacenados. Sin acceso directo a la mesa / vista, es mucho más difícil inyectar algo. Si el usuario de la aplicación está comprometido, solo tiene permiso para ejecutar exactamente lo que estaba predefinido.
Mis dos centavos.
La inyección SQL es un subconjunto del problema más grande de la inyección de código, donde los datos y el código se proporcionan a través del mismo canal y los datos se confunden con el código. Las consultas parametrizadas evitan que esto ocurra formando la consulta utilizando un contexto sobre qué son los datos y qué es el código.
En algunos casos específicos, esto no es suficiente. En muchos DBMS, es posible ejecutar dinámicamente SQL con procedimientos almacenados, introduciendo un error de inyección SQL en el nivel DBMS. Llamar a dicho procedimiento almacenado mediante consultas parametrizadas no evitará que se explote la inyección SQL en el procedimiento. Otro ejemplo se puede ver en esta publicación de blog .
Más comúnmente, los desarrolladores usan la funcionalidad incorrectamente. Comúnmente, el código se ve algo así cuando se hace correctamente:
db.parameterize_query("select foo from bar where baz = ''?''", user_input)
Algunos desarrolladores concatenan cadenas y luego usan una consulta parametrizada, que en realidad no hace la distinción de código / datos antes mencionada que proporciona las garantías de seguridad que estamos buscando:
db.parameterize_query("select foo from bar where baz = ''" + user_input + "''")
El uso correcto de las consultas parametrizadas proporciona una protección muy fuerte, pero no impenetrable, contra los ataques de inyección SQL.
Los enlaces que he publicado en mis comentarios a la pregunta explican muy bien el problema. He resumido mis sentimientos sobre por qué persiste el problema, a continuación:
Quienes recién comienzan pueden no tener conciencia de la inyección SQL.
Algunos conocen la inyección de SQL, pero piensan que escaparse es la (¿única?) Solución. Si realiza una búsqueda rápida de Google para la
php mysql query
, la primera página que aparece es la páginamysql_query
, en la que hay un ejemplo que muestra la interpolación de la entrada del usuario escapado en una consulta. No hay mención (al menos no que yo pueda ver) de usar declaraciones preparadas en su lugar. Como han dicho otros, hay tantos tutoriales que usan interpolación de parámetros, que no es realmente sorprendente la frecuencia con la que todavía se usa.Una falta de comprensión de cómo funcionan las declaraciones parametrizadas. Algunos piensan que es solo un medio elegante de escapar de los valores.
Otros conocen sentencias parametrizadas, pero no las usan porque han escuchado que son demasiado lentas. Sospecho que muchas personas han escuchado lo increíblemente lentas declaraciones paramétricas, pero en realidad no han hecho ninguna prueba propia. Como señaló Bill Karwin en su charla, la diferencia en el desempeño rara vez se debe utilizar como un factor cuando se considera el uso de declaraciones preparadas. Los beneficios de prepararse una vez, ejecutar muchos , a menudo parecen olvidarse, al igual que las mejoras en la seguridad y el mantenimiento del código.
Algunos usan sentencias parametrizadas en todas partes, pero con la interpolación de valores no verificados, como nombres de tablas y columnas, palabras clave y operadores condicionales. Las búsquedas dinámicas, como aquellas que permiten a los usuarios especificar una cantidad de campos de búsqueda diferentes, condiciones de comparación y orden de clasificación, son ejemplos principales de esto.
Falso sentido de seguridad al usar un ORM. Los ORM aún permiten la interpolación de partes de la declaración SQL - ver 5.
La programación es un tema grande y complejo, la administración de la base de datos es un tema grande y complejo, la seguridad es un tema grande y complejo. Desarrollar una aplicación de base de datos segura no es fácil, incluso los desarrolladores experimentados pueden quedar atrapados.
Muchas de las respuestas en no ayudan. Cuando las personas escriben preguntas que utilizan SQL dinámico y la interpolación de parámetros, a menudo hay una falta de respuestas que sugieren el uso de declaraciones parametrizadas en su lugar. En algunas ocasiones, la gente ha refutado mi sugerencia de usar declaraciones preparadas, generalmente debido a la percepción de una sobrecarga de rendimiento inaceptable. Dudo seriamente que quienes hacen la mayoría de estas preguntas estén en una posición en la que los pocos milisegundos extra que se toman para preparar una declaración parametrizada tendrán un efecto catastrófico en su aplicación.
Para proteger su aplicación de la inyección de SQL, realice los siguientes pasos:
Paso 1. Restrinja la entrada. Paso 2. Usa los parámetros con los procedimientos almacenados. Paso 3. Use los parámetros con SQL dinámico.
Consulte http://msdn.microsoft.com/en-us/library/ff648339.aspx
Primero mi respuesta a su primera pregunta: sí, hasta donde yo sé, mediante el uso de consultas parametrizadas, las inyecciones de SQL ya no serán posibles. En cuanto a sus siguientes preguntas, no estoy seguro y solo puedo darle mi opinión sobre los motivos:
Creo que es más fácil "solo" escribir la cadena de consulta SQL mediante la concatenación de algunas partes diferentes (tal vez incluso dependientes de algunas verificaciones lógicas) junto con los valores que se insertarán. Simplemente está creando la consulta y ejecutándola. Otra ventaja es que puede imprimir (echo, output o lo que sea) la cadena de consulta SQL y luego usar esta cadena para una consulta manual al motor de la base de datos.
Al trabajar con declaraciones preparadas, siempre tiene al menos un paso más: tiene que crear su consulta (incluidos los parámetros, por supuesto). Debe preparar la consulta en el servidor. Debe vincular los parámetros con los valores reales que desea. para usar para su consulta. Debe ejecutar la consulta.
Eso es algo más de trabajo (y no tan sencillo de programar) especialmente para algunos trabajos "rápidos y sucios" que a menudo resultan ser de larga duración ...
Atentamente,
Caja
Sí, el uso de declaraciones preparadas detiene todas las inyecciones de SQL, al menos en teoría. En la práctica, las declaraciones parametrizadas pueden no ser declaraciones preparadas reales, por ejemplo, PDO
en PHP las emula de forma predeterminada, por lo que está abierto a un ataque de caso extremo .
Si está utilizando declaraciones preparadas reales, todo está seguro. Bueno, al menos siempre que no concatene SQL inseguro en su consulta como reacción a no poder preparar nombres de tabla, por ejemplo.
En caso afirmativo, ¿por qué hay tantas inyecciones SQL exitosas? ¿Solo porque algunos desarrolladores son demasiado tontos para usar declaraciones parametrizadas?
Sí, la educación es el punto principal aquí, y las bases del código heredado. Muchos tutoriales usan escapes y estos no se pueden eliminar fácilmente de la web, desafortunadamente.
Yo no diría "tonto".
Creo que los tutoriales son el problema. La mayoría de los tutoriales SQL, libros, lo que sea que explique SQL con valores en línea, sin mencionar los parámetros de enlace. Las personas que aprenden de estos tutoriales no tienen la oportunidad de aprender bien.