2009-04-01 20 views
7

Por el bien del argumento, digamos que me tengo que crear una variable local que contiene una consulta SQL que tiene una inserción:SQL Server: Desinfección @param contra ataques de inyección

DECLARE @insert NVARCHAR(MAX) 
SELECT @insert = 'INSERT INTO [dbo].[' + @table + '] VALUES... 
EXEC (@insert) 

Este tubo es también va a contener un valor de columna:

DECLARE @insert NVARCHAR(MAX) 
SELECT @insert = 
    'INSERT INTO [dbo].[' + @table + '] VALUES (N''' + @message + ''')' 
EXEC (@insert) 

Ahora, obviamente estoy preocupado por un ataque de inyección, y me gustaría para garantizar el valor de ese mensaje @ no puede tomar el valor de @ inserción malicioso o mal formado como una consulta a EXEC .

Esto nos lleva a mi pregunta: ¿es suficiente escaparse de los 'caracteres en @message? ¿Hay algún otro personaje que pueda aparecer en @mensaje que pueda escapar?

Ejemplo:

DECLARE @insert NVARCHAR(MAX) 
SELECT @message = REPLACE(@message,'''','''''') 
SELECT @insert = 
    'INSERT INTO [dbo].[' + @table + '] VALUES (N''' + @message + ''')' 
EXEC (@insert) 

(cuando digo "tienen que", esto se debe a mi consulta es en un procedimiento almacenado, y este procedimiento almacenado acepta @table, que es la tabla de destino a INSERT En. No estoy interesado en discutir mi arquitectura o por qué la tabla para INSERTAR se especifica "dinámicamente" a través de un parámetro de procedimiento. Por favor, abstente de comentar en este a menos que exista otra forma además de EXEC() de consultar para especificar una tabla para INSERTAR en cuando el nombre de la tabla se recibe como un parámetro de procedimiento).

+1

¿Realmente queremos ocuparnos de las preguntas relacionadas con la forma intencional de escribir código incorrecto? – dkretz

+7

Si eso significa encontrar una solución, sí. Los desarrolladores deben lidiar con el código basura todo el tiempo, especialmente si consultan y heredan proyectos escritos en Bangladesh por $ 8/hr. No siempre nos damos el lujo de construir proyectos desde cero, construirlos de la manera que deberían * haber * sido.:) – core

Respuesta

10

Utilice sp_executesql y el quotename() incorporado. Este artículo, The Curse and Blessings of Dynamic SQL, es más o menos la referencia definitiva.

+0

++ referencia canónica – dkretz

+0

Aparentemente hay un límite de 128 longitudes para quotename(), incluso en 2008, ya que espera un identificador SQL. La referencia sugiere crear una función de comillastring() (http://www.sommarskog.se/dynamic_sql.html#quotestring), que hace exactamente lo mismo que REPLACE (@variable, '' '', '' '' '') – core

1

En lugar de llamar a EXEC (@somesql), sugiero usar el sp_executesql stored procedure. Específicamente, esto le permite pasar parámetros, y el sistema verificará que los parámetros sean válidos.

+0

Sí, desafortunadamente, SQL Server 2008 Express me dice "Debe declarar la variable de tabla '@my_table'." después de ejecutar ese código. – core

+0

Estaba seguro de haberlo hecho en SQL Server 2005, pero admito que podría estar muy cansado. Me disculpo. Voy a intentar esto mañana. –

+0

No, estoy equivocado. Eliminaré esa sección de la respuesta. –

1

En primer lugar, podría consultar la información del esquema con T-SQL regular y asegurarse de que el nombre de la tabla exista primero. De esta forma, si tiene SQL mal formado, no se ejecutará como código. Solo será un nombre de tabla VARCHAR.

DECLARE @Table AS VARCHAR(MAX) 
DECLARE @Exists AS BIT 

SET @Table = 'Vicious malformed dynamic SQL' 

SELECT @Exists = COUNT(TABLE_NAME) 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_NAME = @Table 

IF (@Exists = 1) 
    BEGIN 
    PRINT 'Table exists' 
    -- Execute dynamic SQL. 
    END 
ELSE 
    PRINT 'Invalid table' 

(o simplemente utilizar SI EXISTE (SELECCIONAR ....))

+0

Buen pensamiento. He estado haciendo lo que sigue. ¿Hay alguna ventaja de cualquiera? Lo que he estado haciendo: "SI OBJECT_ID (@ table, 'U') IS NULL ..." – core

0

Al parecer hay un límite de 128 de longitud a QUOTENAME(), incluso en 2008, de acuerdo con mi prueba, ya que espera un SQL identificador. La referencia sugiere la creación de una función quotestring(), que hace lo mismo que:

REPLACE(@variable,'''','''''') 

Por lo tanto estoy proponiendo que la respuesta es crear una función de la REPLACE() anteriores del siguiente modo:

CREATE FUNCTION quotestring(@string nvarchar(MAX)) 
RETURNS nvarchar(MAX) AS 
BEGIN 
    RETURN(REPLACE(@string,'''','''''')) 
END 

... A menos que haya entendido mal algo.

+0

¿Se puede pasar un nvarchar (MAX) como un parámetro como ese? –

+0

Claro, ¿por qué no? Ahora es una función operativa en mi desarrollador. ambiente. :) – core

Cuestiones relacionadas