mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-31 06:03:29 +08:00
102 lines
3.4 KiB
Markdown
102 lines
3.4 KiB
Markdown
---
|
||
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`。
|