某在线监控信息管理平台-java代审
字数 1618 2025-08-23 18:31:24
Java代码审计实战:某在线监控信息管理平台漏洞分析
1. 前言
本文基于某在线监控信息管理平台的Java代码审计实践,详细分析发现的多个安全漏洞,包括权限绕过、任意文件下载、文件包含与上传、未授权密码修改等问题,并提供相应的修复建议。
2. 漏洞详细分析
2.1 Spring方法拦截缺失
漏洞描述:
系统虽然实现了权限校验机制,但对login.jsp中的特定方法未进行拦截,导致权限绕过风险。
代码表现:
// 错误示例:Spring Security配置中遗漏了对login.jsp相关方法的拦截
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
// 遗漏了对/login.jsp相关路径的权限控制
.anyRequest().authenticated();
}
修复建议:
- 确保Spring Security配置中明确拦截所有敏感路径
- 对
login.jsp相关方法添加适当的权限控制
// 正确配置示例
http.authorizeRequests()
.antMatchers("/login.jsp/**").authenticated()
// 其他配置...
2.2 任意文件下载漏洞
漏洞描述:
login.jsp中存在文件下载功能,未对文件路径进行严格校验,导致攻击者可下载服务器任意文件。
漏洞代码:
// 危险的文件下载实现
@RequestMapping("/download")
public void downloadFile(@RequestParam String filePath, HttpServletResponse response) {
File file = new File(filePath); // 直接使用用户输入作为文件路径
// ...文件下载逻辑...
}
利用方式:
攻击者可通过构造恶意请求下载系统敏感文件:
/download?filePath=/etc/passwd
/download?filePath=C:/Windows/System32/drivers/etc/hosts
修复建议:
- 白名单校验允许下载的文件类型和路径
- 对用户输入进行规范化处理
- 使用文件ID代替直接路径
// 安全实现示例
private static final String SAFE_DIR = "/var/safe_downloads/";
@RequestMapping("/download")
public void downloadFile(@RequestParam String fileId, HttpServletResponse response) {
// 验证文件ID格式
if(!fileId.matches("[a-zA-Z0-9_-]+")) {
throw new IllegalArgumentException("Invalid file ID");
}
Path safePath = Paths.get(SAFE_DIR, fileId + ".pdf");
// 检查路径是否在安全目录内
if(!safePath.normalize().startsWith(SAFE_DIR)) {
throw new SecurityException("Invalid file path");
}
// ...安全下载逻辑...
}
2.3 任意文件包含与上传漏洞
漏洞描述:
系统存在文件包含和文件上传功能,且未对文件类型、内容进行充分校验。
文件上传漏洞代码:
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
String fileName = file.getOriginalFilename();
File dest = new File("/uploads/" + fileName); // 直接使用原始文件名
file.transferTo(dest); // 未校验文件内容
return "上传成功";
}
文件包含漏洞代码:
<%@ include file="<%= request.getParameter("template") %>" %>
利用方式:
- 上传WebShell(如JSP恶意文件)
- 包含服务器敏感文件或上传的恶意文件
修复建议:
- 文件上传:
- 校验文件扩展名和MIME类型
- 重命名上传文件
- 存储到Web根目录外
- 扫描文件内容
// 安全上传示例
private static final Set<String> ALLOWED_TYPES = Set.of("image/jpeg", "image/png");
private static final Pattern FILE_NAME_PATTERN = Pattern.compile("^[a-z0-9-]+\\.(jpg|png)$");
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
// 校验内容类型
if(!ALLOWED_TYPES.contains(file.getContentType())) {
throw new IllegalArgumentException("Invalid file type");
}
// 校验文件名
String fileName = file.getOriginalFilename().toLowerCase();
if(!FILE_NAME_PATTERN.matcher(fileName).matches()) {
throw new IllegalArgumentException("Invalid file name");
}
// 安全存储
String safeName = UUID.randomUUID() + "." + FilenameUtils.getExtension(fileName);
Path dest = Paths.get("/var/uploads/", safeName);
Files.copy(file.getInputStream(), dest, StandardCopyOption.REPLACE_EXISTING);
return "上传成功";
}
- 文件包含:
- 避免动态包含
- 如需动态包含,使用白名单机制
2.4 未授权任意密码修改
漏洞描述:
login.jsp中存在密码修改功能,未进行充分授权验证,且使用可预测的userId参数。
漏洞代码:
@PostMapping("/updatePassword")
public String updatePassword(@RequestParam String userId,
@RequestParam String newPassword) {
// 直接修改密码,未验证当前用户权限
userService.updatePassword(userId, newPassword);
return "密码修改成功";
}
风险:
攻击者可枚举userId修改任意用户密码,但由于无法直接关联userId与用户名,利用难度中等。
修复建议:
- 强制验证当前用户身份
- 要求提供原密码
- 使用会话中的用户ID而非请求参数
@PostMapping("/updatePassword")
public String updatePassword(@RequestParam String currentPassword,
@RequestParam String newPassword,
Principal principal) {
// 从认证信息中获取用户ID
String username = principal.getName();
User user = userService.findByUsername(username);
// 验证当前密码
if(!passwordEncoder.matches(currentPassword, user.getPassword())) {
throw new AccessDeniedException("Current password incorrect");
}
// 更新密码
userService.updatePassword(user.getId(), passwordEncoder.encode(newPassword));
return "密码修改成功";
}
2.5 默认口令问题
漏洞描述:
系统为新增用户设置默认口令XXX,虽然登录后强制要求修改为强口令,但仍存在初始风险。
代码表现:
public User createNewUser(User user) {
// 设置默认密码
user.setPassword("XXX"); // 硬编码默认密码
user.setForcePasswordChange(true); // 强制修改密码
return userRepository.save(user);
}
风险:
- 管理员可能忘记通知用户修改密码
- 批量创建用户时存在初始风险
修复建议:
- 为每个用户生成随机初始密码
- 通过安全渠道(如邮件)单独发送初始密码
- 设置短期有效的初始密码
public User createNewUser(User user) {
// 生成随机密码
String tempPassword = generateRandomPassword();
user.setPassword(passwordEncoder.encode(tempPassword));
user.setForcePasswordChange(true);
// 发送邮件通知用户
emailService.sendTempPassword(user.getEmail(), tempPassword);
return userRepository.save(user);
}
private String generateRandomPassword() {
// 使用安全的随机数生成器
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[12];
random.nextBytes(bytes);
return Base64.getEncoder().encodeToString(bytes);
}
2.6 敏感信息泄露
漏洞描述:
代码中存在邮箱等敏感信息硬编码或不当记录问题。
代码表现:
// 敏感信息硬编码
private static final String ADMIN_EMAIL = "admin@example.com";
// 日志记录敏感信息
logger.debug("Sending email to " + user.getEmail() + " with content: " + emailContent);
风险:
- 源代码泄露导致敏感信息暴露
- 日志文件可能被未授权访问
修复建议:
- 将敏感信息移至配置文件或环境变量
- 避免在日志中记录敏感信息
- 对日志中的敏感信息进行脱敏
// 从配置读取敏感信息
@Value("${admin.email}")
private String adminEmail;
// 安全的日志记录
logger.debug("Sending email to {}", maskEmail(user.getEmail()));
private String maskEmail(String email) {
if(email == null) return null;
int atIndex = email.indexOf('@');
if(atIndex <= 2) return "***" + email.substring(atIndex);
return email.substring(0, 2) + "***" + email.substring(atIndex);
}
3. 综合修复方案
-
权限控制:
- 实现完整的RBAC模型
- 默认拒绝所有请求,显式允许必要请求
- 对管理接口添加
@PreAuthorize注解
-
输入验证:
- 所有用户输入视为不可信
- 实施白名单验证策略
- 对文件操作进行路径规范化检查
-
安全配置:
- 禁用JSP动态包含功能
- 配置安全文件上传目录
- 实现内容安全策略(CSP)
-
密码安全:
- 废除所有默认密码
- 实现密码强度策略
- 添加密码修改的二次认证
-
敏感信息保护:
- 代码扫描移除硬编码凭证
- 实现日志脱敏过滤器
- 加密存储敏感数据
4. 审计方法论总结
-
入口点分析:
- 重点检查Controller层和JSP文件
- 特别关注
login.jsp等认证相关页面
-
数据流追踪:
- 从用户输入点到敏感操作跟踪数据流
- 检查所有未经验证的用户输入使用点
-
权限验证检查:
- 验证每个功能点的权限控制
- 测试垂直和水平权限提升可能
-
安全配置审查:
- 检查Spring Security配置
- 验证文件处理相关配置
-
敏感操作审计:
- 重点审计文件操作、用户管理、密码修改等功能
- 检查所有包含动态包含或重定向的代码
通过系统性的代码审计,可以有效识别和修复此类Web应用中的安全漏洞,提升系统整体安全性。