不安全的 JDBC Statement


#Java JDBC#


JDBC Statement 形式的数据库操作,是将一个组装好的带有数据的SQL直接提交给MySQL服务,这有SQL注入的危险,是不安全的。

本文举一个不安全的例子。

数据库和表设计

-- 创建数据库
CREATE DATABASE `bank`;

-- 切换到 bank 库
USE `bank`;

-- 创建表
CREATE TABLE `user_balance` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  `balance` BIGINT NOT NULL ,
  PRIMARY KEY (`id`)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci;

user_balance 表中准备两条数据:

mysql> select * from user_balance;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | letian |    1000 |
|  2 | xiaosi |    1001 |
+----+--------+---------+
2 rows in set (0.00 sec)

一个不安全的查询案例

package demo;

import com.sun.tools.internal.ws.wsdl.document.soap.SOAPUse;

import java.sql.*;

/**
 * 使用 Statement 是不安全的
 * sql 注入
 */
public class UnsafeStatement {

    private static final String USER = "root";
    private static final String PASSWORD = "123456";

    private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    private static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/bank";

    public static void select(String name) throws ClassNotFoundException, SQLException {
        Class.forName(JDBC_DRIVER);
        Connection conn =  DriverManager.getConnection(DB_URL, USER, PASSWORD);
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
            String sql = String.format("SELECT * FROM user_balance WHERE name='%s'", name);
            System.out.println(sql);
            ResultSet resultSet = stmt.executeQuery(sql);
            while (resultSet.next()) {
                System.out.printf("id: %s, name: %s, balance: %s\n",
                        resultSet.getLong("id"),
                        resultSet.getString("name"),
                        resultSet.getLong("balance"));
            }
            resultSet.close();
        } finally {
            if (stmt != null) {
                stmt.close();
            }
            conn.close();
        }
    }


    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        select("letian' OR '1'='1");
    }
}

执行结果:

SELECT * FROM user_balance WHERE name='letian' OR '1'='1'
id: 1, name: letian, balance: 1000
id: 2, name: xiaosi, balance: 1001

select 方法的本意是根据 name 查询对应的记录。但是「坏人」精心构造了name的值,最终导致组装的SQL变成了:

SELECT * FROM user_balance WHERE name='letian' OR '1'='1'

这个SQL会查询所有的数据,不是select 方法预期的功能。

怎么办? 用 PreparedStatement。


( 本文完 )