Java代码安全案例

wen java案例 2

Java 代码安全案例

以下是一些常见的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();
}

安全实践建议

  1. 定期更新依赖 - 及时更新 Java 和相关库到最新版本
  2. 输入验证 - 对所有用户输入进行严格验证
  3. 使用安全编码框架 - 利用 Spring Security 等成熟安全框架
  4. 代码审计 - 使用 SonarQube 等工具进行静态代码分析
  5. 安全培训和最佳实践 - 团队定期进行安全培训

工具推荐

  • 静态分析: SonarQube, FindBugs/SpotBugs, PMD
  • 依赖检查: OWASP Dependency-Check, Snyk
  • 运行时防护: Contrast Security, RASP
  • 渗透测试: OWASP ZAP, Burp Suite

这些案例展示了常见的Java安全漏洞和相应的最佳实践,在实际开发中,应该始终将安全性作为首要考虑因素。

抱歉,评论功能暂时关闭!