--- paths: - "**/*.java" --- # Java 安全 > 本文档在 [common/security.md](../common/security.md) 的基础上,补充了 Java 相关的内容。 ## 密钥管理 * 切勿在源代码中硬编码 API 密钥、令牌或凭据 * 使用环境变量:`System.getenv("API_KEY")` * 生产环境密钥请使用密钥管理器(如 Vault、AWS Secrets Manager) * 包含密钥的本地配置文件应放在 `.gitignore` 中 ```java // BAD private static final String API_KEY = "sk-abc123..."; // GOOD — environment variable String apiKey = System.getenv("PAYMENT_API_KEY"); Objects.requireNonNull(apiKey, "PAYMENT_API_KEY must be set"); ``` ## SQL 注入防护 * 始终使用参数化查询——切勿将用户输入拼接到 SQL 语句中 * 使用 `PreparedStatement` 或你所使用框架的参数化查询 API * 对用于原生查询的任何输入进行验证和清理 ```java // BAD — SQL injection via string concatenation Statement stmt = conn.createStatement(); String sql = "SELECT * FROM orders WHERE name = '" + name + "'"; stmt.executeQuery(sql); // GOOD — PreparedStatement with parameterized query PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE name = ?"); ps.setString(1, name); // GOOD — JDBC template jdbcTemplate.query("SELECT * FROM orders WHERE name = ?", mapper, name); ``` ## 输入验证 * 在处理前,于系统边界处验证所有用户输入 * 使用验证框架时,在 DTO 上使用 Bean 验证(`@NotNull`, `@NotBlank`, `@Size`) * 在使用文件路径和用户提供的字符串前,对其进行清理 * 对于验证失败的输入,应拒绝并提供清晰的错误信息 ```java // Validate manually in plain Java public Order createOrder(String customerName, BigDecimal amount) { if (customerName == null || customerName.isBlank()) { throw new IllegalArgumentException("Customer name is required"); } if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("Amount must be positive"); } return new Order(customerName, amount); } ``` ## 认证与授权 * 切勿自行实现认证加密逻辑——请使用成熟的库 * 使用 bcrypt 或 Argon2 存储密码,切勿使用 MD5/SHA1 * 在服务边界强制执行授权检查 * 清理日志中的敏感数据——切勿记录密码、令牌或个人身份信息 ## 依赖项安全 * 运行 `mvn dependency:tree` 或 `./gradlew dependencies` 来审计传递依赖项 * 使用 OWASP Dependency-Check 或 Snyk 扫描已知的 CVE * 保持依赖项更新——设置 Dependabot 或 Renovate ## 错误信息 * 切勿在 API 响应中暴露堆栈跟踪、内部路径或 SQL 错误 * 在处理器边界将异常映射为安全、通用的客户端消息 * 在服务器端记录详细错误;向客户端返回通用消息 ```java // Log the detail, return a generic message try { return orderService.findById(id); } catch (OrderNotFoundException ex) { log.warn("Order not found: id={}", id); return ApiResponse.error("Resource not found"); // generic, no internals } catch (Exception ex) { log.error("Unexpected error processing order id={}", id, ex); return ApiResponse.error("Internal server error"); // never expose ex.getMessage() } ``` ## 参考 关于 Spring Security 认证与授权模式,请参见技能:`springboot-security`。 关于通用安全检查清单,请参见技能:`security-review`。