一、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 注入的危害
- 数据窃取:账号密码、身份证、手机号、订单、商业机密
- 数据篡改:修改金额、权限、状态、配置
- 数据破坏:删表、删库、清空数据
- 服务器入侵:读取服务器文件、写入 Webshell、提权
- 合规风险:违反《网络安全法》《个人信息保护法》,面临巨额罚款
五、防御措施
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 + 最小权限
七、思维导图

八、考试 / 面试必背高频考点
- SQL 注入的根本原因:用户输入直接拼接 SQL
- 最有效防御:参数化查询(预编译)
- MyBatis 安全写法:#{} ,不安全:${}
- 盲注分为:布尔盲注、时间盲注
- 防御三要素:预编译、权限最小、输入校验