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. 防御三要素:预编译、权限最小、输入校验
相关推荐
eggrall1 小时前
MySQL表的操作
数据库·mysql
wearegogog1231 小时前
MATLAB椭圆参数检测算法实现
数据库·算法·matlab
福娃筱欢1 小时前
金仓数据库同步延迟告警处理步骤
数据库
2301_781571422 小时前
JavaScript中Object-getOwnPropertySymbols获取方法
jvm·数据库·python
jump_jump3 小时前
Drizzle 凭什么贴着 Go 跑——从设计哲学到热路径源码
数据库·性能优化·orm
jay神3 小时前
基于SpringBoot的宠物生命周期信息管理系统
java·数据库·spring boot·后端·web开发·宠物·管理系统
秋93 小时前
MySQL 8.0.46 与 MySQL 9.7.0在sql语句方面的区别并举例说明
数据库·sql·mysql
一只数据集3 小时前
NVIDIA Nemotron AIQ Agentic Safety Dataset:面向企业级智能体系统的安全与防护评估数据集全面解析
网络·数据库·安全
Amazinqc4 小时前
Mysql数据库数据软隔离的并发死锁情况
数据库·mysql·死锁