Java安全编码之SQL注入
字数 1334 2025-08-15 21:31:37

Java安全编码之SQL注入防护指南

1. 框架介绍

当前Java项目中广泛采用的两个持久层框架:

  • Hibernate:全自动ORM框架,自动生成SQL语句
  • MyBatis:支持自定义SQL的持久层框架,因其非侵入性逐渐取代Hibernate

2. Hibernate中的SQL注入防护

2.1 环境配置

  • SpringBoot 2.3.1.RELEASE
  • MySQL 5.7.20

2.2 常见漏洞场景

2.2.1 SQL注入(字符串拼接方式)

// 危险示例:直接拼接SQL
String sql = "from User u where u.userName like '%" + username + "%'";

攻击方式:

  • 直接使用SQLMap等工具可轻松注入

2.2.2 HQL注入

特点:

  • 利用难度大于SQL注入
  • 系统表通常未映射,难以获取系统信息
  • 复杂语句支持较差

2.2.3 预编译防护(推荐方式)

// 安全示例:使用setParameter预编译
Query query = this.entityManager.createQuery(
    "from User u where u.userName like :userName", User.class);
query.setParameter("userName", "%"+username+"%");

预编译机制:

  1. 单引号会被转义为两个单引号(' → '')
  2. 反斜杠会被转义为双反斜杠(\ → \)
  3. 根据参数类型调用不同方法(setString/setInt等)

底层实现:

  • 通过ClientPreparedQueryBindings.setString方法处理参数
  • 对特殊字符进行转义处理

3. MyBatis中的SQL注入防护

3.1 安全使用方式(#{}预编译)

<!-- 安全示例:使用#{}预编译 -->
<select id="getList" resultType="User">
    SELECT * FROM user_tbl WHERE username LIKE #{param}
</select>

特点:

  • 预编译机制与Hibernate相同
  • 参数会被正确转义处理

3.2 危险使用方式(${}字符串拼接)

<!-- 危险示例:使用${}字符串拼接 -->
<select id="getList" resultType="User">
    SELECT * FROM user_tbl WHERE username LIKE '${param}'
</select>

风险:

  • 直接拼接SQL语句,易受SQL注入攻击
  • SQLMap等工具可轻松注入

3.3 Order By特殊处理

常见误区:

  • "Order By不会参与预编译"是错误的
  • 实际是预编译会使Order By参数被引号包裹而失效

解决方案:

  1. 将Order By字段直接写死在代码中
  2. 使用白名单校验Order By参数
  3. 通过返回数据顺序差异获取信息(盲注)

3.4 useServerPrepStmts参数

重要说明:

  • 只有JDBC开启useServerPrepStmts=true才是真正的服务端预编译
  • 但字符串拼接方式即使开启预编译也无效
  • JDBC默认不开启此参数的原因在于预编译机制本身已提供足够防护

4. 综合防护方案

4.1 优先使用预编译

  • 在Hibernate中使用setParameter
  • 在MyBatis中使用#{}

4.2 无法预编译时的替代方案

  1. 类型严格化

    • 数字参数规范为Integer/Long等类型
    • 在数据库操作前会进行类型检查
  2. 过滤方案

    • 使用Spring AOP添加前置Filter
    • 全局过滤有害字符(可能影响正常业务)
    • 使用注解方式过滤特定参数(推荐)
  3. 工具类

    • 使用Apache Jakarta Commons提供的过滤方法

4.3 其他建议

  • 避免直接拼接SQL语句
  • 对用户输入进行严格校验
  • 使用最小权限原则配置数据库账户
  • 定期进行安全审计和渗透测试

5. 关键总结

  1. 预编译是防护SQL注入的核心机制,能使用预编译的地方必须使用
  2. 理解框架底层处理机制(如Hibernate的HQL、MyBatis的#{}和${}区别)
  3. 特殊场景(如Order By)需要特殊处理
  4. 无法预编译时要有替代方案(类型检查、过滤等)
  5. 防御要分层,不能仅依赖单一防护措施

通过遵循这些原则和实践,可以显著降低Java应用中的SQL注入风险。

Java安全编码之SQL注入防护指南 1. 框架介绍 当前Java项目中广泛采用的两个持久层框架: Hibernate :全自动ORM框架,自动生成SQL语句 MyBatis :支持自定义SQL的持久层框架,因其非侵入性逐渐取代Hibernate 2. Hibernate中的SQL注入防护 2.1 环境配置 SpringBoot 2.3.1.RELEASE MySQL 5.7.20 2.2 常见漏洞场景 2.2.1 SQL注入(字符串拼接方式) 攻击方式: 直接使用SQLMap等工具可轻松注入 2.2.2 HQL注入 特点: 利用难度大于SQL注入 系统表通常未映射,难以获取系统信息 复杂语句支持较差 2.2.3 预编译防护(推荐方式) 预编译机制: 单引号会被转义为两个单引号(' → '') 反斜杠会被转义为双反斜杠(\ → \\) 根据参数类型调用不同方法(setString/setInt等) 底层实现: 通过 ClientPreparedQueryBindings.setString 方法处理参数 对特殊字符进行转义处理 3. MyBatis中的SQL注入防护 3.1 安全使用方式(#{}预编译) 特点: 预编译机制与Hibernate相同 参数会被正确转义处理 3.2 危险使用方式(${}字符串拼接) 风险: 直接拼接SQL语句,易受SQL注入攻击 SQLMap等工具可轻松注入 3.3 Order By特殊处理 常见误区: "Order By不会参与预编译"是错误的 实际是预编译会使Order By参数被引号包裹而失效 解决方案: 将Order By字段直接写死在代码中 使用白名单校验Order By参数 通过返回数据顺序差异获取信息(盲注) 3.4 useServerPrepStmts参数 重要说明: 只有JDBC开启 useServerPrepStmts=true 才是真正的服务端预编译 但字符串拼接方式即使开启预编译也无效 JDBC默认不开启此参数的原因在于预编译机制本身已提供足够防护 4. 综合防护方案 4.1 优先使用预编译 在Hibernate中使用setParameter 在MyBatis中使用#{} 4.2 无法预编译时的替代方案 类型严格化 数字参数规范为Integer/Long等类型 在数据库操作前会进行类型检查 过滤方案 使用Spring AOP添加前置Filter 全局过滤有害字符(可能影响正常业务) 使用注解方式过滤特定参数(推荐) 工具类 使用Apache Jakarta Commons提供的过滤方法 4.3 其他建议 避免直接拼接SQL语句 对用户输入进行严格校验 使用最小权限原则配置数据库账户 定期进行安全审计和渗透测试 5. 关键总结 预编译是防护SQL注入的核心机制 ,能使用预编译的地方必须使用 理解框架底层处理机制(如Hibernate的HQL、MyBatis的#{}和${}区别) 特殊场景(如Order By)需要特殊处理 无法预编译时要有替代方案(类型检查、过滤等) 防御要分层,不能仅依赖单一防护措施 通过遵循这些原则和实践,可以显著降低Java应用中的SQL注入风险。