Java安全编码实践总结
字数 991 2025-08-15 21:31:19

Java安全编码实践指南

1. SQL注入防范

1.1 预编译技术

适用场景:大多数对数据库进行操作的场景

实现方式

  • JDBC:

    String sql = "SELECT * FROM users WHERE username = ?";
    PreparedStatement stmt = connection.prepareStatement(sql);
    stmt.setString(1, username);
    
  • Hibernate:

    Query query = session.createQuery("FROM User WHERE username = :username");
    query.setParameter("username", username);
    
  • MyBatis:

    <select id="getUser" resultType="User">
      SELECT * FROM users WHERE username = #{username}
    </select>
    

1.2 无法使用预编译的场景

场景:涉及表名、字段名的动态查询(如ORDER BY、LIMIT、GROUP BY等)

解决方案:白名单验证

// 定义允许的排序字段白名单
private static final Set<String> ALLOWED_SORT_FIELDS = new HashSet<>(Arrays.asList("id", "name", "create_time"));

public List<User> getUsers(String sortField) {
    if (!ALLOWED_SORT_FIELDS.contains(sortField)) {
        throw new IllegalArgumentException("Invalid sort field");
    }
    String sql = "SELECT * FROM users ORDER BY " + sortField;
    // 执行查询...
}

2. NoSQL注入防范

MongoDB注入防范

错误写法(拼接用户查询条件):

// 错误示例
BasicDBObject query = new BasicDBObject("$where", "this.username == '" + username + "'");

正确写法(参数绑定):

// 正确示例
BasicDBObject query = new BasicDBObject("username", username);

3. XSS防范

3.1 白名单校验

适用场景:纯数字、纯文本等简单输入

// 只允许字母数字
if (!username.matches("^[a-zA-Z0-9]+$")) {
    throw new IllegalArgumentException("Invalid username");
}

3.2 ESAPI处理

适用场景:常规输入输出,如用户评论

import org.owasp.esapi.ESAPI;

// 过滤XSS
String safeInput = ESAPI.encoder().encodeForHTML(userInput);

4. XXE注入防范

4.1 禁用外部实体

XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
xmlReader.parse(new InputSource(new StringReader(body)));

4.2 禁用DOCTYPE声明(更严格)

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

5. 文件上传漏洞防范

5.1 安全上传实践

// 限制文件类型
private static final Set<String> ALLOWED_TYPES = Set.of("image/jpeg", "image/png");

// 生成随机文件名
String fileName = UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(originalFilename);

// 限制文件大小
@Bean
public MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    factory.setMaxFileSize("10MB");
    factory.setMaxRequestSize("10MB");
    return factory.createMultipartConfig();
}

6. 文件包含防范

6.1 安全实践

// 通过ID映射真实路径
public String getFile(String fileId) {
    if (SecurityUtil.checkId(fileId) == null) {
        return "资源文件不存在!";
    }
    return get_file(SecurityUtil.find_path(fileId));
}

7. CSRF防范

7.1 Spring Security配置

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

8. 越权防范

8.1 垂直越权

// 检查用户权限
public void sensitiveOperation(User user) {
    if (!user.getRoles().contains("ADMIN")) {
        throw new AccessDeniedException("权限不足");
    }
    // 执行操作...
}

8.2 水平越权

// 检查数据所有权
public User getUserDetails(Long userId, Long currentUserId) {
    User user = userRepository.findById(userId);
    if (!user.getId().equals(currentUserId)) {
        throw new AccessDeniedException("无权访问此数据");
    }
    return user;
}

9. URL重定向与SSRF防范

9.1 URL重定向安全

// 白名单验证
private static final Set<String> ALLOWED_DOMAINS = Set.of("example.com", "trusted.org");

public String redirect(String url) {
    try {
        URI uri = new URI(url);
        if (!ALLOWED_DOMAINS.contains(uri.getHost())) {
            return "warning"; // 给用户风险提示
        }
        return "redirect:" + url;
    } catch (URISyntaxException e) {
        return "error";
    }
}

9.2 SSRF防范

// 限制协议和域名
private static final Set<String> ALLOWED_PROTOCOLS = Set.of("http", "https");
private static final Set<String> ALLOWED_HOSTS = Set.of("api.example.com");

public void makeRequest(String url) {
    try {
        URI uri = new URI(url);
        if (!ALLOWED_PROTOCOLS.contains(uri.getScheme()) || 
            !ALLOWED_HOSTS.contains(uri.getHost())) {
            throw new IllegalArgumentException("非法请求");
        }
        // 执行请求...
    } catch (URISyntaxException e) {
        throw new IllegalArgumentException("非法URL");
    }
}

10. 拒绝服务防范

10.1 正则表达式DoS防范

// 设置超时
Pattern pattern = Pattern.compile("(a+)+$");
Matcher matcher = pattern.matcher(input);
long startTime = System.currentTimeMillis();

while (matcher.find()) {
    if (System.currentTimeMillis() - startTime > 2000) {
        throw new RuntimeException("匹配超时");
    }
    // 处理匹配结果...
}

11. 加密安全

11.1 安全加密模式

错误写法(使用ECB模式):

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

正确写法(使用CBC模式):

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

12. 随机数安全

