SQL注入攻击的方法与预防
一、SQL注入概述
SQL 注入(SQL Injection)是一种常见且危险的 Web 应用程序安全漏洞,攻击者通过在用户输入中注入恶意 SQL 代码,从而操纵后端数据库执行。
这种攻击可能会导致数据泄露、数据篡改、权限提升甚至整个数据库的破坏。
二、SQL 注入的工作原理
SQL 注入的核心原理是"数据与代码的混淆"。
当应用程序将用户输入直接拼接到 SQL 查询中而没有适当处理时,攻击者可以插入特殊的 SQL 语法,改变原始查询的意图。
例如:
-- 原始查询
SELECT * FROM users WHERE username = 'user_input'-- 攻击者输入: admin' --
-- 最终查询
SELECT * FROM users WHERE username = 'admin' -- '
三、SQL注入的常见攻击方法
1. 基于联合查询的注入
使用 UNION 操作符将恶意查询附加到原始查询上,获取其他表的数据。
-- 原始查询
SELECT name, email FROM users WHERE id = user_input-- 攻击者输入: 1 UNION SELECT username, password FROM admin_users
2. 布尔盲注
攻击者通过真/假条件判断来推断数据库类型和版本信息
-- 判断数据库类型
' AND 1=CONVERT(int, @@version) --
3. 时间盲注
通过条件语句和延时函数判断条件真假。
-- 如果响应延迟5秒 → 数据库名的第一个字符是'a'
' AND IF(SUBSTRING(database(),1,1)='a', SLEEP(5), 0) --
4. 堆叠查询注入
执行多个 SQL 语句,实现更复杂的攻击。
-- 攻击者输入: 1; DROP TABLE users --
通过以上的注入手段,我们发现这种通过把外部传入的参数直接拼接成 SQL 的方式非常危险!
四、SQL 注入的预防措施
1. 使用参数化查询(预编译语句)
PreparedStatement 的 SQL 语句是固定的,不是直接拼出来的方式,而是将外界传进来的条件当作参数 set 进去,这样就避免了 SQL 注入的风险。
Java JDBC 示例:
```sql
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
```
MyBatis 示例:
<!-- 安全方式 -->
<select id="findUser" resultType="User">SELECT * FROM users WHERE username = #{username}
</select>
2. 使用ORM框架
现代 ORM 框架(如Hibernate、JPA)通常内置了 SQL 注入防护。
// JPA示例
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsername(@Param("username") String username);
3. 输入验证与过滤
- 白名单验证:只允许特定格式的输入(如邮箱、电话号码)
- 黑名单过滤:过滤特殊字符(如单引号、分号)
4. 最小权限原则
数据库用户应仅具有必要的最小权限,避免使用 root 或 sa 账户连接数据库。
五、MyBatis中安全使用动态SQL
大家都知道 MyBatics 有两种占位符:${} 和 #{},
但是我们应该避免在 MyBatis 中使用 ${} 进行字符串替换,可能会引发 SQL 注入风险。
而使用 #{} 可以避免 SQL 注入,原因其实就是 #{} 的底层就是预编译语句 PreparedStatement 。
如下所示:
<select id="findUsers" resultType="User">SELECT * FROM users<where><if test="name != null">AND name = #{name}</if><if test="ids != null">AND id IN<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach></if></where>
</select>
六、总结
通过采用预编译语句、ORM 框架、输入验证与过滤和最小权限原则等防御措施,可以有效地防止这类攻击。
所以我们开发人员应当始终对用户的输入保持警惕,遵循安全的编码规范,定期进行安全测试和代码审计,以确保应用程序的安全性。