多语言字符集本身不导致sql注入,但会放大编码不一致时的绕过风险——例如utf-8与gbk双字节编码差异,可能使过滤器漏掉'或--的变体形式;应用层与数据库连接层字符集声明必须严格一致,否则如0xbf27在gbk下被误解析为单引号,可绕过前端过滤。

多语言字符集本身不导致SQL注入,但会放大编码不一致时的绕过风险——比如UTF-8与GBK双字节编码差异,可能让过滤器漏掉'或--的变体形式。
字符集声明必须与数据库连接层严格一致
常见错误是应用层用UTF-8,而MySQL连接默认用latin1,导致0xbf27这类双字节序列被MySQL误解析为单引号(在GBK下),绕过前端过滤。
- Java JDBC连接串显式指定字符集:
jdbc:mysql://host/db?useUnicode=true&characterEncoding=UTF-8 - Go
database/sql需在DSN中加charset=utf8mb4,不能只靠建表时的CHARSET=utf8mb4 - PHP PDO需同时设置
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"和连接选项charset=utf8mb4
过滤器/沙盒校验前必须做统一解码
用户输入可能经过URL编码、Base64、HTML实体转义等多层包装,直接对原始request.getParameter("q")做关键字匹配会失效。
- 先调用
URLDecoder.decode(input, "UTF-8")还原原始字节,再做SQL关键字扫描 - 若后端接收JSON,需在
Content-Type: application/json解析后,对每个字符串字段单独解码 - 避免在过滤器里用
new String(bytes, "GBK")这种隐式转换——不同JVM默认编码可能不同
MyBatis等ORM框架的#{} vs ${}陷阱
#{}虽默认参数化,但若数据库配置了character_set_client=utf8mb4而应用层传入GBK编码字符串,MyBatis仍可能把0xa1aa(GBK单引号)错当成普通字符拼进预编译语句。
- 确认MySQL服务器变量:
SHOW VARIABLES LIKE 'character\_set%',重点检查character_set_client和character_set_connection - 禁用
${}拼接动态表名/列名,改用白名单校验:if (!ALLOWED_TABLES.contains(tableName)) throw new IllegalArgumentException(); - Spring Boot中通过
spring.datasource.hikari.connection-init-sql=SET NAMES utf8mb4强制初始化连接
最容易被忽略的是日志和调试输出——哪怕SQL本身安全,若logger.info("raw input: {}", userInput)打印了未解码的原始字节,攻击者可能从日志里反推出绕过路径。所有日志写入前必须做StringEscapeUtils.escapeSql()或至少input.replace("'", "''")。











