¿Cómo pueden las instrucciones preparadas protegerse de los ataques de inyección SQL?


¿Cómo nos ayudan las declaraciones preparadas a prevenir los ataques de inyección SQL ?

Wikipedia dice:

Las sentencias preparadas son resistentes a la inyección SQL, porque valores de los parámetros, que se transmiten más tarde protocolo, no necesita ser escapado correctamente. Si la declaración original la plantilla no se deriva de la entrada externa, la inyección SQL no se puede ocurrir.

No puedo ver la razón muy bien. Lo que sería un simple explicación en inglés fácil y algunos ejemplos?

Author: Peter Mortensen, 2011-11-25

9 answers

La idea es muy simple: la consulta y los datos se envían al servidor de la base de datos por separado.
Eso es todo.

La raíz del problema de la inyección SQL es la mezcla del código y los datos.

De hecho, nuestra consulta SQL es un programa legítimo. Y estamos creando un programa de este tipo dinámicamente, mediante la adición de algunos datos sobre la marcha. Por lo tanto, estos datos pueden interferir con el código de programa e incluso alterarlo, como cada ejemplo de inyección SQL lo muestra (todos los ejemplos en PHP/Mysql):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

Producirá una consulta regular

SELECT * FROM users where id=1

Mientras que este código

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

Producirá una secuencia maliciosa

SELECT * FROM users where id=1; DROP TABLE users;

Funciona porque estamos agregando los datos directamente al cuerpo del programa y se convierten en parte del programa, por lo que los datos pueden alterar el programa y dependiendo de los datos pasados, tendremos una salida regular o una tabla users eliminada.

Mientras que en caso de declaraciones preparadas no alter our program, it remains intact
Ese es el punto.

Estamos enviando un programa al servidor primero

$db->prepare("SELECT * FROM users where id=?");

Donde los datos son sustituidos por una variable llamada parámetro o marcador de posición.

Tenga en cuenta que la misma consulta se envía al servidor, sin ningún dato en él! Y luego estamos enviando los datos con la segunda solicitud , esencialmente separada de la consulta en sí:

$db->execute($data);

Por lo tanto, no puede alterar nuestra programa y haz cualquier daño.
Muy simple, ¿no?

Sin embargo, vale la pena señalar que no cada vez que está utilizando un marcador de posición, se procesa como una declaración preparada.

Un marcador de posición es una idea general para sustituir los datos reales con una variable para el procesamiento futuro (ver printf() por ejemplo), mientras que una instrucción preparada es el único subconjunto de la misma.

Hay casos (notablemente PDO en PHP puede hacerlo) cuando una declaración preparada se puede emular, y una consulta se compone junto con los datos y se envía al servidor en una sola solicitud. Pero es importante entender que este enfoque es igualmente seguro, porque cada bit de datos está formateado correctamente de acuerdo con su tipo y, por lo tanto, no podría suceder nada malo.

Lo único que tengo que añadir que siempre se omite en cada manual:

Las declaraciones preparadas solo pueden proteger los datos , pero no pueden defender el programa en sí mismo .
Entonces, una vez que tenemos que agregar, digamos, un identificador dinámico - un nombre de campo, por ejemplo, las declaraciones preparadas no pueden ayudarnos. He explicó el asunto recientemente, así que no me repetiré.

 229
Author: Your Common Sense,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-05 07:14:43

Aquí está SQL para configurar un ejemplo:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

La clase Inject es vulnerable a la inyección SQL. La consulta se pega dinámicamente junto con la entrada del usuario. La intención de la consulta era mostrar información sobre Bob. Ya sea salario o bono, basado en la entrada del usuario. Pero el usuario malicioso manipula la entrada corrompiendo la consulta al marcar el equivalente de un 'or true' a la cláusula where para que todo se devuelva, incluida la información sobre Aaron que se suponía que debía estar oculto.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Ejecutando esto, el primer caso es con uso normal, y el segundo con la inyección maliciosa:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

No debe construir sus sentencias SQL con concatenación de cadenas de entrada de usuario. No solo es vulnerable a la inyección, sino que también tiene implicaciones de almacenamiento en caché en el servidor (la instrucción cambia, por lo que es menos probable que obtenga una consulta de caché de instrucciones SQL, mientras que el ejemplo de bind siempre ejecuta la misma instrucción).

Aquí hay un ejemplo de Enlace a evite este tipo de inyección:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Ejecutar esto con la misma entrada que el ejemplo anterior muestra que el código malicioso no funciona porque no hay ningún tipo de pago que coincida con esa cadena:

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
 20
Author: Glenn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 18:33:16

Básicamente, con declaraciones preparadas, los datos que llegan de un hacker potencial se tratan como datos, y no hay forma de que se mezclen con su aplicación SQL y/o se interpreten como SQL (lo que puede suceder cuando los datos pasados se colocan directamente en su aplicación SQL).

Esto se debe a que las sentencias preparadas "preparan" la consulta SQL primero para encontrar un plan de consulta eficiente, y envían los valores reales que presumiblemente vienen de un formulario más tarde - en ese momento la consulta es realmente ejecutado.

Más información aquí:

Instrucciones preparadas e Inyección SQL

 13
