java-security-issues

Java 专项安全问题分类

最后更新:2026-04-17

概述

本文档整理 Java 生态特有的安全问题,这些问题的根源在于 Java 语言特性、JVM 机制或 Java 框架组件。


一、注入类漏洞

1.1 SQL 注入

类型 说明 Java 相关
JDBC 拼接 字符串拼接 SQL Statement.executeQuery()
MyBatis 动态 SQL ${} 拼接 select * from users where name = '${name}'
JPA/JPQL 字符串拼接 createQuery("... where name = '" + name + "'")
HQL 注入 Hibernate 查询语言 类似 SQL 注入

安全编码

// MyBatis 安全用法
select * from users where name = #{name}

// JPA 安全用法
TypedQuery<User> query = em.createQuery("SELECT u FROM User u WHERE u.name = :name", User.class);
query.setParameter("name", name);

1.2 XSS (Cross-Site Scripting)

类型 说明 Java 相关
反射型 XSS 参数直接输出 JSP ${param.xxx}
存储型 XSS 存储后输出 富文本编辑器内容
DOM型 XSS 前端渲染 后端返回 JSON 未转义

安全编码

<!-- JSP 安全输出 -->
<c:out value="${userInput}" />
${fn:escapeXml(userInput)}

1.3 命令注入

// 漏洞代码
Runtime.getRuntime().exec("ping " + ip);

// 安全代码:使用数组形式
Runtime.getRuntime().exec(new String[]{"ping", ip});

1.4 表达式注入

表达式引擎 注入方式 影响
SpEL T(java.lang.Runtime) Spring 应用
OGNL Struts2 框架 RCE
EL JSP 表达式语言 信息泄露
MVEL 规则引擎 RCE

示例

// SpEL 漏洞
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(userInput); // 危险

// 安全做法:禁用危险功能
SpelParserConfiguration config = new SpelParserConfiguration(false, false);

1.5 JNDI 注入

// 漏洞代码:用户可控的 JNDI 查找
Context ctx = new InitialContext();
Object obj = ctx.lookup(userInput); // LDAP/RMI 注入

// 安全代码:白名单校验
if (!ALLOWED_JNDI_NAMES.contains(userInput)) {
    throw new SecurityException("Invalid JNDI name");
}

二、文件操作类漏洞

2.1 路径遍历

// 漏洞代码
String filename = request.getParameter("file");
File file = new File("/var/www/files/" + filename);
FileInputStream fis = new FileInputStream(file);

// 安全代码:规范化和校验
Path basePath = Paths.get("/var/www/files/").normalize().toAbsolutePath();
Path filePath = basePath.resolve(filename).normalize().toAbsolutePath();
if (!filePath.startsWith(basePath)) {
    throw new SecurityException("Path traversal detected");
}

2.2 任意文件上传

// 漏洞代码:无校验
MultipartFile file = request.getFile("file");
file.transferTo(new File("/uploads/" + file.getOriginalFilename()));

// 安全代码:白名单校验
String filename = file.getOriginalFilename();
String ext = FilenameUtils.getExtension(filename).toLowerCase();
if (!ALLOWED_EXTENSIONS.contains(ext)) {
    throw new SecurityException("Invalid file type");
}
// 重命名文件,不使用原始文件名
String safeName = UUID.randomUUID() + "." + ext;
file.transferTo(new File("/uploads/" + safeName));

三、反序列化漏洞

3.1 Java 原生反序列化

// 漏洞代码
ObjectInputStream ois = new ObjectInputStream(input);
Object obj = ois.readObject();

// 安全代码:白名单过滤
ObjectInputStream ois = new ObjectInputStream(input) {
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        if (!ALLOWED_CLASSES.contains(desc.getName())) {
            throw new InvalidClassException("Unauthorized deserialization", desc.getName());
        }
        return super.resolveClass(desc);
    }
};

3.2 Fastjson 反序列化

// 漏洞配置:开启 AutoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

// 安全配置:关闭 AutoType,使用白名单
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
ParserConfig.getGlobalInstance().addAccept("com.example.");

3.3 Jackson 反序列化

// 漏洞配置:开启默认类型
objectMapper.enableDefaultTyping();

// 安全配置:禁用默认类型
objectMapper.disableDefaultTyping();
// 或使用白名单
objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);

3.4 反序列化漏洞影响范围

库/组件 漏洞入口 攻击方式
Java 原生 ObjectInputStream ysoserial gadget chain
Fastjson JSON.parseObject AutoType 触发恶意类
Jackson ObjectMapper.readValue enableDefaultTyping
Hessian HessianInput.readObject gadget chain
XStream XStream.fromXML 任意对象实例化
SnakeYAML Yaml.load 任意类实例化
Kryo Kryo.readClassAndObject 任意类实例化

