1. 概述
本规范针对 Java/JVM 生态系统, 提供具体的安全编码规则和代码示例。内容涵盖 SQL 注入防护、XSS 防御、反序列化安全、密码学 API 使用和文件 I/O 安全五个关键领域。所有规则基于安全编码总则中的通用原则, 并结合 Java 语言特性给出可操作的编码指导。
java.security 包的增强) 可能不适用于旧版本。
2. SQL注入防护
2.1 使用 PreparedStatement
SQL注入仍然是 Web 应用中最常见的高危漏洞之一。Java 中防御 SQL 注入的首要规则是: 永远使用 PreparedStatement 进行参数化查询, 禁止字符串拼接构造 SQL。
错误示例 (存在SQL注入):
// 危险: 直接拼接用户输入
String query = "SELECT * FROM users WHERE username = '" + userInput + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
正确示例:
// 安全: 使用参数化查询
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, userInput);
ResultSet rs = pstmt.executeQuery();
2.2 ORM框架的安全使用
使用 Hibernate/JPA 时, 同样需要避免 HQL/JPQL 字符串拼接:
// 危险: HQL拼接
String hql = "FROM User WHERE name = '" + name + "'";
// 安全: 使用命名参数
String hql = "FROM User WHERE name = :name";
Query query = session.createQuery(hql);
query.setParameter("name", name);
3. XSS防御
3.1 输出编码
在将用户输入渲染到 HTML 页面时, 必须进行上下文感知的输出编码。推荐使用 OWASP Java Encoder 库:
import org.owasp.encoder.Encode;
// HTML Context
String safeHtml = Encode.forHtml(userInput);
// JavaScript Context
String safeJs = Encode.forJavaScript(userInput);
// URL Parameter Context
String safeUrl = Encode.forUriComponent(userInput);
3.2 Content Security Policy
在 Servlet Filter 或 Spring Interceptor 中配置 CSP 头:
response.setHeader("Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
th:utext (unescaped text) 时编码会被禁用, 应仅在确认内容安全时使用。
4. 反序列化安全
4.1 风险背景
Java 原生反序列化 (ObjectInputStream) 是已知的高危攻击面。攻击者可以构造恶意序列化数据, 在反序列化过程中触发任意代码执行 (Remote Code Execution)。Apache Commons Collections、Spring Framework 等常用库中均发现过可利用的 gadget chain。
4.2 防御策略
首选方案: 避免 Java 原生反序列化
- 使用 JSON (Jackson, Gson) 或 Protocol Buffers 替代 Java 原生序列化
- API 接口使用 JSON/XML 而非 Java 序列化格式
不得不使用时: 白名单过滤
// 使用 ObjectInputFilter (Java 9+)
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.myapp.dto.*;!*" // 仅允许指定包下的类
);
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);
4.3 依赖管理
定期更新依赖, 移除或升级已知存在反序列化漏洞的库版本。使用 SCA 工具 (如 OWASP Dependency-Check) 自动检测易受攻击的依赖。
5. 密码学API使用
5.1 对称加密
// 推荐: AES-256-GCM (提供加密 + 完整性保护)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
禁止使用的算法和模式:
DES,3DES- 密钥长度不足AES/ECB/*- ECB 模式不提供语义安全性AES/CBC/PKCS5Padding- 容易受到 Padding Oracle 攻击 (若未配合 HMAC 使用)
5.2 密码哈希
// 密码存储: 使用 bcrypt (推荐 Spring Security 的实现)
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hashed = encoder.encode(rawPassword);
boolean matches = encoder.matches(rawPassword, hashed);
5.3 安全随机数
// 安全场景必须使用 SecureRandom
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] token = new byte[32];
random.nextBytes(token);
// 禁止使用 java.util.Random 或 Math.random() 生成安全相关的随机值
6. 文件I/O安全
6.1 路径遍历防护
当文件路径包含用户输入时, 必须防止路径遍历攻击:
// 危险: 直接使用用户输入构造路径
File file = new File("/uploads/" + userFileName);
// 安全: 规范化路径后验证
Path basePath = Paths.get("/uploads").toRealPath();
Path targetPath = basePath.resolve(userFileName).normalize().toRealPath();
if (!targetPath.startsWith(basePath)) {
throw new SecurityException("Path traversal detected");
}
6.2 临时文件安全
// 使用安全的临时文件创建方式
Path tempFile = Files.createTempFile("prefix_", ".tmp");
// 设置仅所有者可读写
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
Files.setPosixFilePermissions(tempFile, perms);
6.3 资源释放
使用 try-with-resources 确保文件句柄和数据库连接等资源正确释放, 避免资源泄露导致的拒绝服务:
try (BufferedReader reader = Files.newBufferedReader(path)) {
// 处理文件内容
} // 自动关闭, 即使发生异常
7. 小结
Java 安全编码的核心在于: 参数化查询防 SQL 注入, 输出编码防 XSS, 避免原生反序列化, 使用标准密码学 API, 以及严格的文件路径验证。建议将这些规则集成到代码审查 Checklist 和 SAST 工具规则集中, 实现自动化的安全编码合规检查。