Author: Jose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-26 05:41:23

En SQL Server, usar una instrucción preparada es definitivamente a prueba de inyección porque los parámetros de entrada no forman la consulta. Significa que la consulta ejecutada no es una consulta dinámica. Ejemplo de una sentencia vulnerable de inyección SQL.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

Ahora, si el valor en la variable inoutusername es algo así como a ' o 1 = 1--, esta consulta ahora se convierte en:

select * from table where username='a' or 1=1 -- and password=asda

Y el resto se comenta después de --, por lo que nunca se ejecuta y se omite como usando la instrucción preparada ejemplo como abajo.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

Así que en efecto no puede enviar otro parámetro, evitando así la inyección SQL...

 4
Author: lloydom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 18:39:51

La frase clave es: need not be correctly escaped. Eso significa que no debe preocuparse por las personas que intentan lanzar guiones, apóstrofos, citas, etc...

Todo se maneja por ti.

 3
Author: Feisty Mango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-24 23:19:45

Cuando crea y envía una instrucción preparada al DBMS, se almacena como la consulta SQL para su ejecución.

Más tarde vinculará sus datos a la consulta de modo que el DBMS utilice esos datos como parámetros de consulta para la ejecución (parametrización). El DBMS no usa los datos que enlaza como un complemento a la consulta SQL ya compilada; son simplemente los datos.

Esto significa que es fundamentalmente imposible realizar una inyección SQL usando instrucciones preparadas. La naturaleza misma del preparado las declaraciones y su relación con el DBMS lo impiden.

 3
Author: wulfgarpro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-13 12:24:34
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

Vamos a suponer que tienes que en un Servlet derecho. Si una persona malévola pasa un mal valor para 'filtro' que podría hackear su base de datos.

 2
Author: MeBigFatGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 18:25:21

Leí las respuestas y todavía sentí la necesidad de enfatizar el punto clave que ilumina la esencia de las Declaraciones Preparadas. Considere dos formas de consultar la propia base de datos donde la entrada del usuario está involucrada:

Enfoque Ingenuo

Se concatena la entrada del usuario con alguna cadena SQL parcial para generar una instrucción SQL. En este caso, el usuario puede incrustar comandos SQL maliciosos, que luego se enviarán a la base de datos para su ejecución.

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

Por ejemplo, el usuario malicioso la entrada puede llevar a que {[1] } sea igual a "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

Debido al usuario malicioso, SQLString contiene 2 sentencias, donde la segunda ("DROP TABLE CUSTOMERS") causará daño.

Declaraciones Preparadas

En este caso, debido a la separación de la consulta y los datos, la entrada del usuario nunca se trata como una instrucción SQL, y por lo tanto nunca se ejecuta. Es por esta razón, que cualquier código SQL malicioso inyectado no causaría ningún daño. Así que el "DROP TABLE CUSTOMERS" nunca se ejecutaría en el caso arriba.

En pocas palabras, con declaraciones preparadas código malicioso introducido a través de la entrada del usuario no se ejecutará!

 1
Author: N.Vegeta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-27 02:04:21

Causa Raíz # 1-El Problema del Delimitador

La inyección sql es posible porque usamos comillas para delimitar cadenas y también para ser partes de cadenas, por lo que a veces es imposible interpretarlas. Si tuviéramos delimitadores que no se podrían usar en datos de cadena, la inyección sql nunca habría ocurrido. Resolver el problema del delimitador elimina el problema de la inyección sql. Las consultas de estructura hacen eso.

Causa Raíz # 2-La Naturaleza Humana, las personas son Astutas y Algunos Astutos Personas Son Maliciosos Y Todas Las Personas Cometen Errores

La otra causa raíz de la inyección sql es la naturaleza humana. La gente, incluidos los programadores, comete errores. Cuando comete un error en una consulta estructurada, no hace que su sistema sea vulnerable a la inyección sql. Si no está utilizando consultas estructuradas, los errores pueden generar vulnerabilidad de inyección sql.

Cómo las Consultas Estructuradas Resuelven las Causas Raíz de SQL Inyección

Las consultas estructuradas Resuelven el Problema del Delimitador, poniendo comandos sql en una instrucción y poniendo los datos en una instrucción de programación separada. Las declaraciones de programación crean la separación necesaria.

Las consultas estructuradas ayudan a evitar que los errores humanos creen agujeros de seguridad críticos. Con respecto a los humanos que cometen errores, la inyección sql no puede ocurrir cuando se utilizan consultas de estructura. Hay formas de prevenir la inyección sql que no implican consultas estructuradas, pero el error humano normal en ese enfoque generalmente conduce a al menos cierta exposición a la inyección sql. Las consultas estructuradas son a prueba de fallos de la inyección sql. Puedes cometer todos los errores del mundo, casi, con consultas estructuradas, igual que cualquier otra programación, pero ninguna que puedas hacer se puede convertir en un ssstem tomado por inyección sql. Es por eso que a la gente le gusta decir que esta es la forma correcta de prevenir la inyección sql.

Así que, ahí lo tienes, las causas de sql la inyección y la naturaleza estructuran las consultas que las hace imposibles cuando se utilizan.

 0
Author: DanAllen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-09 16:10:48