Java 代码安全案例
以下是一些常见的Java安全漏洞案例及其修复方法,帮助你理解和防范安全风险。

SQL注入
❌ 不安全代码
public User getUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// ...
}
✅ 安全修复(使用PreparedStatement)
public User getUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
// ...
}
跨站脚本攻击 (XSS)
❌ 不安全代码
@GetMapping("/search")
public String search(HttpServletRequest request, Model model) {
String query = request.getParameter("q");
model.addAttribute("result", "搜索: " + query);
return "searchResults";
}
✅ 安全修复(使用HTML编码)
@GetMapping("/search")
public String search(HttpServletRequest request, Model model) {
String query = request.getParameter("q");
String encodedQuery = StringEscapeUtils.escapeHtml4(query);
model.addAttribute("result", "搜索: " + encodedQuery);
return "searchResults";
}
反序列化漏洞
❌ 不安全代码
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
User user = (User) ois.readObject(); // 危险!
// 处理用户对象
}
✅ 安全修复(使用白名单校验)
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = ois.readObject();
// 白名单校验
if (!(obj instanceof User)) {
throw new SecurityException("非法的反序列化对象");
}
User user = (User) obj;
// 或使用 ObjectInputFilter
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"maxdepth=10;java.lang.*;com.example.model.*;!*"
);
ois.setObjectInputFilter(filter);
User user = (User) ois.readObject();
} catch (ClassNotFoundException e) {
throw new ServletException("类未找到", e);
}
}
硬编码敏感信息
❌ 不安全代码
public class DatabaseConfig {
private static final String DB_PASSWORD = "admin123";
private static final String API_KEY = "sk_test_xxxxxxxx";
public Connection getConnection() {
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/app",
"root",
DB_PASSWORD
);
}
}
✅ 安全修复(使用环境变量)
public class DatabaseConfig {
private String getPassword() {
String pass = System.getenv("DB_PASSWORD");
if (pass == null || pass.isEmpty()) {
throw new IllegalStateException("DB_PASSWORD 环境变量未设置");
}
return pass;
}
public Connection getConnection() {
return DriverManager.getConnection(
System.getenv("DB_URL"),
System.getenv("DB_USERNAME"),
getPassword()
);
}
}
文件上传漏洞
❌ 不安全代码
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
String originalFilename = file.getOriginalFilename();
String path = "/uploads/" + originalFilename;
file.transferTo(new File(path)); // 路径穿越漏洞!
return "上传成功";
}
✅ 安全修复
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
// 1. 验证文件类型
if (!isAllowedFileType(file.getContentType())) {
return "不允许的文件类型";
}
// 2. 验证文件大小
if (file.getSize() > MAX_FILE_SIZE) {
return "文件大小超出限制";
}
// 3. 使用随机文件名和确认的路径
String originalFilename = file.getOriginalFilename();
String extension = getFileExtension(originalFilename);
String randomFilename = UUID.randomUUID().toString() + extension;
Path uploadPath = Paths.get(UPLOAD_DIR, randomFilename).normalize();
// 确保路径在目标目录内
if (!uploadPath.startsWith(UPLOAD_DIR)) {
throw new SecurityException("无效的上传路径");
}
file.transferTo(uploadPath);
return "上传成功";
}
不安全的随机数生成
❌ 不安全代码
public String generatePasswordResetToken() {
return Integer.toString(new Random().nextInt(999999)); // 可预测!
}
✅ 安全修复
public String generatePasswordResetToken() {
SecureRandom secureRandom = new SecureRandom();
byte[] tokenBytes = new byte[32];
secureRandom.nextBytes(tokenBytes);
StringBuilder tokenBuilder = new StringBuilder();
for (byte b : tokenBytes) {
tokenBuilder.append(String.format("%02x", b));
}
return tokenBuilder.toString();
}
日志注入
❌ 不安全代码
public void login(String username) {
logger.info("用户登录: " + username); // 日志注入风险
// 登录逻辑...
}
✅ 安全修复
public void login(String username) {
String sanitizedUsername = username.replaceAll("[\r\n]", "");
logger.info("用户登录: {}", sanitizedUsername);
// 登录逻辑...
}
CSRF 防护(Spring Security配置)
✅ 安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().enable()
.and()
.authorizeRequests()
.antMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.and()
.headers()
.contentSecurityPolicy("script-src 'self'");
}
}
敏感数据泄露(错误处理)
❌ 不安全代码
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
try {
return userService.findById(id);
} catch (Exception e) {
return new ErrorResponse("数据库错误: " + e.getMessage());
}
}
}
✅ 安全修复
@RestController
public class UserController {
@GetMapping("/user/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
try {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
} catch (Exception e) {
// 记录完整错误日志
logger.error("获取用户失败", e);
// 返回通用错误信息
return ResponseEntity.status(500)
.body(new ErrorResponse("服务器内部错误"));
}
}
}
不安全的直接对象引用(IDOR)
❌ 不安全代码
@GetMapping("/order/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
return orderRepository.findById(orderId).get();
}
✅ 安全修复
@GetMapping("/order/{orderId}")
public ResponseEntity<?> getOrder(@PathVariable Long orderId) {
User currentUser = getCurrentUser(); // 从SecurityContext获取
Optional<Order> order = orderRepository.findById(orderId);
// 验证订单是否属于当前用户
if (order.isPresent() && order.get().getUserId().equals(currentUser.getId())) {
return ResponseEntity.ok(order.get());
}
return ResponseEntity.status(403).build();
}
安全实践建议
- 定期更新依赖 - 及时更新 Java 和相关库到最新版本
- 输入验证 - 对所有用户输入进行严格验证
- 使用安全编码框架 - 利用 Spring Security 等成熟安全框架
- 代码审计 - 使用 SonarQube 等工具进行静态代码分析
- 安全培训和最佳实践 - 团队定期进行安全培训
工具推荐
- 静态分析: SonarQube, FindBugs/SpotBugs, PMD
- 依赖检查: OWASP Dependency-Check, Snyk
- 运行时防护: Contrast Security, RASP
- 渗透测试: OWASP ZAP, Burp Suite
这些案例展示了常见的Java安全漏洞和相应的最佳实践,在实际开发中,应该始终将安全性作为首要考虑因素。