侧边栏壁纸
博主头像
ProSayJ 博主等级

Talk is cheap. Show me the code.

  • 累计撰写 72 篇文章
  • 累计创建 24 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

为什么sql预编译 为什么能防止sql注入?

YangJian
2025-07-18 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

为什么 SQL 预编译(PreparedStatement)能防止 SQL 注入,并从多个层面展开分析。


一、什么是 SQL 注入?

SQL 注入是一种攻击方式,攻击者通过将恶意 SQL 语句注入输入参数中,使原本正常的查询逻辑被篡改,执行非预期的 SQL 操作(如读取全部数据、绕过登录验证、删除表等)。

示例(未预编译):

String name = "a' OR '1'='1";
String sql = "SELECT * FROM users WHERE name = '" + name + "'";

拼接后 SQL 为:

SELECT * FROM users WHERE name = 'a' OR '1'='1'

这就返回了所有用户,而不是指定 name 的记录。


二、什么是 SQL 预编译(PreparedStatement)?

SQL 预编译指的是:

数据库会 先编译 SQL 模板(不带具体值,仅保留参数占位符 ?),

然后在执行时再将参数值 安全地绑定到这些占位符上。

PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, name); // 即使 name 是恶意构造的也不会被解析为 SQL 语法

三、为什么预编译能防止注入?

✅ 核心原理:

参数值不会参与 SQL 语法解析

预编译后 SQL 模板被“锁定”,参数仅作为数据处理,数据库不会再将参数解析成 SQL 语句的一部分。


四、举个对比实验

❌ 非预编译(会注入):

String userInput = "1 OR 1=1";
String sql = "SELECT * FROM users WHERE id = " + userInput;

// 实际执行 SQL:SELECT * FROM users WHERE id = 1 OR 1=1

✅ 预编译(防注入):

PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setString(1, "1 OR 1=1");

// 实际执行:SELECT * FROM users WHERE id = '1 OR 1=1' → 字符串字面量,不参与 SQL 判断

五、数据库如何处理预编译语句?

以 MySQL 为例:

  1. 客户端发出 SQL 模板(如 SELECT * FROM users WHERE id = ?)

  2. MySQL 生成并缓存执行计划(execution plan)

  3. 再接收参数(如 id = '1 OR 1=1')

  4. 将参数 当作字面值 插入表达式中,不再进行 SQL 解析

  5. 执行安全查询


六、总结为一句话:

预编译 SQL 是将“代码”与“数据”分离的机制,从根本上杜绝了 SQL 注入的可能。

0

评论区