9

Tenemos una gran cantidad de procedimientos almacenados de SQL Server que se basan en SQL dinámico.Cómo limpiar SQL dinámico en SQL Server: evitar la inyección SQL

Los parámetros para el procedimiento almacenado se utilizan en una instrucción SQL dinámica.

Necesitamos una función de validación estándar dentro de estos procedimientos almacenados para validar estos parámetros y evitar la inyección SQL.

Asumamos que tenemos estas limitaciones:

  1. No podemos reescribir los procedimientos no utilizar SQL dinámico

  2. No podemos utilizar sp_OACreate etc., para utilizar expresiones regulares para su validación.

  3. No podemos modificar la aplicación que llama al procedimiento almacenado para validar los parámetros antes de pasarlos al procedimiento almacenado.

¿Hay algún conjunto de caracteres que podamos filtrar para garantizar que no seamos susceptibles a la inyección SQL?

+0

ouch. normalmente es 3) que debe modificarse para evitar la inyección de SQL. Recuerde, es "Inyección de SQL", no "Rechazo de SQL". Una vez que llega al DB, ya debe ser limpiado. Pero si dices que no puedes cambiar la aplicación, entonces supongo que no puedes. Interesado en ver las respuestas. – RPM1984

Respuesta

10

Creo que hay tres casos diferentes que usted tiene que preocuparse:

  • cuerdas (todo lo que requiere comillas): '''' + replace(@string, '''', '''''') + ''''
  • nombres (cualquier cosa, donde las cotizaciones no están permitidos): quotename(@string)
  • cosas que no pueden ser citados: esto requiere de listas blancas

Nota: Todo en una variable de cadena (char, varchar, nchar, nvarchar, etc.) que proviene de fuentes controladas por el usuario debe utilizar uno de los métodos anteriores. Eso significa que incluso las cosas que espera que sean números se cotizan si están almacenadas en variables de cadena.

Para más detalles, ver el Microsoft Magazine (enlace obsoleto: 19/10/2016).

Aquí hay un ejemplo usando los tres métodos:

