¿Los procedimientos almacenados evitan la inyección de SQL?

83

¿Es cierto que los procedimientos almacenados evitan ataques de inyección SQL contra bases de datos PostgreSQL? Investigué un poco y descubrí que SQL Server, Oracle y MySQL no están seguros contra la inyección de SQL, incluso si solo utilizamos procedimientos almacenados. Sin embargo, este problema no existe en PostgreSQL.

¿La implementación del procedimiento almacenado en el núcleo de PostgreSQL previene los ataques de inyección SQL o es algo más? ¿O PostgreSQL también es susceptible a la inyección de SQL incluso si solo usamos procedimientos almacenados? Si es así, muéstrame un ejemplo (por ejemplo, libro, sitio, papel, etc.).

Am1rr3zA
fuente
44
Curiosamente, las principales respuestas aquí son en su mayoría OT relacionadas con SQL Server, mientras que la pregunta es sobre Postgres . Aquí hay una respuesta relacionada para Postgres: dba.stackexchange.com/questions/49699/… . Hay un par de otros, intente una búsqueda: dba.stackexchange.com/…
Erwin Brandstetter
@ErwinBrandstetter la pregunta original no fue etiquetada (por el OP) con postgres y fue, y aún así, menciona varios otros DBMS. Supongo que esa es la razón de las diversas respuestas que se centran en otros DBMS. Le sugiero que agregue uno más centrado en Postgres.
ypercubeᵀᴹ
@ ypercubeᵀᴹ: agregaré una respuesta aquí cuando encuentre tiempo. Mientras tanto, actualicé dba.stackexchange.com/questions/49699/… para que sea más claro y completo.
Erwin Brandstetter

Respuestas:

71

No, los procedimientos almacenados no impiden la inyección de SQL. Aquí hay un ejemplo real (de una aplicación interna que alguien creó donde trabajo) de un procedimiento almacenado que desafortunadamente permite la inyección de SQL:

Este código de servidor sql:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

aproximadamente equivalente a postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

La idea del desarrollador era crear un procedimiento de búsqueda versátil, pero el resultado es que la cláusula WHERE puede contener todo lo que el usuario desee, lo que permite una visita de las pequeñas Bobby Tables .

No importa si usa sentencias SQL o procedimiento almacenado. Lo que importa es si su SQL usa parámetros o cadenas concatenadas. Los parámetros evitan la inyección de SQL; cadenas concatenadas permiten la inyección de SQL.

Kyralessa
fuente
46

Los ataques de inyección SQL son aquellos en los que la entrada no confiable se agrega directamente a las consultas, lo que permite al usuario ejecutar efectivamente código arbitrario, como se ilustra en este cómic canónico XKCD.

Por lo tanto, obtenemos la situación:

userInput = getFromHTML # "Robert ') Descartar estudiantes de la tabla; -"

Query = "Seleccionar * de estudiantes donde studentName =" + userInput

Los procedimientos almacenados son, en general, buenas defensas contra los ataques de inyección SQL porque los parámetros entrantes nunca se analizan.

En un procedimiento almacenado, en la mayoría de los DB (y programas, no olvide que las consultas precompiladas cuentan como procedimientos almacenados) tienen el siguiente aspecto:

 

crear procedimientos almacenados foo (
seleccione * de estudiantes donde studentName =: 1
);

Luego, cuando el programa desea acceder, llama foo(userInput)y felizmente recupera el resultado.

Un procedimiento almacenado no es una defensa mágica contra la inyección de SQL, ya que las personas pueden escribir procedimientos almacenados incorrectos . Sin embargo, las consultas precompiladas, ya sean almacenadas en la base de datos o en el programa, son mucho más difíciles de abrir agujeros de seguridad si comprende cómo funciona la inyección SQL.

Puede leer más sobre la inyección SQL:

Brian Ballsun-Stanton
fuente
29

Sí, hasta cierto punto.
Los procedimientos almacenados por sí solos no impedirán la inyección de SQL.

Permítanme citar primero sobre la inyección SQL de OWASP

Un ataque de inyección SQL consiste en la inserción o "inyección" de una consulta SQL a través de los datos de entrada del cliente a la aplicación. Una explotación de inyección SQL exitosa puede leer datos confidenciales de la base de datos, modificar datos de la base de datos (Insertar / Actualizar / Eliminar), ejecutar operaciones de administración en la base de datos (como apagar el DBMS), recuperar el contenido de un archivo dado presente en el archivo DBMS sistema y en algunos casos emiten comandos al sistema operativo. Los ataques de inyección SQL son un tipo de ataque de inyección, en el que los comandos SQL se inyectan en la entrada del plano de datos para efectuar la ejecución de comandos SQL predefinidos.

Debe desinfectar las entradas del usuario y no concatenar las declaraciones SQL, incluso si está utilizando un procedimiento almacenado.

Jeff Attwood explicó las consecuencias de concatenar sql en " Dame SQL parametrizado o dame la muerte "

