动态 SQL(Dynamic SQL) injection 是一个常见且高风险的安全问题。下面从原理 → 防护手段 → 不同场景示例系统说明如何防止注入。
动态 SQL 通常是把用户输入拼接到 SQL 字符串中:
"SELECT * FROM user WHERE name = '" + name + "'"
如果 name = "admin' --"
最终 SQL 变成:
SELECT * FROM user WHERE name = 'admin' --'
攻击者可以操控 SQL 逻辑。
✅ 核心原则:永远不要直接拼接用户输入到 SQL 中
这是最有效、最推荐的方式。
String sql = "SELECT * FROM user WHERE name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
<select id="getUser">
SELECT * FROM user WHERE name = #{name}
</select>
✅ #{} 是参数化
❌ ${} 是直接拼接(易注入)
cursor.execute(
"SELECT * FROM user WHERE name = %s",
(name,)
)
${}在 MyBatis、XML、模板 SQL 中:
| 写法 | 是否安全 |
|---|---|
#{} |
✅ 安全 |
${} |
❌ 不安全(除非极特殊场景) |
❌ 错误示例:
ORDER BY ${column}
✅ 安全替代:
<choose>
<when test="sort == 'name'">ORDER BY name</when>
<when test="sort == 'age'">ORDER BY age</when>
</choose>
表名、字段名不能参数化,必须用白名单:
Set<String> allowed = Set.of("name", "age", "email");
if (!allowed.contains(column)) {
throw new IllegalArgumentException("非法字段");
}
Integer / Long数据库账号:
DROPFILE#{})ORM 默认使用参数化查询。
SELECT * FROM user
WHERE name = '${name}'
SELECT * FROM user
WHERE name = #{name}
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
❌ “转义引号就够了”
❌ “前端校验能防注入”
❌ “内部系统不用防”
这些都不安全。
动态 SQL 防注入 = 参数化查询 + 白名单 + 输入校验 + 最小权限
如果你用的是 MyBatis / JPA / JDBC / Python / Node,我可以给你针对具体代码的防注入写法。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。