实习生给MyBatis参数少加了个#,第二天用户信息全乱套

大哥打扰一下,实习生给MyBatis参数少加了个#,第二天用户信息全乱套
最新回答
词家小生

2026-07-11 19:17:27

该事故的核心原因是实习生在MyBatis的Mapper文件中错误使用了${}代替#{},导致SQL注入漏洞被利用,攻击者通过构造恶意输入篡改了SQL逻辑,最终引发用户信息泄露和乱码问题。

一、技术原理:#{}与${}的本质区别
  • #{}是预编译占位符

    MyBatis将其转换为JDBC的PreparedStatement,参数以安全占位符(?)形式传递,数据库直接处理参数值,不会解析SQL语法。

    示例:WHERE username = #{username} → 实际执行时参数被替换为?,攻击者无法修改SQL结构。

  • ${}是字符串拼接

    MyBatis直接将参数值拼接到SQL语句中,等同于原始字符串拼接,数据库会解析拼接后的完整SQL。

    示例:WHERE username = ${username} → 若输入'admin' OR 1=1 --,最终SQL变为WHERE username = '' OR 1=1 --',导致条件恒真,返回所有数据。

二、攻击过程复盘:从输入到数据泄露
  1. 恶意输入构造攻击者输入:username=' OR 1=1 --

    '闭合原字符串,OR 1=1使条件恒真,--注释后续SQL,避免语法错误。

  2. SQL动态拼接MyBatis直接拼接参数:

    SELECT * FROM users WHERE username = '' OR 1=1 --'

    数据库执行时忽略注释部分,实际查询所有用户。

  3. 后果

    信息泄露:攻击者获取全部用户数据(如私密信息、手机号)。

    数据篡改:若漏洞存在于更新语句(如UPDATE users SET phone=${phone}),攻击者可输入乱码覆盖原数据。

三、紧急修复方案:4层防护
  1. 基础修复:替换为#{}

    <select id="getUserInfo" resultType="User"> SELECT * FROM users WHERE username = #{username} <!-- 正确写法 --></select>
  2. 强制预编译配置在mybatis-config.xml中设置默认语句类型为预编译:

    <settings> <setting name="defaultStatementType" value="PREPARED"/></settings>
  3. 敏感字段加密存储

    存储时加密(如AES):

    public void setPhone(String phone) { this.phone = AES.encrypt(phone, secretKey);}

    查询时解密,即使泄露也为密文。

  4. 数据库权限最小化

    创建只读用户,禁止删除、更新操作:

    CREATE USER 'web_user'@'%' IDENTIFIED BY 'StrongPassword!';GRANT SELECT ON user_table TO 'web_user'@'%';REVOKE DELETE, UPDATE, DROP ON *.* FROM 'web_user'@'%';
四、深度防御:代码安全实践
  1. 全局SQL注入检测

    通过GitHub Actions集成SQLMap,自动扫描注入点:

    name: SQL Injection Scanon: [push]jobs: sqlscan: steps: - uses: sqlmapproject/sqlmap@master with: args: -u
    http://testenv/api/getUser
    --risk=3 --level=5
  2. MyBatis插件拦截危险SQL

    自定义插件检测${}并抛出异常:

    @Intercepts({@Signature(type=Executor.class, method="query", args={MappedStatement.class, Object.class})})public class SqlInspectPlugin implements Interceptor { public Object intercept(Invocation invocation) { String sql = ((MappedStatement)invocation.getArgs()[0]).getBoundSql(params).getSql(); if (sql.contains("${")) throw new DangerousSqlException("禁止使用${}!"); return invocation.proceed(); }}
  3. 自动化代码审查规则(SonarQube)

    配置规则禁止${},发现即标记为严重漏洞:

    <rule key="MyBatisDynamicSqlCheck"> <regexp>(${w+})</regexp> <severity>CRITICAL</severity></rule>
五、团队改进:从事故中吸取的教训
  1. 代码审查必查项

    Mapper文件中是否出现${}。

    SQL语句是否存在字符串拼接(如+或concat函数)。

  2. 新人培训重点

    MyBatis安全规范:

    所有查询参数必须用#{}。

    动态表名/列名需使用<script>标签并严格校验:

    <script>SELECT * FROM ${tableName} WHERE id=#{id}</script> <!-- 需白名单校验tableName -->
  3. 监控预警

    日志关键字告警:${、OR 1=1、--。

    异常数据查询告警(如单次返回1000+条记录)。

六、扩展建议:安全开发素养提升
  • 参考权威资料:

    OWASP Top 10:明确SQL注入为高危漏洞。

    《Web安全攻防:渗透测试实战指南》第3章:详细讲解SQL注入原理与防御。

  • 安全意识培养:

    安全不是功能,而是开发者的基本素养,需贯穿需求、设计、编码、测试全流程。