Si es así, ¿por qué todavía hay tantas inyecciones de SQL exitosas? ¿Solo porque algunos desarrolladores son demasiado tontos para usar declaraciones parametrizadas?
Si es así, ¿por qué todavía hay tantas inyecciones de SQL exitosas? ¿Solo porque algunos desarrolladores son demasiado tontos para usar declaraciones parametrizadas?
Respuestas:
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:
Aquellos que recién comienzan pueden no tener conocimiento de la inyección SQL.
Algunos son conscientes de la inyección SQL, pero piensan que escapar es la (¿única?) Solución. Si realiza una búsqueda rápida en Google php mysql query
, la primera página que aparece es la mysql_query
página, en la que hay un ejemplo que muestra la interpolación de la entrada de escape del usuario en una consulta. No se menciona (al menos no que yo pueda ver) de usar declaraciones preparadas en su lugar. Como han dicho otros, hay tantos tutoriales que usan la interpolación de parámetros, que no es realmente sorprendente la frecuencia con la que todavía se usa.
Falta de comprensión de cómo funcionan las declaraciones parametrizadas. Algunos piensan que es solo un medio elegante para escapar de los valores.
Otros son conscientes de las declaraciones parametrizadas, pero no las usan porque han escuchado que son demasiado lentas. Sospecho que muchas personas han escuchado lo increíblemente lentas que son las declaraciones paramterizadas, pero en realidad no han hecho ninguna prueba por su cuenta. Como señaló Bill Karwin en su charla, la diferencia en el desempeño rara vez debe usarse como un factor al considerar el uso de declaraciones preparadas. Los beneficios de preparar una vez, ejecutar muchas , a menudo parecen haberse olvidado, al igual que las mejoras en la seguridad y la capacidad de mantenimiento del código.
Algunos usan declaraciones parametrizadas en todas partes, pero con interpolación de valores no verificados, como nombres de tablas y columnas, palabras clave y operadores condicionales. Las búsquedas dinámicas, como las que permiten a los usuarios especificar varios campos de búsqueda diferentes, condiciones de comparación y orden de clasificación, son ejemplos principales de esto.
Falsa sensación de seguridad al utilizar un ORM. Los ORM aún permiten la interpolación de partes de la declaración SQL; consulte 5.
La programación es un tema grande y complejo, la gestión de bases 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 sobre stackoverflow no ayudan. Cuando las personas escriben preguntas que usan SQL dinámico e interpolación de parámetros, a menudo hay una falta de respuestas que sugieran el uso de declaraciones parametrizadas. En algunas ocasiones, he tenido personas que refutan mi sugerencia de usar declaraciones preparadas, generalmente debido a la percepción de gastos generales inaceptables de desempeño. Dudo seriamente que aquellos que 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 tengan un efecto catastrófico en su aplicación.
Cuando los artículos hablan de consultas parametrizadas que detienen los ataques SQL, no explican realmente por qué, a menudo se trata de un caso de "Sí, así que no preguntes por qué", posiblemente porque no se conocen a sí mismos. Una señal segura de un mal educador es uno que no puede admitir que no sabe algo. Pero yo divago. Cuando digo que me pareció totalmente comprensible estar confundido es simple. Imagina una consulta SQL dinámica
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
por lo que una simple inyección de sql sería solo poner el nombre de usuario como 'OR 1 = 1-- Esto efectivamente haría que la consulta sql:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
Esto dice seleccionar todos los clientes donde su 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. Entonces, esto simplemente 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, puede estar pensando, esto no cambia nada en absoluto. Seguramente aún podría poner en el campo de nombre de usuario algo como Nadie O 1 = 1 '-, haciendo efectivamente la consulta:
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 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 "Nadie O 1 = 1 '-" y una contraseña en blanco, que debería resultar falsa.
Sin embargo, esta no es una solución completa, y aún será necesario realizar la validación de entrada, ya que esto no afectará a 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ía como javascript normal, dependiendo de cualquier validación de salida. Entonces, realmente lo mejor que puede hacer es seguir usando la validación de entrada, pero usando consultas parametrizadas o procedimientos almacenados para detener cualquier ataque SQL.
Bueno, buena pregunta. La respuesta es más estocástica que determinista e intentaré explicar mi punto de vista con un pequeño ejemplo.
Hay muchas referencias en la red que nos sugieren usar parámetros en nuestras consultas o usar procedimientos almacenados con parámetros para evitar la Inyección de SQL (SQLi). Les mostraré que los procedimientos almacenados (por ejemplo) no son la varita mágica contra SQLi. La responsabilidad sigue siendo del programador.
Considere el siguiente procedimiento almacenado de SQL Server que obtendrá la fila de 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 para simplificar 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 si seguimos el principio "Crear un procedimiento almacenado y pasar los campos para buscar como parámetros" el SQLi todavía se realiza. Esto se debe a que simplemente copiamos nuestra mala práctica de programación dentro del procedimiento almacenado. La solución al problema es reescribir 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 salvaguardar su código en consecuencia. Seguir ciegamente las 'mejores prácticas' no siempre es la forma más segura ... y tal vez es por eso que tenemos tantas 'mejores prácticas': ¡fallas!
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 tablas, por ejemplo.
Si es así, ¿por qué todavía hay tantas inyecciones de 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 de código heredado. Muchos tutoriales utilizan el escape y, lamentablemente, no se pueden eliminar fácilmente de la web.
Evito los absolutos en la programación; siempre hay una excepción. Recomiendo ampliamente los procedimientos almacenados y los objetos de comando. La mayor parte de mi experiencia es con SQL Server, pero juego con MySql de vez en cuando. Los procedimientos almacenados tienen muchas ventajas, incluidos los planes de consultas en caché; sí, esto se puede lograr con parámetros y SQL en línea, pero eso abre más posibilidades para ataques de inyección y no ayuda con la separación de 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 tabla / vista, es mucho más difícil inyectar algo. Si el usuario de las aplicaciones se ve comprometido, solo se tiene permiso para ejecutar exactamente lo que estaba predefinido.
Mis dos centavos.
Yo no diría "tonto".
Creo que los tutoriales son el problema. La mayoría de los tutoriales de SQL, libros, cualquier cosa que explique SQL con valores en línea, sin mencionar los parámetros de enlace en absoluto. Las personas que aprenden de estos tutoriales no tienen la oportunidad de aprenderlo correctamente.
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 características (especialmente algo visible que se puede vender) y seguridad / estabilidad / confiabilidad (que es mucho más difícil de vender), casi invariablemente elegirán el ex. La seguridad es solo una preocupación cuando se convierte en un problema.
¿Puede la declaración parametrizada detener todas las inyecciones de SQL?
Sí, siempre que el controlador de su base de datos ofrezca un marcador de posición para cada literal SQL posible. La mayoría de los controladores de declaraciones preparados no lo hacen. Digamos que nunca encontrará un marcador de posición para un nombre de campo o para una matriz de valores. Lo que hará que un desarrollador recurra a la adaptación de una consulta a mano, utilizando concatenación y formateo manual. Con resultado previsto.
Es por eso que creé mi contenedor Mysql para PHP que admite la mayoría de los literales que se pueden agregar a la consulta de forma dinámica, incluidas matrices e identificadores.
Si es así, ¿por qué todavía hay tantas inyecciones de 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.
Primero, mi respuesta a su primera pregunta: Sí, hasta donde yo sé, al usar consultas parametrizadas, las inyecciones SQL ya no serán posibles. En cuanto a sus siguientes preguntas, no estoy seguro y solo puedo dar mi opinión sobre las razones:
Creo que es más fácil "simplemente" escribir la cadena de consulta SQL concatenar algunas partes diferentes (tal vez incluso dependiendo de algunas comprobaciones lógicas) junto con los valores que se insertarán. Es solo crear la consulta y ejecutarla. Otra ventaja es que puede imprimir (eco, salida 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.
Cuando trabajas con declaraciones preparadas, siempre tienes al menos un paso más: Tienes que construir tu consulta (incluyendo los parámetros, por supuesto) Tienes que preparar la consulta en el servidor Tienes que vincular los parámetros a los valores reales que deseas utilizar para su consulta Debe ejecutar la consulta.
Eso es algo más trabajo (y no tan sencillo de programar) especialmente para algunos trabajos "rápidos y sucios" que a menudo resultan ser muy duraderos ...
Atentamente,
Caja
La inyección de SQL es un subconjunto del problema mayor 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 usando el contexto sobre qué son datos y qué es código.
En algunos casos específicos, esto no es suficiente. En muchos DBMS, es posible ejecutar SQL dinámicamente con procedimientos almacenados, lo que introduce un defecto de inyección de SQL en el nivel de DBMS. Llamar a un procedimiento almacenado de este tipo mediante consultas parametrizadas no evitará que se aproveche 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 de manera incorrecta. Por lo general, el código se parece a esto cuando se hace correctamente:
db.parameterize_query("select foo from bar where baz = '?'", user_input)
Algunos desarrolladores concatenarán cadenas juntas y luego usarán una consulta parametrizada, que en realidad no hace la distinción de datos / código 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 consultas parametrizadas proporciona una protección muy fuerte, pero no impenetrable, contra los ataques de inyección SQL.
Para proteger su aplicación de la inyección SQL, realice los siguientes pasos:
Paso 1. Restrinja la entrada. Paso 2. Utilice parámetros con procedimientos almacenados. Paso 3. Utilice parámetros con SQL dinámico.
Consulte http://msdn.microsoft.com/en-us/library/ff648339.aspx
Incluso si las declaraciones preparadas se utilizan correctamente en todo el código de la aplicación web, aún pueden existir fallas de inyección SQL si los componentes del código de la base de datos construyen consultas a partir de la entrada del usuario de una manera insegura. El siguiente es un ejemplo de un procedimiento almacenado que es vulnerable a la inyección de SQL en el parámetro @name:
CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO
Incluso si la aplicación pasa el valor del nombre proporcionado por el usuario al procedimiento almacenado de manera segura, el procedimiento en sí lo concatena directamente en una consulta dinámica y, por lo tanto, es vulnerable.