开放签电子签章系统审计
字数 1841 2025-10-01 14:05:44
电子签章系统安全审计教学:开放签漏洞分析与挖掘
1. 项目概述
开放签是一个电子签章平台,提供完整的电子签约解决方案,主要功能包括:
- 合同创建、签署、管理及验证
- 个人与企业实名认证
- 电子印章管理
- 合同模板配置
- 签署流程控制
系统入口:
- 网页版登录界面:需通过实际环境获取
- 登录后台:需通过实际环境获取
- 企业管理后台:需通过实际环境获取
- FOFA查询:可通过网络空间测绘平台搜索相关资产
2. 鉴权机制分析
2.1 技术栈识别
系统采用Shiro框架作为权限控制核心,配置文件位于项目配置文件中。
2.2 白名单配置
Shiro配置中存在多个白名单接口,需要重点关注:
- 检查白名单路由是否包含敏感信息或功能
- 分析是否有不必要的接口被暴露
2.3 JWT过滤器实现
系统注册了JwtFilter,拦截所有路由请求,关键实现逻辑:
public class JwtFilter extends AuthenticatingFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
// 自动调用executeLogin方法处理凭据
return executeLogin(request, response);
}
// 实际登录逻辑
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
// 从请求头或参数获取token
String token = getToken(request);
JwtToken jwtToken = new JwtToken(token);
// Shiro主题登录验证
getSubject(request, response).login(jwtToken);
return true;
}
}
2.4 认证流程分析
- Token获取:从请求头或参数中提取JWT token
- Realm处理:调用
ShiroRealm.doGetAuthenticationInfo()方法 - Token验证:
- 第126行:调用
checkUserTokenIsEffect校验token有效性 - 第145行:使用
JwtUtil.getUsername()解密token获取用户名 - 第152行:根据用户名查询数据库获取用户信息
- 第170行:使用
JwtUtil.verify()方法根据用户名和查询到的密码进行token校验
- 第126行:调用
2.5 JWT实现缺陷
// JWT验证逻辑问题
public boolean verify(String token, String username, String password) {
// 使用用户密码生成JWT密钥
String secret = generateSecret(password);
// 用此密钥验证token
// ...
}
安全问题:JWT密钥基于用户密码生成,这意味着攻击者如果已获取密码,可直接登录系统,降低了JWT的安全价值。
3. 任意文件上传漏洞
3.1 漏洞位置
文件:FileStorageController.java
方法:create()
3.2 漏洞分析
@PostMapping("/create")
public Result create(@RequestParam("file") MultipartFile file) {
// 获取文件原始后缀
String originalExtension = getOriginalExtension(file);
// 获取文件类型
String fileType = getFileType(file);
// 生成新文件名:UUID + 原始后缀
String newFileName = generateUUID() + originalExtension;
// 调用存储服务保存文件
iAnnexStorageService.create(file, newFileName);
return Result.ok().data("uid", newFileName);
}
3.3 存储实现分析
系统支持三种存储方式:
- 阿里云OSS存储
- 本地存储
- MinIO存储
本地存储实现(重点风险点):
public class LocalStorageService implements StorageService {
public void store(InputStream inputStream, String fileName) {
// 直接使用Files工具类写入文件
Files.copy(inputStream, Paths.get(storagePath, fileName));
}
}
存储路径:需查看配置文件确定具体存储位置
3.4 利用限制
- 上传后仅返回文件UID,不返回完整路径
- 需要结合其他漏洞获取文件访问路径
- 实际利用难度较高但仍存在风险
4. 用户信息泄露漏洞
4.1 权限控制机制
系统采用注解形式进行权限控制,未配置权限注解的接口可能存在越权访问风险。
4.2 未授权接口发现
漏洞位置1:SysUserController.java中的getAllUsers方法
// 缺少权限注解
@GetMapping("/all")
public Result<List<SysUser>> getAllUsers() {
// 使用MyBatis-Plus自动生成查询
List<SysUser> users = sysUserService.list();
return Result.ok().data(users);
}
泄露数据:用户ID、注册账号(手机号)
验证请求:
GET /sys/user/all
漏洞位置2:SysUserController.java中的userInfo方法
// 缺少权限注解
@GetMapping("/info/{id}")
public Result<SysUser> userInfo(@PathVariable String id) {
// 通过ID查询用户信息
SysUser user = sysUserService.getById(id);
return Result.ok().data(user);
}
泄露数据:完整用户信息
验证请求:
GET /sys/user/info/{userId}
4.3 攻击链构建
- 通过
/all接口获取所有用户ID和手机号 - 使用获取的ID调用
/info/{id}接口获取完整用户信息 - 收集敏感信息进行进一步攻击
5. 审计方法与技巧总结
5.1 鉴权审计要点
-
Shiro配置审计:
- 检查白名单接口合理性
- 验证自定义Filter的安全性
-
JWT实现审计:
- 检查密钥生成算法
- 验证token验证逻辑是否与用户敏感信息耦合
5.2 文件上传审计要点
- 文件后缀处理:是否仅使用原始后缀而未做安全过滤
- 存储方式:本地存储直接写入文件系统风险最高
- 回显信息:是否暴露文件存储路径
5.3 权限控制审计要点
- 注解缺失检查:查找未配置权限注解的接口
- 默认权限:检查新注册用户的默认权限分配
- 接口参数:验证基于ID的参数是否存在越权
5.4 自动化辅助工具
- 代码扫描:使用SAST工具搜索特定关键词(如
MultipartFile、@GetMapping) - 接口遍历:使用Burp Suite等工具遍历所有接口检测未授权访问
- 依赖检查:检查使用的框架版本是否存在已知漏洞
6. 修复建议
-
JWT安全增强:
- 使用独立于用户密码的JWT密钥
- 增加token过期时间和刷新机制
-
文件上传修复:
- 限制上传文件类型
- 对上传文件重命名并隐藏存储路径
- 设置文件服务器隔离环境
-
权限控制修复:
- 为所有敏感接口添加权限注解
- 实施基于角色的访问控制(RBAC)
- 添加接口级权限验证
-
敏感信息保护:
- 对返回的用户信息进行脱敏处理
- 实施最小权限原则,仅返回必要信息
通过本教学文档,安全研究人员可以深入了解电子签章系统的安全审计方法,掌握常见漏洞的挖掘技巧和修复方案。