12.1 安全随机数

错误写法(使用伪随机):

Random random = new Random(seed); // 不安全

正确写法(使用SecureRandom):

SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);

13. 条件竞争防范

13.1 线程同步

private final Object lock = new Object();

public void checkIn(User user) {
    synchronized (lock) {
        if (user.hasCheckedInToday()) {
            throw new IllegalStateException("今日已签到");
        }
        user.addPoints(1);
        user.setLastCheckIn(LocalDate.now());
    }
}

14. 日志伪造防范

14.1 安全日志记录

public void logUserAction(String username) {
    // 过滤换行和特殊字符
    Pattern p = Pattern.compile("\\s*|\t|\r|\n");
    Matcher m = p.matcher(username);
    String safeUsername = m.replaceAll("");
    logger.info("User action by: " + safeUsername);
}

15. 动态代码执行安全

15.1 命令执行安全

错误写法

Runtime.getRuntime().exec(userInput);

正确写法

// 1. 输入净化
String sanitized = userInput.replaceAll("[^a-zA-Z0-9]", "");

// 2. 命令映射
if (!"allowedCommand".equals(sanitized)) {
    throw new IllegalArgumentException("非法命令");
}

// 3. 使用系统API替代
String userName = System.getProperty("user.name");

16. 反序列化安全

16.1 安全反序列化

错误写法

ObjectInputStream in = new ObjectInputStream(inputStream);
Object obj = in.readObject();

正确写法(使用SerialKiller):

try {
    ObjectInputStream in = new SerialKiller(inputStream, "serialkiller.conf");
    Object obj = in.readObject();
} catch (InvalidClassException e) {
    logger.warn("尝试反序列化非法类");
}

17. 表达式注入防范

17.1 SpEL表达式安全

错误写法

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
parser.parseExpression("Name = " + userInput).getValue(context, object);

正确写法

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression("Name = " + userInput).getValue(context, object);

18. Spring Boot安全配置

18.1 关键安全配置

# 禁用shutdown端点
endpoints.shutdown.enabled=false

# 禁用开发工具
spring.devtools.restart.enabled=false

# 禁用管理功能
spring.application.admin.enabled=false

# 启用HTML转义
spring.mvc.contentnegotiation.favor-path-extension=false

# Actuator安全配置
management.security.enabled=true
management.endpoints.web.exposure.include=health,info
management.endpoints.jmx.exposure.exclude=*

总结

Java安全编码需要从多个层面进行防护:

  1. 输入验证:始终验证用户输入,使用白名单优于黑名单
  2. 安全配置:框架和服务器应进行安全配置
  3. 最小权限:遵循最小权限原则
  4. 安全API:优先使用安全API而非危险API
  5. 防御性编程:考虑边界条件和异常情况
  6. 持续更新:保持依赖库和框架的最新版本

通过实施这些安全编码实践,可以显著降低Java应用程序的安全风险。

Java安全编码实践指南 1. SQL注入防范 1.1 预编译技术 适用场景 :大多数对数据库进行操作的场景 实现方式 : JDBC: Hibernate: MyBatis: 1.2 无法使用预编译的场景 场景 :涉及表名、字段名的动态查询(如ORDER BY、LIMIT、GROUP BY等) 解决方案 :白名单验证 2. NoSQL注入防范 MongoDB注入防范 错误写法 (拼接用户查询条件): 正确写法 (参数绑定): 3. XSS防范 3.1 白名单校验 适用场景 :纯数字、纯文本等简单输入 3.2 ESAPI处理 适用场景 :常规输入输出,如用户评论 4. XXE注入防范 4.1 禁用外部实体 4.2 禁用DOCTYPE声明(更严格) 5. 文件上传漏洞防范 5.1 安全上传实践 6. 文件包含防范 6.1 安全实践 7. CSRF防范 7.1 Spring Security配置 8. 越权防范 8.1 垂直越权 8.2 水平越权 9. URL重定向与SSRF防范 9.1 URL重定向安全 9.2 SSRF防范 10. 拒绝服务防范 10.1 正则表达式DoS防范 11. 加密安全 11.1 安全加密模式 错误写法 (使用ECB模式): 正确写法 (使用CBC模式): 12. 随机数安全 12.1 安全随机数 错误写法 (使用伪随机): 正确写法 (使用SecureRandom): 13. 条件竞争防范 13.1 线程同步 14. 日志伪造防范 14.1 安全日志记录 15. 动态代码执行安全 15.1 命令执行安全 错误写法 : 正确写法 : 16. 反序列化安全 16.1 安全反序列化 错误写法 : 正确写法 (使用SerialKiller): 17. 表达式注入防范 17.1 SpEL表达式安全 错误写法 : 正确写法 : 18. Spring Boot安全配置 18.1 关键安全配置 总结 Java安全编码需要从多个层面进行防护: 输入验证:始终验证用户输入,使用白名单优于黑名单 安全配置:框架和服务器应进行安全配置 最小权限:遵循最小权限原则 安全API:优先使用安全API而非危险API 防御性编程:考虑边界条件和异常情况 持续更新:保持依赖库和框架的最新版本 通过实施这些安全编码实践,可以显著降低Java应用程序的安全风险。