2011-12-19 11 views
5

Esto se puede insertar fácilmente aquí porque el parámetro @ID puede ser prácticamente cualquier cosa en esta instrucción de SQL al ingresarlo, sin embargo, ¿cómo se puede evitar este exploit?Haciendo un procedimiento almacenado de SQL Server a salvo de las inyecciones de SQL

Prefiero prevenir específicamente este exploit en este nivel en lugar de nivel de aplicación, ¿alguna sugerencia?

CREATE PROCEDURE [dbo].[GetDataByID] 
@ID bigint, 
@Table varchar(150) 
AS 
BEGIN 

Declare @SQL Varchar(1000) 

SELECT @SQL = 'SELECT * FROM ' + @Table + ' WHERE ID = ' + CONVERT(varchar,@ID) 

SET NOCOUNT ON; 

EXEC(@sql) 
END 
+1

Sólo una pequeña cosa que no afecta mucho las cosas, pero ¿no es el param de '@ Table' ese el problema aquí? '@ ID' es un' bigint', por lo que solo puede ser un número cuando se llega al punto de construir la declaración SQL dinámica, ¿verdad? –

+0

Supongo que ambos ... –

Respuesta

9

Comprobar this page, que tiene una guía maravillosa para SQL dinámico, y las opciones para ejecutar de forma segura

En su caso, debería ser así:

SELECT @SQL = N'SELECT * FROM ' + quotename(@Table) + N' WHERE ID = @xid' 
EXEC sp_executesql @SQL, N'@xid bigint', @ID 
+0

+1 Para vincular a Sommarskog. – Oded

+1

+1 siendo la primera vez que conozco a alguien cuya actividad me encuentro en StackOverflow;) – Tao

+0

¡lol! Hola Tao! :) –

1

1) crear una nueva tabla que tendrá una identidad PK y contendrá los nombres de la tabla de cadenas
2) inserte todas/solo las tablas válidas que permitirá en su procedimiento
3) use this int identidad PK como el valor del parámetro de entrada (TableID) para el procedimiento almacenado
4) en el procedimiento, simplemente busque el valor de cadena (nombre de tabla) desde la identidad PK dada y puede concatenar esa cadena buscada en su consulta . 5) su cláusula WHERE está bien ya que pasa en int

0

Recomendaría evitar sql dinámico por completo. Los problemas son los siguientes:

  • La inyección obvia adjuntar escenario
  • ataques de inyección binarias son mucho más inteligentes y pueden pasar por alto de cuerda tradicional escapar
  • Rendimiento es el grande - SQL Server está diseñado para gestionar los planes de ejecución de procedimientos almacenados y se ejecutarán más rápido que las consultas que se generan dinámicamente. Si está utilizando sql dinámico, no hay ningún beneficio real para usar un procedimiento almacenado. Si desea flexibilidad en el código para seleccionar de varias tablas, debe considerar un ORM o algo para facilitar su código. Teniendo en cuenta que debe pasar dinámicamente la tabla, me atrevería a decir que no tiene sentido un procedimiento como el anterior y una solución diferente es la mejor opción. Si solo está escribiendo contra SQL (es decir, sin ORM), entonces el código que genera procesos separados sería incluso una mejor opción.

NOTA QUOTENAME NO garantizamos que está a salvo de la inyección. La inyección de truncamiento todavía es posible. Lea http://msdn.microsoft.com/en-us/library/ms161953.aspx antes de usarlo.

+0

Gracias por su respuesta, pero ¿puede publicar un enlace de trabajo sobre QUOTENAME? –

+0

No estoy seguro de lo que sucedió allí :) Debería funcionar ahora. – Gats

0

Aunque aconsejaría en contra de dynamic sql en general, en este caso creo que puede salirse con la suya comprobando si la variable @Table contiene un nombre de tabla válido.

  • La pregunta es si usted planea permitir nombres de esquema y/o consultas a través del db, estoy asumiendo que usted no quiere salir de la db (o el servidor) aquí, pero sí permiten diferentes esquemas de (AdventureWorks muestra cómo se pueden usar)
  • También PODRÍA incluir vistas para @Table.
  • Probablemente sería "bueno" si también verificara si el objeto encontrado tiene una columna ID y arroja un error de "usuario amigable" si no es así. Opcional sin embargo.

Solo poniendo QuoteName() alrededor de @table NO lo protegerá de todo.Aunque es una gran función, está lejos de ser perfecta. En mi humilde opinión, su mejor opción sería analizar la variable @Table, verificar si su contenido es válido y luego crear un sql dinámico basado en las partes obtenidas. empecé a cabo haciendo la mayor parte de arriba y es sorprendente que no se requiere una gran cantidad de comprobación de algo que parece tan simple como esto =)

CREATE PROCEDURE [dbo].[GetDataByID] ( 
             @ID bigint, 
             @Table nvarchar(300) 
            ) 
AS 

DECLARE @sql nvarchar(max) 

DECLARE @server_name sysname, 
     @db_name  sysname, 
     @schema_name sysname, 
     @object_name sysname, 
     @schema_id int   

SELECT @server_name = ParseName(@Table, 4), 
     @db_name  = ParseName(@Table, 3), 
     @schema_name = ParseName(@Table, 2), 
     @object_name = ParseName(@Table, 1) 

IF ISNULL(@server_name, @@SERVERNAME) <> @@SERVERNAME 
    BEGIN 
     RaisError('Queries are restricted to this server only.', 16, 1) 
     Return(-1) 
    END 

IF ISNULL(@db_name, DB_Name()) <> DB_Name() 
    BEGIN 
     RaisError('Queries are restricted to this database only.', 16, 1) 
     Return(-1) 
    END 


IF @schema_name IS NULL 
    BEGIN 
     IF NOT EXISTS (SELECT * 
          FROM sys.objects 
         WHERE name = @object_name 
          AND type IN ('U', 'V')) 
      BEGIN 
       RaisError('Requested @Table not found. [%s]', 16, 1, @object_name) 
       Return(-1) 
      END 

     SELECT @sql = 'SELECT * FROM ' + QuoteName(@object_name) + ' WHERE ID = @ID' 
    END 
ELSE 
    BEGIN 

     SELECT @schema_id = Schema_id(@schema_name) 

     IF @schema_id IS NULL 
      BEGIN 
       RaisError('Unrecognized schema requested [%s].', 16, 1, @schema_name) 
       Return(-1) 
      END 

     IF NOT EXISTS (SELECT * 
          FROM sys.objects 
         WHERE name = @object_name 
          AND schema_id = @schema_id 
          AND type IN ('U', 'V')) 
      BEGIN 
       RaisError('Requested @Table not found. [%s].[%s]', 16, 1, @schema_name, @object_name) 
       Return(-1) 
      END 

     SELECT @sql = 'SELECT * FROM ' + QuoteName(@schema_name) + '.' + QuoteName(@object_name) + ' WHERE ID = @ID' 
    END 

EXEC sp_executesql @stmt = @sql, 
        @params = N'@ID bigint', 
        @ID  = @ID 

Return(0)  

Supra compila, pero es posible que tenga que limar algunos errores como yo no' Basta con verificar todas las rutas de código.

Cuestiones relacionadas