四、框架组件漏洞

4.1 Spring 框架

漏洞 CVE 影响
Spring4Shell CVE-2022-22965 JDK9+ RCE
Spring Cloud Function SpEL CVE-2022-22963 RCE
Spring Cloud Gateway CVE-2022-22947 RCE
Spring Data MongoDB CVE-2022-22980 SpEL RCE

4.2 Struts2 框架

漏洞编号 类型 影响
S2-045 OGNL 注入 RCE
S2-046 OGNL 注入 RCE
S2-048 OGNL 注入 RCE
S2-057 OGNL 注入 RCE

4.3 Apache Shiro

漏洞 CVE 影响
RememberMe 反序列化 CVE-2016-4437 RCE
权限绕过 CVE-2020-1957 授权绕过
认证绕过 CVE-2022-32532 认证绕过

4.4 Log4j2

漏洞 CVE 影响
Log4Shell CVE-2021-44228 JNDI 注入 RCE
DoS 攻击 CVE-2021-45046 拒绝服务

五、加密安全类

5.1 弱加密算法

算法类型 不安全算法 安全替代
哈希 MD5、SHA1 SHA-256、SHA-3
对称加密 DES、3DES、Blowfish AES-256-GCM
非对称加密 RSA-1024 RSA-2048、ECC
密码存储 明文、MD5 BCrypt、Argon2

5.2 密码安全存储

// 漏洞代码:MD5 存储密码
String hashed = DigestUtils.md5Hex(password);

// 安全代码:BCrypt
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));

5.3 随机数安全

// 漏洞代码:不安全的随机数
Random random = new Random();
String token = String.valueOf(random.nextInt());

// 安全代码:安全随机数
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[32];
secureRandom.nextBytes(token);

六、配置安全类

6.1 常见配置问题

问题 风险 修复
硬编码密钥 密钥泄露 使用密钥管理服务
默认账户 未授权访问 删除/修改默认账户
调试模式开启 信息泄露 生产环境关闭
错误堆栈暴露 信息泄露 自定义错误页面
目录列表 信息泄露 关闭目录列表

6.2 敏感配置加密

# 漏洞配置:明文存储密码
spring.datasource.password=admin123

# 安全配置:使用 Jasypt 加密
spring.datasource.password=ENC(加密后的密码)

七、业务逻辑漏洞

7.1 越权操作

// 漏洞代码:未校验订单归属
@PostMapping("/order/{id}/cancel")
public void cancelOrder(@PathVariable Long id) {
    orderService.cancel(id);
}

// 安全代码:校验订单归属
@PostMapping("/order/{id}/cancel")
public void cancelOrder(@PathVariable Long id, Principal principal) {
    Order order = orderService.findById(id);
    if (!order.getUserId().equals(principal.getName())) {
        throw new AccessDeniedException("无权操作此订单");
    }
    orderService.cancel(id);
}

7.2 并发竞争

// 漏洞代码:无锁操作
@PostMapping("/coupon/{code}/claim")
public void claimCoupon(@PathVariable String code) {
    Coupon coupon = couponService.findByCode(code);
    if (coupon.getRemaining() > 0) {
        couponService.claim(coupon);
    }
}

// 安全代码:使用乐观锁或分布式锁
@PostMapping("/coupon/{code}/claim")
@Transactional
public void claimCoupon(@PathVariable String code) {
    Coupon coupon = couponService.findByCodeWithLock(code);
    if (coupon.getRemaining() > 0) {
        couponService.claim(coupon);
    }
}

八、资源消耗类

8.1 ReDoS(正则表达式拒绝服务)

// 漏洞代码:回溯爆炸的正则
Pattern pattern = Pattern.compile("^(a+)+$");

// 安全代码:避免嵌套量词,限制输入长度
Pattern pattern = Pattern.compile("^[a]+$");
if (input.length() > 100) {
    throw new IllegalArgumentException("Input too long");
}

8.2 OOM(内存溢出攻击)

// 漏洞代码:无限制的文件上传大小
@PostMapping("/upload")
public void upload(MultipartFile file) {
    byte[] bytes = file.getBytes(); // 可能 OOM
}

// 安全代码:限制大小
@PostMapping("/upload")
public void upload(MultipartFile file) {
    if (file.getSize() > MAX_FILE_SIZE) {
        throw new IllegalArgumentException("File too large");
    }
}

参考资料