prevenir - ¿Cómo pueden las declaraciones preparadas proteger de los ataques de inyección de SQL?
proteger mysql de ataques (9)
¿Cómo ayudan las declaraciones preparadas a prevenir los ataques de inyección SQL ?
Wikipedia dice:
Las sentencias preparadas son flexibles frente a la inyección de SQL, porque los valores de los parámetros, que se transmiten posteriormente utilizando un protocolo diferente, no necesitan ser escapados correctamente. Si la plantilla de extracto original no se deriva de una entrada externa, la inyección de SQL no puede ocurrir.
No puedo ver la razón muy bien. ¿Cuál sería una explicación simple en inglés fácil y algunos ejemplos?
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 de SQL. La consulta se pega dinámicamente junto con la entrada del usuario. El objetivo de la consulta era mostrar información sobre Bob. O salario o bonificación, según la entrada del usuario. Pero el usuario malintencionado manipula la entrada que corrompe la consulta al tachar el equivalente de una cláusula ''o true'' a where para que todo se devuelva, incluida la información sobre Aaron que se suponía estaba oculta.
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"));
}
}
}
Al ejecutar esto, el primer caso es con el 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 de SQL con concatenación de cadenas de entrada del usuario. No solo es vulnerable a la inyección, sino que también tiene implicaciones de caché en el servidor (la declaración cambia, por lo que es menos probable obtener un caché de sentencia de SQL mientras que el ejemplo de vinculación siempre ejecuta la misma instrucción).
Aquí hay un ejemplo de Encuadernación para evitar 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"));
}
}
}
Al ejecutar esto con la misma entrada que en el ejemplo anterior, se muestra que el código malicioso no funciona porque no hay un paymentType 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=?
Básicamente, con declaraciones preparadas, los datos provenientes de un pirata informático potencial se tratan como datos, y no hay forma de que se mezclen con la 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 declaraciones preparadas "preparan" la consulta SQL primero para encontrar un plan de consulta eficiente y enviar los valores reales que presumiblemente proceden de un formulario posterior, en ese momento la consulta se ejecuta realmente.
Más información excelente aquí:
Cuando crea y envía una declaración preparada al DBMS, se almacena como la consulta SQL para su ejecución.
Más tarde vinculará sus datos a la consulta de manera que el DBMS use esos datos como los parámetros de consulta para la ejecución (parametrización). El DBMS no utiliza los datos que usted vincula 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 declaraciones preparadas. La naturaleza misma de las declaraciones preparadas y su relación con el DBMS lo impide.
En SQL Server , usar una declaración preparada definitivamente es a prueba de inyecciones porque los parámetros de entrada no forman la consulta. Significa que la consulta ejecutada no es una consulta dinámica. Ejemplo de una instrucción vulnerable a la 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 --
, por lo que nunca se ejecuta y se pasa por alto como si se utilizara el ejemplo de declaración preparado como se muestra a continuación.
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();
Entonces, en efecto, no puedes enviar otro parámetro, evitando así la inyección de SQL ...
La frase clave need not be correctly escaped
. Eso significa que no debe preocuparse por las personas que intentan lanzar guiones, apóstrofes, citas, etc.
Todo se maneja para ti.
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 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 dicho programa dinámicamente, agregando algunos datos sobre la marcha. Por lo tanto, estos datos pueden interferir con el código del programa e incluso modificarlo, como lo muestra cada ejemplo de inyección SQL (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 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 convierte en una parte del programa, por lo que los datos pueden alterar el programa y, según los datos que se pasen, tendremos un resultado regular o una tabla users
eliminen los users
.
Si bien en el caso de declaraciones preparadas no alteramos nuestro programa, permanece intacto
Ese es el punto.
Estamos enviando un programa al servidor primero
$db->prepare("SELECT * FROM users where id=?");
donde los datos son sustituidos por alguna 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 ella! Y luego estamos enviando los datos con la segunda solicitud, esencialmente separada de la consulta misma:
$db->execute($data);
por lo tanto, no puede alterar nuestro programa y hacer daño.
Muy simple, ¿no?
Sin embargo, vale la pena señalar que no cada vez que utiliza 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 declaración preparada es el único subconjunto de la misma.
Hay casos (especialmente PDO en PHP puede hacerlo) cuando se puede emular una declaración preparada, y una consulta se compone realmente junto con datos y se envía al servidor en una solicitud. Pero es importante entender que este enfoque es igualmente seguro , ya que cada bit de datos está formateado adecuadamente de acuerdo con su tipo y, por lo tanto, no puede pasar nada malo.
Lo único que tengo que agregar que siempre se omite en cada manual:
Las declaraciones preparadas pueden proteger solo los datos , pero no pueden defender el programa en sí .
Entonces, una vez que tenemos que agregar, digamos, un identificador dinámico, un nombre de campo, por ejemplo, las declaraciones preparadas no pueden ayudarnos. Ya expliqué el asunto recientemente , así que no me repetiré.
Leí las respuestas y aún sentí la necesidad de enfatizar el punto clave que ilumina la esencia de las Declaraciones preparadas. Considere dos formas de consultar la base de datos en la que participa el usuario:
Enfoque ingenuo
Uno concatena la entrada del usuario con alguna cadena SQL parcial para generar una declaración SQL. En este caso, el usuario puede incorporar 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, la entrada de usuarios malintencionados puede hacer que SQLString
sea igual a "SELECT * FROM CUSTOMERS WHERE NAME=''James'';DROP TABLE CUSTOMERS;''
Debido al usuario malicioso, SQLString
contiene 2 instrucciones, donde el segundo ( "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 declaració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. Por lo tanto, los "DROP TABLE CUSTOMERS"
nunca se ejecutarán en el caso anterior.
En pocas palabras, con las declaraciones preparadas, el código malicioso introducido a través de la entrada del usuario no se ejecutará.
Causa raíz n. ° 1: el problema del delimitador
La inyección Sql es posible porque utilizamos comillas para delimitar cadenas y también para ser partes de cadenas, por lo que es imposible interpretarlas a veces. Si tuviéramos delimitadores que no podrían ser utilizados en datos de cadena, la inyección sql nunca hubiera sucedido. La solución del problema del delimitador elimina el problema de inyección de sql. Las consultas de estructura hacen eso.
Causa raíz n. ° 2: la naturaleza humana, las personas son astutas y algunas personas astutas son maliciosas y todas las personas cometen errores
La otra causa de la inyección sql es la naturaleza humana. Las personas, incluidos los programadores, cometen 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 una vulnerabilidad de inyección sql.
Cómo las consultas estructuradas resuelven las causas raíz de la inyección de SQL
Las consultas estructuradas resuelven el problema del delimitador, al poner comandos sql en una declaración y poner los datos en una declaración de programación separada. Las instrucciones de programación crean la separación necesaria.
Las consultas estructuradas ayudan a evitar que el error humano cree agujeros de seguridad críticos. Con respecto a los humanos que cometen errores, la inyección de sql no puede ocurrir cuando se utilizan consultas de estructura. Hay formas de prevenir la inyección de sql que no involucran consultas estructuradas, pero el error humano normal en esos enfoques generalmente conduce a al menos cierta exposición a la inyección de sql. Las consultas estructuradas son a prueba de fallas de la inyección sql. Puede hacer todos los errores en el mundo, casi, con consultas estructuradas, igual que cualquier otra programación, pero ninguna que pueda hacer se puede convertir en un sistema tomado por la inyección sql. Es por eso que a la gente le gusta decir que esta es la manera correcta de prevenir la inyección de sql.
Entonces, ahí lo tiene, las causas de la inyección sql y las consultas estructuradas por naturaleza que las hacen imposibles cuando se usan.
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");
Supongamos que tienes eso en un servlet correcto. Si una persona malévola pasa un mal valor por ''filtro'', puede piratear su base de datos.