preparedstatement本身不直接防范宽字节注入,其有效防御需配合useunicode=true、characterencoding=utf8mb4等jdbc连接参数及utf8mb4字符集配置,否则在gbk编码+错误连接设置下仍可能被绕过。

PreparedStatement 本身不直接防范宽字节注入,因为宽字节注入是 MySQL 特定编码场景下的漏洞,根源在字符集配置与 JDBC 驱动行为的配合失当,而非 SQL 参数绑定逻辑本身。只要正确配置连接参数并规范使用 PreparedStatement,宽字节注入就无法生效。
宽字节注入发生的前提条件
该攻击仅在以下三者同时满足时才可能得逞:
- 数据库表/列使用 GBK、GB2312 等双字节编码(UTF-8 下基本不可行)
- JDBC 连接 URL 未显式指定 characterEncoding=utf8(或 utf8mb4),且未设置 useUnicode=true
- 代码中仍用 字符串拼接构造 SQL(例如 Statement 或“伪 PreparedStatement”),使恶意多字节字符干扰单引号转义
为什么正确使用 PreparedStatement 能阻断它
宽字节注入依赖于:攻击者输入如 %A1%27(GBK 编码下,%A1 是有效首字节,%27 单引号被“吃掉”导致逃逸)。但 PreparedStatement 的防御机制完全绕过文本解析环节:
- SQL 模板(如
"SELECT * FROM user WHERE name = ?")在 prepare 阶段已由数据库编译定型 -
pstmt.setString(1, "%A1%27")传入的是原始字节数组,JDBC 驱动按连接声明的字符集(如 utf8mb4)将其作为完整字符串值处理,不会触发 GBK 下的“截断+逃逸”链 - 数据库收到的是带引号的纯数据,不是待解析的 SQL 片段——
%A1%27永远查不到记录,也绝不会变成' OR '1'='1
必须配套的关键配置项
仅靠 setString() 不够,需确保 JDBC 连接层不给宽字节漏洞留缝隙:
- MySQL 连接 URL 必须包含:
?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai - 数据库、表、字段的默认字符集统一设为
utf8mb4(执行ALTER DATABASE db_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;) - 禁用旧版驱动(如
com.mysql.jdbc.Driver),改用 MySQL 8.0+ 官方驱动com.mysql.cj.jdbc.Driver,它默认更严格校验编码
不安全的对照写法(务必避免)
以下任一操作都会让 PreparedStatement “失效”,重新打开宽字节入口:
- 连接 URL 漏掉
characterEncoding,且数据库用 GBK —— 即便用了 PreparedStatement,驱动可能按平台默认编码解码参数,造成错乱 - 把用户输入先做
new String(input.getBytes("GBK"), "ISO-8859-1")类似转换再塞进 setString() —— 手动破坏编码一致性 - 在 SQL 字符串里拼接任何用户内容,例如:
"SELECT * FROM " + tableName + " WHERE id = ?"—— 表名位置无法参数化,一旦 tableName 来自请求且含恶意字节,预编译整体崩塌
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











