Explicación detallada del caso del problema de inyección JDBC SQL (versión simple y fácil de entender)

¿Qué es la inyección SQL?
La inyección SQL significa que la aplicación web no juzga la legalidad de la entrada de datos por parte del usuario o que el filtrado no es estricto. El atacante puede agregar declaraciones SQL adicionales al final de la declaración de consulta definida en la aplicación web sin el conocimiento del administrador. En el caso de operaciones ilegales, engañar al servidor de la base de datos para realizar cualquier consulta no autorizada, a fin de obtener la información de datos correspondiente.
En pocas palabras,
cuando los datos ingresados ​​por el usuario tienen palabras clave o sintaxis SQL, y participa en la compilación de la declaración SQL, el resultado de la condición después de compilar la declaración SQL es verdadero y siempre se obtiene el resultado correcto. Llamado de inyección SQL


Caso ella vino en silencio ...

Inserte la descripción de la imagen aquí


Crear tabla

  • Crear una tabla de usuario Usuario
    • ID clave principal, crecimiento automático
    • el tipo de cadena del nombre de usuario no está vacío
    • el tipo de cadena de contraseña no está vacío
    • tipo de cadena telefónica
  • Insertar 2 declaraciones de prueba
#创建数据库
CREATE DATABASE temp CHARACTER SET utf8;
#使用该数据库
USE temp;
#创建用户表
CREATE TABLE user (
	id INT PRIMARY KEY auto_increment,
	username CHARACTER(20) NOT NULL,
	password CHARACTER(20) NOT NULL,
	phone CHARACTER(11)
) charset = utf8;
#初始化(插入)表中数据
INSERT INTO user (username, password, phone) VALUES ('ziph', '123456', '16688889999');
INSERT INTO user (username, password, phone) VALUES ('zhangsan', '123456', '16644445555');

Iniciar sesión

  • A través de la consola, el usuario ingresa el nombre de usuario y la contraseña
  • El nombre de usuario y la contraseña ingresados ​​por el usuario se utilizan como parámetros para escribir una declaración SQL de consulta.
  • Si se consulta a un usuario, el usuario existe, solicitando un inicio de sesión exitoso; de lo contrario, solicita un error.

Código JDBC simple:

import java.sql.*;
import java.util.Scanner;

public class TestLogin {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String userName = scanner.nextLine();
        System.out.print("请输入密码:");
        String password = scanner.nextLine();

        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接对象,连接数据库
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8", "root", "123456");
        //3.创建执行SQL语句的对象
        Statement statement = connection.createStatement();
        //4.编写SQL语句,并执行SQL语句
        String sql = "select * from user where username = '" + userName + "' and password = '" + password + "'";
        ResultSet resultSet = statement.executeQuery(sql);
        //5.处理结果
        if (resultSet.next()) {//通过参数,查到一行数据,就提示用户的登陆成功!
            System.out.println("登录成功!");
        } else {
            System.out.println("登录失败!");
        }
        //6.释放资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

La versión simple y fácil de entender de las pruebas de inyección SQL es la siguiente:

Nombre de usuario, contraseña (no se encuentra en la base de datos de nombres de usuario, la contraseña se ingresa de manera casual) Ingresé el contenido en la consola, ¡pero el inicio de sesión fue exitoso! Que esta pasando Entonces echemos un vistazo nuevamente, ¡vea la imagen a continuación!
Inserte la descripción de la imagen aquí


Inserte la descripción de la imagen aquí
Eche un vistazo a la parte resaltada: esta parte es la declaración ejecutada por SQL, y el nombre de usuario y la contraseña son la información ingresada en nuestra consola (también involucrada en la ejecución de las declaraciones SQL). Pero cuando ingresa la cadena lalala no hay problema, pero cuando vuelve a ingresar ', cuando se ejecuta SQL, pensará que' es el final de userName. Junto con esto, ingresé el contenido detrás o 1 = 1 para participar en la ejecución de SQL en lugar de la contraseña ingresada. Aquí explico, porque 1 = 1, el valor predeterminado es verdadero, por lo que se pasa la contraseña. ; Se considera como la marca final de la instrucción SQL, seguida de un # comentado todo después de la contraseña y así sucesivamente. Cuando resultSet.next () juzga, debido a que su resultado devuelve el tipo booleano, la inyección SQL anterior naturalmente devuelve un verdadero. ¡Cruzó con éxito los límites de la cuenta y la contraseña, y el inicio de sesión fue exitoso! Esta es la inyección SQL! ¡Todos pueden entenderlo! ¡Entonces sigamos leyendo la siguiente declaración! Esta declaración es la declaración ejecutada por SQL después de la inyección de SQL:

sql = "select * from user where username='xxx' or 1=1;#'and password ='123456'";

¿Cómo evitar problemas de inyección SQL?

La instrucción SQL se compila después de que el usuario ingresa los datos y luego se compila en instrucciones SQL después de la integración. Entonces, para evitar el problema de la inyección de SQL, la declaración SQL se compiló antes de que el usuario ingrese los datos, y la declaración SQL se compiló en una declaración SQL completa, y luego los datos se llenan (debe compilar la declaración SQL en una línea por adelantado después de ingresarla en nuestra consola) Cadena larga, evitar la inyección), desea saber cómo resolver, luego continúe mirando los siguientes puntos clave, ¡recuerde ser el punto clave!


