在 Java 项目中,SQL 注入防御至关重要,以下是详细的防御方法:
- 使用预编译语句(PreparedStatement):这是防止 SQL 注入的最有效手段之一。它将 SQL 语句的结构与用户输入数据分离,确保输入内容始终作为数据处理,而非可执行的代码。例如:
String sql = "SELECT * FROM users WHERE username = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
try (ResultSet rs = pstmt.executeQuery()) {
// 处理查询结果
}
}
- 使用 ORM 框架:如 Hibernate、JPA 等,这些框架会自动生成参数化查询,减少手写 SQL 带来的注入风险。例如,使用 Hibernate 的 Criteria API:
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(cb.equal(root.get("username"), username),
cb.equal(root.get("password"), password));
User user = session.createQuery(query).uniqueResult();
- 输入验证与过滤:对用户输入的数据进行严格验证和过滤,限制输入格式和长度,使用白名单过滤。例如,校验用户名是否符合规则(仅允许字母、数字、下划线):
if (!username.matches("^(a-zA-Z0-9_){4,20}$")) {
throw new IllegalArgumentException("Invalid username");
}
- 遵循最小权限原则:应用程序使用的数据库账号应仅拥有必要的权限,如 SELECT、INSERT、UPDATE 等,禁止 DROP、GRANT 等高危操作。同时,禁用数据库中敏感函数,如 MySQL 的 LOAD_FILE、INTO OUTFILE 等。
- 避免动态拼接 SQL:在动态表名、列名等无法通过 PreparedStatement 参数化的场景中,要使用白名单校验合法值。例如:
Set<String> validTables = Set.of("users", "products");
if (!validTables.contains(tableName)) {
throw new IllegalArgumentException("Invalid table name");
}
String sql = "SELECT * FROM " + tableName;
- 日志监控与异常处理:记录详细的 SQL 执行日志,以便及时发现异常的 SQL 操作。在生产环境中,捕获 SQL 异常时应返回通用错误信息,避免泄露数据库细节。例如:
try {
// 执行数据库操作
} catch (SQLException e) {
logger.error("Database error", e);
throw new RuntimeException("Internal server error");
}
- 使用安全工具:可以使用 OWASP ESAPI 等安全编码工具,提供安全的 SQL 转义方法。同时,集成 sqlmap 或 SonarQube 等 SQL 注入扫描工具进行代码审计,及时发现潜在的 SQL 注入漏洞。
- Web 应用防火墙(WAF):部署 WAF 可以拦截恶意的 SQL 输入。例如,使用 Kubernetes 部署 WAF,并配置规则如 SecRule ARGS “@contains ' OR '” “id:1000,deny,log,status:403,msg:'SQL Injection Attempt'” 来检测和阻止包含特定恶意字符的请求。