Lo siguiente es la caricatura interesante que viene a mi mente cada vez que escucho SQL Injection texto alternativo , creo que entendiste :-)

Eche un vistazo a la hoja de trucos de prevención de inyección SQL , los métodos de prevención se explican de forma clara ...

CoderHawk
fuente
12

La concatenación de cadenas es la causa de la inyección de SQL. Esto se evita utilizando la parametrización.

Los procedimientos almacenados agregan una capa adicional de seguridad al aplicar una sintaxis no válida cuando concatena, pero no son "más seguros" si usa, por ejemplo, SQL dinámico en ellos.

Entonces, su código anterior es causado por la concatenación de estas cadenas

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

Esto da una sintaxis no válida, por suerte

Parametrizarlo daría

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

Esto significa

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

Ahora, en el código anterior no obtendrás filas porque supongo que no tienes usuario x' AND 1=(SELECT COUNT(*) FROM Client); --

Si el proceso almacenado se veía así (usando SQL dinámico concatenado ), entonces su llamada de proceso almacenado parametrizada todavía permitirá la inyección SQL

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

Entonces, como se demostró, la concatenación de cadenas es el principal enemigo para la inyección de SQL

Los procedimientos almacenados agregan encapsulación, manejo de transacciones, permisos reducidos, etc., pero aún se puede abusar de ellos para la inyección de SQL.

Puede consultar Stack Overflow para obtener más información sobre la parametrización.

gbn
fuente
10

"Ataques de inyección SQL ocurren cuando la entrada del usuario se codifica de manera incorrecta. Típicamente, la entrada de usuario se algunos datos que el usuario envía con su consulta, es decir, valores en los $_GET, $_POST, $_COOKIE, $_REQUEST, o $_SERVERarrays. Sin embargo, la entrada del usuario pueden también provienen de una variedad de otros fuentes, como sockets, sitios web remotos, archivos, etc. Por lo tanto, realmente debe tratar todo menos las constantes (como 'foobar') como entrada del usuario ".

He estado investigando a fondo sobre este tema recientemente y me gustaría compartir con otros material bastante interesante, por lo que esta publicación es más completa e instructiva para todos.



De YouTube


De Wikipedia


De OWASP


Del manual de PHP


De Microsoft y Oracle


Desbordamiento de pila


Escáner de inyección SQL

Ilia Rostovtsev
fuente
2

Los procedimientos almacenados no evitan mágicamente la inyección de SQL, pero sí hacen que evitarlo sea mucho más fácil. Todo lo que tienes que hacer es algo como lo siguiente (ejemplo de Postgres):

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

¡Eso es! El problema solo surge cuando se forma una consulta a través de la concatenación de cadenas (es decir, SQL dinámico), ¡e incluso en esos casos puede vincular! (Depende de la base de datos).

Cómo evitar la inyección de SQL en su consulta dinámica:

Paso 1) Pregúntese si realmente necesita una consulta dinámica. Si está uniendo cadenas solo para configurar la entrada, entonces probablemente lo esté haciendo mal. (Hay excepciones a esta regla: una excepción es para informar consultas en algunas bases de datos, puede tener problemas de rendimiento si no lo obliga a compilar una nueva consulta con cada ejecución. Pero investigue este problema antes de saltar a eso. )

Paso 2) Investigue la forma correcta de establecer la variable para su RDBMS particular. Por ejemplo, Oracle le permite hacer lo siguiente (citando sus documentos):

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

Aquí todavía no está concatenando la entrada. ¡Estás atado de forma segura! ¡Hurra!

Si su base de datos no admite algo como lo anterior (es de esperar que ninguno de ellos siga siendo tan malo, pero no me sorprendería), o si todavía debe concatenar su entrada (como en el caso "a veces" de informar consultas como Insinué arriba), entonces debe usar una función de escape adecuada. No lo escribas tú mismo. Por ejemplo, postgres proporciona la función quote_literal (). Entonces correría:

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

De esta manera, si in_name es algo tortuoso como '[snip] o 1 = 1' (la parte "o 1 = 1" significa seleccionar todas las filas, ¡permitiendo al usuario ver los salarios que no debería!), Entonces quote_literal guarda su trasero por haciendo la cadena resultante:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

No se encontrarán resultados (a menos que tenga algunos empleados con nombres realmente extraños).

Esa es la esencia de esto! Ahora déjame dejarte con un enlace a una publicación clásica del gurú de Oracle Tom Kyte sobre el tema de la inyección SQL, para llevar el punto a casa: Linky

MWDB
fuente
No olvide mencionarlo quote_ident(), pero en general la forma más fácil de escribir SQL dinámico a prueba de inyección es usar format()y usar los marcadores %Ide posición para identificadores y %Lliterales. De esa manera, el SQL es mucho más legible que la versión equivalente que usa ||y quote_....()funciones
a_horse_with_no_name