Solicitud de declaración preparada

La interfaz PreparedStatement hereda la interfaz de instrucción. ¡No hay diferencia en el método de ejecución de sentencias SQL!

Papel:

  • 1. Sentencias SQL precompiladas, ¡alta eficiencia!
  • 2. Seguridad, evite la inyección de SQL
  • 3. Puede completar datos dinámicamente y ejecutar múltiples sentencias SQL homogéneas

Marcador de parámetro

//1.预编译SQL语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
  • Nota: Cuando se aplica PreparedStatement, los parámetros de la cadena SQL están representados por el símbolo?, Que se denomina marcador de parámetro. Antes de ejecutar la instrucción SQL, asigne valores a cada parámetro

Enlace de parámetros dinámicos

pstmt.setXxx (subíndice, valor); el subíndice del parámetro comienza desde 1 y vincula el valor para el subíndice de marcador de posición especificado

//2.为占位符下标赋值
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);

Resuelva este problema de inyección SQL de prueba

import java.sql.*;
import java.util.Scanner;

public class TestSafeLogin {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String username = scanner.nextLine();
        System.out.print("请输入密码:");
        String password = scanner.nextLine();

        //1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8","root", "123456");
        //3.创建执行sql语句的对象
        String sql = "select * from user where username = ? and password = ?";
        //预编译SQL语句————提前把SQL语句编译为字符串,其中用到了转义字符防止个别符号注入SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        /**
         * 查看预编译后的SQL语句字符串
         * 此查看不计为jdbc的开发步骤中
         */
        System.out.println(preparedStatement);

        //为占位符下标赋值
        preparedStatement.setString(1, username);
        preparedStatement.setString(2, password);

        /**
         * 查看赋值后SQL语句的字符串
         * 此查看不计为jdbc的开发步骤中
         */
        System.out.println(preparedStatement);

        //4.执行SQL语句————此时executeQuery()不需要在传入参数
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.处理结果
        if (resultSet.next()) {//通过参数,查到一行数据,提示用户登录成功!
            System.out.println("登陆成功!");
        } else {
            System.out.println("登录失败!");
        }
        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

Resumen

¡Ingrese el nombre de usuario y la contraseña de la pregunta de inyección SQL y obtenga el siguiente resultado!

Vea el resultado de la cadena de instrucción SQL precompilada: com.mysql.jdbc.JDBC4PreparedStatement@4b9af9a9: seleccione * del usuario donde nombre de usuario = ** NO ESPECIFICADO ** y contraseña = ** NO ESPECIFICADO **

Vea el resultado de la cadena de la instrucción SQL después de la asignación: com.mysql.jdbc.JDBC4PreparedStatement@4b9af9a9: seleccione * del usuario donde username = 'lalal' o 1 = 1; # 'y contraseña =' asdsad '

Resultado final: ¡ inicio de sesión fallido!

Nota: Podemos ver que 'lalal detrás' se ha agregado con un carácter de escape, o 1 = 1; # seguido del indicador de finalización del nombre de usuario que se debe agregar en la instrucción SQL '.

Publicado 155 artículos originales · elogiados 337 · 90,000 vistas +

Supongo que te gusta

Origin blog.csdn.net/weixin_44170221/article/details/105259177
Recomendado
Clasificación