Java中String不可变性不防注入,真正防护靠主动校验与参数化查询:对输入强转校验、白名单限制动态SQL、强制使用PreparedStatement,并避免用String存敏感数据。

Java 中 String 本身不可变,这个特性不负责校验,也不直接防止非法注入;它只是确保一旦字符串创建,内容就不会被意外或恶意覆盖。真正防止 SQL 注入、路径遍历、命令注入等非法行为的,是在使用字符串前做语义校验和安全处理,而不是依赖“不可变”来拦住攻击。
校验重点不在 String 是否可变,而在内容是否可信
不可变性解决的是“值被中途改写”的问题,比如传入的数据库 URL 不会被日志模块偷偷替换成 /etc/shadow。但它不会阻止你把一个恶意字符串(如 ' OR 1=1 --)原封不动地拼进 SQL 里。所以校验必须主动做:
- 对用户输入的字段(如 ID、用户名、搜索关键词)做类型强转或格式验证:ID 尝试转
Long.parseLong(),失败即拒绝;邮箱用正则^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$校验 - 对动态 SQL 片段(如
ORDER BY字段、表别名)只允许白名单枚举:List.of("name", "created_at", "status"),不在其中就抛异常 - 避免用
${}或字符串拼接构造 SQL,MyBatis 中一律用#{};JDBC 层强制走PreparedStatement,禁止Statement++拼接
不可变性间接支持安全校验的可靠性
正因为 String 不可变,你在校验之后拿到的那个对象,后续任何环节都无法篡改它——这让你的校验结果真正“落地”:
- 鉴权方法接收
String token,校验通过后,token 值仍和最初一致,不会被下游过滤器调用token.replace("A", "B")后影响实际鉴权逻辑 - 文件路径
String path = "/home/user/data.txt"经过路径白名单检查(如只允许/home/user/开头)后,后续日志打印、审计记录、IO 打开都基于同一份原始值,杜绝“校验时是合法路径,执行时已变成../../etc/passwd”的竞态
别把不可变当过滤器,该清理的还得清理
有些敏感字符串(如临时密钥片段、原始密码)即使不可变,也应避免长期驻留内存。这时不可变性反而提醒你:它不能擦除,所以一开始就不该用 String 存。正确做法是:
立即学习“Java免费学习笔记(深入)”;
- 密码类字段优先用
char[],校验完立即Arrays.fill(pwd, ' ') - 若必须用 String 做中间载体(如 OAuth code),用完即弃,不缓存、不打日志、不放入长生命周期对象
- 日志输出前统一脱敏:
logger.info("login user: {}", maskUserId(userId)),而不是指望 String 自己“变安全”


















