LIKE直接拼接用户输入必然导致SQL注入,因%和_是SQL通配符,且攻击者可借引号闭合、注入OR 1=1等恶意逻辑;即使加了前后%(如'%'+input+'%'),未使用预编译(#{})即等同于将SQL解释权交予用户。

LIKE直接拼接用户输入为什么必然导致SQL注入
因为%和_是SQL标准通配符,而攻击者只要控制任意一段被拼进SQL字符串的内容,就能闭合引号、插入OR 1=1或;执行多语句——哪怕你只加了"%" + userInput + "%",只要没走预编译,就等于把SQL解释权交给了用户。
典型错误写法:WHERE name LIKE '%${username}%'。当输入为admin' OR '1'='1时,实际执行的是:WHERE name LIKE '%admin' OR '1'='1%',整个条件恒真。
CONCAT函数本身不防注入,关键在怎么调用它
CONCAT只是个MySQL函数,它不负责安全;真正起作用的是它必须配合预编译参数(即#{})使用,且所有参与拼接的值都必须是占位符。
- ✅ 正确:
WHERE name LIKE CONCAT('%', #{keyword}, '%')→ JDBC驱动把#{keyword}当作纯数据传入,数据库只执行函数,不解析参数内容 - ❌ 错误:
WHERE name LIKE CONCAT('%', '${keyword}', '%')→${}是字符串替换,照样拼进SQL文本,毫无防护 - ❌ 错误:
WHERE name LIKE CONCAT('%', ?, '%')但用statement.executeUpdate()而非PreparedStatement→ 占位符没被识别,还是拼接
Java侧手动拼%比SQL里用CONCAT更推荐
多数项目没必要非在SQL层用CONCAT,直接在Java里拼好再绑定更清晰、兼容性更好、也更容易做空值判断。
- 先清洗:
String keyword = userInput == null ? "" : userInput.trim() - 再拼接:
String pattern = "%" + keyword + "%" - 再绑定:
ps.setString(1, pattern),对应SQL中WHERE name LIKE #{pattern} - 注意:如果
keyword为空或null,"%" + null + "%"会变成"%null%",查不到数据——得提前拦截
不同数据库对通配符拼接的支持差异
别假设CONCAT通用。MySQL 5.7+支持CONCAT('%', #{k}, '%');PostgreSQL要用'%' || #{k} || '%';Oracle同理,但还要注意NULL与空字符串行为不同——'%' || NULL || '%'结果是NULL,整行WHERE失效。
最稳妥的做法:统一在Java层拼%,只用#{}传参。这样既避开数据库函数差异,又避免XML里写死转义逻辑,还方便加日志和监控。

