EXEC 'SELECT * FROM Employee WHERE Salary > ''' + 
    REPLACE(@salary, '''', '''''') + -- replacing quotes even for numeric data 
    ''' ORDER BY ' + QUOTENAME(@sort_col) + ' ' + -- quoting a name 
    CASE @sort_dir WHEN 'DESC' THEN 'DESC' END  -- whitelisting 

También tenga en cuenta que al hacer todas las operaciones de cadena en línea en la declaración EXEC no hay preocupación por los problemas de truncamiento. Si asigna los resultados intermedios a las variables, debe asegúrese de que las variables sean lo suficientemente grandes como para contener los resultados. Si lo hace SET @result = QUOTENAME(@name), debe definir @result para contener al menos 258 (2 * 128 + 2) caracteres. Si lo hace SET @result = REPLACE(@str, '''', ''''''), debe definir @result para que sea dos veces el tamaño de @str (suponga que cada carácter en @str podría ser un presupuesto). Y, por supuesto, la variable de cadena que contiene la instrucción SQL final debe ser lo suficientemente grande para contener todo el SQL estático más todas las variables de resultado.

+0

Acepto aquí, depende totalmente de qué SQL se esté construyendo –

2

OWASP tiene algo de información sobre esta estrategia. Siempre debe ser una opción de último recurso (como se explica en el artículo que estoy ligarse a), pero si es la única opción ...

http://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

una cita del artículo acerca de que sea un última- opción zanja

Sin embargo, esta metodología es frágil comparación con el uso parametrizados consultas. Esta técnica solo debe ser utilizada, con precaución, para actualizar el código heredado de una manera rentable. Las aplicaciones creadas desde cero o requieren tolerancia de bajo riesgo o se han reescrito usando consultas parametrizadas .

En esencia, el argumento en contra de este enfoque es incluso si usted escapa de todos los datos erróneos conocidos, no hay garantía de que alguien no encuentre una manera de eludirlo en el futuro.

Sin embargo, para responder a su pregunta específica ...

una lista de personajes de escapar se encuentra en el artículo he vinculado anteriormente.

Editar Como se señaló, el artículo no proporciona enlaces muy buenos. Sin embargo, para SQL Server, este hace: http://msdn.microsoft.com/en-us/library/ms161953.aspx

Tenga en cuenta que la lista de caracteres que necesita para escapar variará según la plataforma DB, pero parece que está usando SQL Server, por lo que esto debería ser relevante. .

Cita del artículo siguiente:

filtrado de entrada también puede ser útil en la protección contra la inyección de SQL mediante la eliminación de caracteres de escape.Sin embargo, debido a la gran cantidad de caracteres que pueden plantear problemas, esta no es una defensa confiable. El siguiente ejemplo busca el delimitador de cadena de caracteres.

private string SafeSqlLiteral(string inputSQL) 
{ 
    return inputSQL.Replace("'", "''"); 
} 

cláusulas LIKE

Tenga en cuenta que si está utilizando una cláusula LIKE, caracteres comodín todavía deben escaparse:

s = s.Replace("[", "[[]"); 
s = s.Replace("%", "[%]"); 
s = s.Replace("_", "[_]"); 
+1

-1: El artículo no dice qué caracteres escaparse para MS SQL Server. Simplemente vincula a otro artículo que no hace obvio de qué personajes escapar. – Gabe

+0

Tienes razón. Editando mi respuesta. – David

3

Este es un problema muy desagradable, su no un problema usted desea resolver, sin embargo, aquí hay un caso trivial que funciona, (los revisores, por favor, háganme saber si me perdí un caso, esto viene con NO garantías)

create proc Bad 
    @param nvarchar(500) 
as 

exec (N'select ''' + @param + N'''') 

go 

-- oops injected 
exec Bad 'help'' select ''0wned!'' select ''' 

go 

create proc NotAsBad 
    @param nvarchar(500) 
as 

declare @safish nvarchar(1000), @sql nvarchar(2000) 
set @safish = replace(@param, '''', '''''') 

set @sql = N'select ''' + @safish + N'''' 

exec (@sql) 

go 

-- this kind of works, but I have not tested everything 
exec NotAsBad 'help'' select ''0wned!'' select ''' 
+0

+1, nunca he visto nada que sugiera que esto no funciona. – Gabe

+1

En mi opinión, ejecutar cualquier SQL dinámico utilizando cualquier cosa que no sea sp_executesql con todos los valores pasados ​​como parámetros es pura negligencia. –

+0

Aún vulnerable. Supongamos que el cuerpo de NotAsBad contiene lo siguiente: establece @sql = N'select * from '+ @ safish .... si el usuario puede adivinar el nombre de una tabla, puede enviar @param =' tablename; colocar la base de datos xyz; - ' – frankadelic

6

Los casos triviales pueden ser fijados por QUOTENAME y reemplazar:

set @sql = N'SELECT ' + QUOTENAME(@column) + 
    N' FROM Table WHERE Name = ' + REPLACE(@name, '''', ''''''); 

Aunque QUOTENAME podrán utilizarse en los literales también añadir las comillas simples y reemplazar comillas simples por comillas dobles individuales, ya que trunca la entrada a 128 caracteres no es recomendable.

Pero esto es solo la punta del iceberg.Hay nombres de varias partes (dbo.table) que debe tener en cuenta: al citar el nombre de varias partes se generará un identificador no válido [dbo.table], se debe analizar y dividir (utilizando PARSENAME), y luego se debe citar correctamente en [dbo].[table].

Otro problema son los ataques de truncamiento, que pueden suceder incluso si haces el CAMBIO trivial en literales, mira New SQL Truncation Attacks And How To Avoid Them.

El problema de la inyección de SQL nunca puede resolverse con una función mágica en cada procedimiento. Es como preguntar 'Quiero una función que haga que mi código se ejecute más rápido'. La prevención de ataques de inyección es de extremo a extremo juego que requiere la disciplina de codificación todo el camino a través de, no se puede simplemente agregar como una ocurrencia tardía. Su mejor oportunidad es inspeccionar todos los procedimientos y analizar el código T-SQL línea por línea, con la vista abierta para las vulnerabilidades, luego corrija los problemas a medida que los encuentre.

+0

Recomiendo * no * usando 'PARSENAME' porque está destinado a ser usado en nombres ya citados. Si su usuario le dice que quiere obtener datos de 'secret..table', quiere consultar contra' [secret..table] 'y obtener un error. ¡No quiere que él pueda consultar '[secreto] .. [tabla]'! – Gabe

+0

En mi opinión, ejecutar cualquier SQL dinámico utilizando cualquier cosa que no sea sp_executesql con todos los valores pasados ​​como parámetros es pura negligencia. –

0

Puede obtener SQL CLR puede ser de gran utilidad; al menos puede usarlo para escribir una desinfección mucho más efectiva de lo que se puede hacer con T-SQL. En un mundo real, puede reemplazar los procesos almacenados por completo con sentencias parametrizadas y otras estructuras más sólidas.

+0

desafortunadamente, no puedo usar CLR debido a las restricciones de DBA – frankadelic

4

Con estas limitaciones estás bastante jodido.

Aquí están dos opciones que pueden servir de orientación:

  1. Uso lista blanca validador/analizador que sólo aceptan consultas que se encuentran en un formato y con las palabras clave y las tablas que se esperan. Esto probablemente solo funcione con un muy buen analizador de SQL que realmente entienda la sintaxis.

  2. Ejecuta consultas en un entorno restringido. Por ejemplo, use una cuenta de usuario con derechos muy limitados. Por ejemplo, solo permita el acceso (de lectura) a ciertas vistas que nunca devolverán datos confidenciales y no permitirá el acceso a todas las demás vistas, todos los procedimientos almacenados, funciones y tablas. Incluso más seguro es ejecutar esas consultas en otro servidor de base de datos. Además, no olvide deshabilitar el comando OPENROWSET.

Tenga en cuenta lo siguiente:

  1. Cuando acepta todas las consultas, salvo las que tienen palabras clave no válidas, que sin duda fracasar, porque el listado negro siempre falla. Especialmente con un lenguaje tan complicado como SQL.

  2. No olvide que permitir el SQL dinámico de fuentes en las que no puede confiar es malo en su sentido más puro, incluso cuando utiliza estos consejos, porque de vez en cuando se pueden detectar bugs al enviar SQL especialmente diseñado a un servidor. Por lo tanto, incluso si aplica estos consejos, el riesgo sigue ahí.

  3. Cuando decide ir con una solución que permite SQL dinámico. No crea que puede encontrar una solución segura, especialmente si intenta proteger datos comerciales confidenciales. Contrate a un especialista en seguridad de servidores de bases de datos para que lo ayude con eso.

-1

Hay otro enfoque que puede posiblemente trabajar, aunque depende de lo que los personajes están permitidos en los parámetros del procedimiento almacenado. En lugar de escapar de los caracteres problemáticos que se pueden utilizar para la inyección de SQL, elimine los caracteres en su lugar. Por ejemplo, si tiene este SP:

create procedure dbo.MYSP(@p1 varchar(100)) 
as begin 
    set @p1 = Replace(@p1, '''',' '); -- Convert single quotes to spaces 
    set @p1 = Replace(@p1, ';', ' '); 
    set @p1 = Replace(@p1, '--', ' ');  
    set @p1 = Replace(@p1, '/*', ' ');  
    set @p1 = Replace(@p1, '*/', ' ');  
    set @p1 = Replace(@p1, 'xp_', ' ');  
    ... 
end; 

puede reemplazar cualquier comilla simple con espacios o con una cadena vacía. Este enfoque también se puede usar para reemplazar caracteres de comentarios como/* */- mediante el uso de más comandos Reemplazar (como acabo de mostrar). Pero tenga en cuenta que este enfoque solo funcionará si nunca espera estos caracteres en la entrada normal, y esto depende de su aplicación.

Nota del conjunto de caracteres reemplazados se basa en https://msdn.microsoft.com/en-us/library/ms161953(SQL.105).aspx

+0

La inyección SQL no se conoce como "inyección de una sola cita". Por una razón. –

+0

No estoy familiarizado con la 'inyección de cotización única', la técnica que acabo de describir es un método de protección contra la inyección de SQL y está basada en el artículo de Microsoft al que hice referencia anteriormente. No estoy seguro de por qué votó negativamente esta respuesta. – Ubercoder

+0

luego familiarícese con los principios básicos de seguridad y sepa por qué cualquier enfoque basado en listas negras es deliberadamente defectuoso. –

2

¿Hay un conjunto de caracteres que puede filtrar para asegurar que no son susceptibles a la inyección de SQL?

NO

inyección SQL no se llama un "cierto conjunto de caracteres de inyección", y por una razón. Filtrar ciertos caracteres podría complicar el exploit en particular, pero no previene la inyección de SQL. Para explotar una inyección SQL, uno tiene que escribir SQL. Y SQL no está limitado a algunos caracteres especiales.