Hice el siguiente procedimiento almacenado:
ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender
Ahora, intenté hacer algo como esto. Tal vez estoy haciendo esto mal, pero quiero estar seguro de que dicho procedimiento puede evitar cualquier inyección de SQL:
EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'
La siguiente imagen muestra el SQL anterior ejecutado en SSMS y los resultados se muestran correctamente en lugar de un error:
Por cierto, agregué esa parte después del punto y coma después de que la consulta se terminó de ejecutar. Luego lo ejecuté nuevamente, pero cuando verifiqué si la tabla tblActor existe o no, todavía estaba allí. ¿Estoy haciendo algo mal? ¿O es esto realmente a prueba de inyección? Supongo que lo que estoy tratando de preguntar aquí también es que es un procedimiento almacenado como este seguro. Gracias.
EXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
Respuestas:
Este código funciona correctamente porque es:
Para que la inyección SQL funcione, debe crear una cadena de consulta (lo que no está haciendo) y no traducir apóstrofes individuales (
'
) en apóstrofes escapados (''
) (se escapan a través de los parámetros de entrada).En su intento de pasar un valor "comprometido", la
'Male; DROP TABLE tblActor'
cadena es solo eso, una cadena simple.Ahora, si estuvieras haciendo algo en la línea de:
entonces eso sería susceptible a la inyección de SQL porque esa consulta no está en el contexto actual previamente analizado; esa consulta es solo otra cadena en este momento. Por lo tanto, el valor de
@InputParam
podría ser'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
y eso podría presentar un problema porque esa consulta se representaría y ejecutaría como:Esta es una (de varias) razones principales para usar procedimientos almacenados: inherentemente más seguro (bueno, siempre y cuando no eludir esa seguridad mediante la creación de consultas como mostré anteriormente sin validar los valores de los parámetros utilizados). Aunque si necesita crear SQL dinámico, la forma preferida es parametrizar eso también usando
sp_executesql
:Con este enfoque, alguien que intente pasar
'2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;
unDATETIME
parámetro de entrada recibirá un error al ejecutar el Procedimiento almacenado. O incluso si el Procedimiento almacenado aceptara@InputParameter
comoNVARCHAR(100)
, tendría que convertirseDATETIME
en a para pasar esasp_executesql
llamada. E incluso si el parámetro en el SQL dinámico es un tipo de cadena, al entrar en el Procedimiento almacenado, en primer lugar, cualquier apóstrofe se escaparía automáticamente a un apóstrofe doble.Hay un tipo de ataque menos conocido en el que el atacante intenta llenar el campo de entrada con apóstrofes de modo que una cadena dentro del Procedimiento almacenado que se usaría para construir el SQL dinámico pero que se declara demasiado pequeña no puede caber todo y empuja el apóstrofe final y de alguna manera termina con el número correcto de apóstrofes para que ya no se "escape" dentro de la cadena. Esto se llama Truncamiento SQL y se habló en un artículo de la revista MSDN titulado "Nuevos ataques de truncamiento SQL y cómo evitarlos", por Bala Neerumalla, pero el artículo ya no está en línea. El problema que contiene este artículo, la edición de noviembre de 2006 de MSDN Magazine , solo está disponible como un archivo de Ayuda de Windows (en .chmformato). Si lo descarga, es posible que no se abra debido a la configuración de seguridad predeterminada. Si esto sucede, haga clic derecho en el archivo MSDNMagazineNovember2006en-us.chm y seleccione "Propiedades". En una de esas pestañas, habrá una opción para "Confiar en este tipo de archivo" (o algo así) que debe verificarse / habilitarse. Haga clic en el botón "Aceptar" y luego intente abrir el archivo .chm nuevamente.
Otra variación del ataque de truncamiento es, suponiendo que se use una variable local para almacenar el valor "seguro" suministrado por el usuario, ya que cualquier comilla simple se duplicó para escapar, para completar esa variable local y colocar la comilla simple al final. La idea aquí es que si la variable local no tiene el tamaño adecuado, no habrá suficiente espacio al final para la segunda comilla simple, deje la variable terminando con una comilla simple que luego se combina con la comilla simple que finaliza el valor literal en el SQL dinámico, convirtiendo esa comilla simple final en una comilla simple escapada incrustada, y el literal de cadena en el SQL dinámico luego termina con la siguiente comilla simple que estaba destinada a comenzar el siguiente literal de cadena. Por ejemplo:
Aquí, el SQL dinámico a ejecutar es ahora:
Ese mismo SQL dinámico, en un formato más legible, es:
Arreglar esto es fácil. Solo realice una de las siguientes acciones:
No use una variable local para almacenar el valor "fijo"; simplemente ponga el
REPLACE()
directamente en la creación de Dynamic SQL:El SQL dinámico ya no se ve comprometido:
Notas sobre el ejemplo de Trunction anterior:
DELETE tableName
sea destructivo, pero es menos probable que agregue un usuario de puerta trasera o cambie una contraseña de administrador.Para obtener información más detallada relacionada con la inyección SQL (que cubre varios RDBMS y escenarios), consulte lo siguiente del Proyecto de seguridad de aplicaciones web abiertas (OWASP):
Prueba de inyección SQL
Respuesta de desbordamiento de pila relacionada en inyección de SQL y truncamiento de SQL:
¿Qué tan seguro es T-SQL después de reemplazar el 'carácter de escape?
fuente
El asunto simple es que no estás confundiendo los datos con el comando en absoluto. Los valores de los parámetros nunca se tratan como parte del comando y, por lo tanto, nunca se ejecutan.
Escribí un blog sobre esto en: http://blogs.lobsterpot.com.au/2015/02/10/sql-injection-the-golden-rule/
fuente