SQL 注入

一、SQL 注入 基础定义

SQL 注入(SQL Injection)是Web 应用中最普遍、危害最高 的代码注入漏洞。当应用程序直接将用户输入拼接到 SQL 语句 并交给数据库执行,且未做安全过滤时,攻击者可构造恶意输入,欺骗数据库执行非预期操作,实现窃取、篡改、删除、越权等攻击。


二、核心原理(根本原因)

1. 漏洞本质

用户输入被当作 SQL 代码执行,而非纯数据。

2. 触发条件

  • 用户输入可控(表单、URL、Cookie、HTTP 头)
  • 输入直接拼接进 SQL 语句
  • 未做过滤、转义、预编译处理

3. 危险代码示例

复制代码
// 高危写法:直接字符串拼接
String username = request.getParameter("username");
String pwd = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username='" + username + "' AND pwd='" + pwd + "'";

4. 经典登录绕过

攻击者输入用户名:

复制代码
' OR '1'='1 -- 

最终执行 SQL:

复制代码
SELECT * FROM users WHERE username='' OR '1'='1' -- ' AND pwd=''

1=1 恒成立 → 直接登录成功。


三、常见注入类型与攻击方式

1. 联合查询注入(Union Query Inject)

  • 条件:页面有回显

  • 作用:直接读取其他表数据

  • 语句:

    ' UNION SELECT username, password FROM users --

2. 布尔盲注(Boolean Blind)

  • 条件:无数据回显,仅显示成功 / 失败

  • 原理:通过 AND 1=1 / AND 1=2 判断真假

  • 语句:

    ' AND substring(username,1,1)='a' --

3. 时间盲注(Time Blind)

  • 条件:无回显、无报错

  • 原理:用 SLEEP() 看延迟判断结果

  • 语句:

    ' AND IF(1=1, SLEEP(5), 0) --

4. 报错注入(Error-based Inject)

  • 条件:页面显示数据库报错信息

  • 原理:强制触发异常,从错误中暴数据

  • 语句:

    ' AND (SELECT COUNT(*),CONCAT((SELECT username FROM users LIMIT 1),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x) --

5. 堆叠查询注入(Stacked Queries)

  • 条件:数据库支持多语句执行

  • 作用:执行 DROP、DELETE、ALTER 等高危操作

  • 语句:

    '; DROP TABLE users; --

6. 宽字节注入

  • 条件:使用 GBK 编码
  • 原理:%df' 吃掉转义符 \
  • 典型场景:PHP + MySQL 转义过滤被绕过

四、SQL 注入的危害

  1. 数据窃取:账号密码、身份证、手机号、订单、商业机密
  2. 数据篡改:修改金额、权限、状态、配置
  3. 数据破坏:删表、删库、清空数据
  4. 服务器入侵:读取服务器文件、写入 Webshell、提权
  5. 合规风险:违反《网络安全法》《个人信息保护法》,面临巨额罚款

五、防御措施

1. 预编译语句(参数化查询)------ 首选、最有效

将 SQL 结构与用户数据完全分离,输入永远不被解析为代码。

✅ Java

复制代码
String sql = "SELECT * FROM users WHERE username=? AND password=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);

✅ Python

复制代码
sql = "SELECT * FROM users WHERE username=%s AND password=%s"
cursor.execute(sql, (username, password))

✅ PHP

复制代码
$stmt = $pdo->prepare("SELECT * FROM users WHERE username=?");
$stmt->execute([$username]);

2. 使用安全 ORM 框架

  • MyBatis:必须用 #{} ,禁止 ${}

  • Hibernate / JPA / Django ORM:默认安全

    SELECT * FROM users WHERE username=#{username}

    SELECT * FROM users WHERE username=${username}

3. 输入验证与白名单

  • 用户名:字母数字

  • 手机号:11 位数字

  • 邮箱:标准格式

    if (!username.matches("^[a-zA-Z0-9]{4,16}$")) {
    return "非法输入";
    }

4. 特殊字符转义

对 ' " \ ; -- # () 等进行转义处理。

5. 数据库最小权限原则

  • 业务账号禁止 DROP、DELETE、ALTER、FILE
  • 禁止用 root/admin 连接业务库

6. 关闭错误回显

生产环境不输出数据库原生报错,避免信息泄露。

7. WAF 防护

使用防火墙拦截典型注入特征:UNION SELECT、' OR 1=1 等。

8. 安全审计

定期扫描:SQLMap、AWVS、漏扫平台及时更新数据库补丁


六、核心总结

  • 漏洞本质:用户输入被当作 SQL 代码执行
  • 防御核心:永不直接拼接 SQL
  • 最佳方案:参数化查询 + ORM + 最小权限

七、思维导图


八、考试 / 面试必背高频考点

  1. SQL 注入的根本原因:用户输入直接拼接 SQL
  2. 最有效防御:参数化查询(预编译)
  3. MyBatis 安全写法:#{} ,不安全:${}
  4. 盲注分为:布尔盲注、时间盲注
  5. 防御三要素:预编译、权限最小、输入校验
相关推荐
PaperData7 小时前
1985-2025年全国区县专利申请与授权面板数据
数据库
snpgroupcn7 小时前
大数据量SAP迁移方案:系统越大,越不能硬搬
数据库·sap
Flynt9 小时前
Redis Cluster主节点挂了,为什么"高可用"还全员掉线?我把三次kill的记录翻出来了
数据库·redis·分布式
笃行3509 小时前
KingbaseES 数据加密全解:从 SSL 到全密态
数据库
Ivanqhz9 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
java1234_小锋10 小时前
【技术专题】Mysql8 数据库 - Mysql8 数据类型简介
数据库·mysql
知行EDI10 小时前
知行之桥 MaBang 端口使用指南——Create Order 订单创建篇
java·服务器·数据库
成旭先生11 小时前
【2026】企业信息模糊查询 API 实战:名称、注册号、统一社会信用代码、企业类型与法人一次查全
服务器·数据库·数据服务
JosieBook11 小时前
【数据库】MySQL 实战精通系列 · 第10篇:分库分表与分布式事务实战
数据库·分布式·mysql
我叫洋洋11 小时前
Cadence CIS 元器件库合并实战:3000+ 焊盘、156 个符号零冲突并入自有库
数据库·单片机·嵌入式硬件·oracle·电路