SQL 注入(SQL Injection)是 Web 安全领域最经典、最危险的漏洞之一。
OWASP Top 10 多次将其列为“头号威胁”——因为它能直接让攻击者操控你的数据库,轻则窃取用户数据,重则删除整个系统。
本文将带你:
- 理解 SQL 注入是如何发生的;
- 看清它的真实危害;
- 掌握有效的防御手段。
一、什么是 SQL 注入?
SQL 注入 是一种攻击技术:攻击者通过在 Web 表单、URL 参数或请求体中插入恶意 SQL 片段,改变原有 SQL 语句的逻辑,从而绕过安全控制、读取或篡改数据库。
🎯 核心原因:
应用程序将用户输入直接拼接到 SQL 语句中,未做任何过滤或转义。
二、一个真实例子
假设登录接口的后端代码如下(伪代码):
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
正常用户输入:
username = "alice"password = "123456"
生成的 SQL:
SELECT * FROM users WHERE username = 'alice' AND password = '123456';
✅ 正常验证。
但攻击者输入:
username = "admin' --"password = "任意内容"
生成的 SQL 变成:
SELECT * FROM users WHERE username = 'admin' --' AND password = '任意内容';
在 SQL 中,
--是注释符,后面的内容被忽略!
结果:无需密码即可以 admin 身份登录!
更狠的攻击者可能输入:
username = "admin' OR '1'='1' --
→ 绕过所有条件,返回所有用户!
甚至执行删除操作(如果权限足够):
username = "'; DROP TABLE users; --"
三、SQL 注入的危害
| 风险等级 | 后果 |
| ⚠️ 高 | 绕过身份认证(如上述登录绕过) |
| ⚠️ 高 | 窃取敏感数据(用户密码、身份证、银行卡) |
| ⚠️ 极高 | 篡改或删除数据(如清空订单、修改余额) |
| ⚠️ 极高 | 执行系统命令(在某些数据库如 MySQL + UDF 场景下) |
| ⚠️ 中 | 植入 Web Shell 或恶意脚本(通过写入文件) |
💡 一旦数据库被攻破,整个系统就“裸奔”了。
四、如何防御 SQL 注入?
✅ 1. 使用 预编译语句(Prepared Statements) —— 最有效!
核心思想:SQL 语句结构与数据分离。
Java 示例(JDBC):
String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); ResultSet rs = stmt.executeQuery();
即使用户输入
' OR '1'='1,也会被当作普通字符串处理,不会改变 SQL 逻辑。
在主流框架中:
- MyBatis:使用
#{}(预编译),不要用${}(字符串拼接)
<!-- 安全 --> SELECT * FROM users WHERE id = #{id} <!-- 危险!可能注入 --> SELECT * FROM users WHERE name = '${name}'
- Hibernate / JPA:默认使用参数化查询,天然防注入;
- Spring Data JPA:方法名查询或
@Query中使用参数绑定。
✅ 2. 输入校验(白名单机制)
- 对用户名、ID 等字段做格式限制(如只允许字母数字);
- 使用正则表达式过滤特殊字符(如
',",;,--,/*等); - 但注意:输入校验不能替代预编译,仅作为辅助手段。
✅ 3. 最小权限原则
- 数据库连接账号不要用 root 或 sa;
- 只授予应用所需的最小权限(如只读、仅限特定表);
- 即使被注入,攻击者也无法执行
DROP或SHUTDOWN。
✅ 4. 错误信息处理
- 禁止将数据库错误直接返回给前端(如 “You have an error in your SQL syntax…”);
- 攻击者可借此判断注入点和数据库类型;
- 应返回通用错误:“系统繁忙,请稍后再试”。
✅ 5. 使用 Web 应用防火墙(WAF)
- 如 Nginx + ModSecurity、云厂商 WAF(阿里云、腾讯云);
- 可拦截常见 SQL 注入特征(如
UNION SELECT,SLEEP()); - 但无法防御高级变形注入,不能替代代码层防护。
五、总结:防御口诀
🔒 永远不要信任用户输入!
🔒 SQL 语句必须参数化!
🔒 权限最小化,错误要隐藏!
只要坚持使用 预编译 + 参数绑定,99% 的 SQL 注入风险都能消除。
延伸思考
- NoSQL(如 MongoDB)也有“注入”风险(如 JSON 注入),同样需参数化;
- ORM 框架虽安全,但若滥用原生 SQL 或动态拼接,仍可能引入漏洞;
- 定期进行 安全扫描(如 SonarQube、Burp Suite)可发现潜在注入点。
安全不是功能,而是底线。从今天起,写每一行 SQL 时,都问自己一句:“这里会被注入吗?”