Back to skills
extension
Category: Development & EngineeringNo API key required

java安全扫描修复

Java / Spring Boot 项目安全编码与漏洞修复指南。在生成代码、修改代码或代码审查时自动触发,覆盖以下场景: (1) 编写或修改全局异常处理器、文件上传/下载接口、HTTP 响应头设置、文件流操作、Spring MVC 参数绑定等代码; (2) 拼接用户传入的文件路径、使用 FileInputStream/FileOutputStream/XSSFWorkbook、设置 Content-Disposition/Content-Type 等编码场景; (3) 收到代码扫描报告(fxnk、Fortify、Checkmarx、SonarQube)需要修复中危/高危缺陷; (4) 需要预防或修复信息泄露、HTTP 响应截断、流资源未释放、路径遍历、不安全的框架绑定(mass assignment)等中等级缺陷; (5) 需要预防或修复 SQL 注入、存储型 XSS、反射型 XSS、高危路径遍历等高等级缺陷。 技术栈:Spring MVC + MyBatis + Apache POI。包含预防性安全编码规范、从 .docx 扫描报告提取文本、分类统计、按优先级制定修复计划、具体代码修改模式。

personAuthor: cbq881215hubModelScope

Java 安全扫描中高危等级缺陷修复指南

Problem

Java / Spring Boot 项目在安全代码扫描后,通常会产生数百到数千个中高等级缺陷,涵盖信息泄露、HTTP响应截断、流泄漏、路径遍历、不安全的框架绑定、SQL 注入、XSS 等类别。需要系统性地分类、制定计划、实施修复并验证编译。

Context / Trigger Conditions

编码/审查时(预防与修复均可触发):

  • 编写或修改 GlobalExceptionHandler 等全局异常处理器
  • 设置 HTTP 响应头(Content-DispositionContent-TypeSet-Cookie 等)且值来自用户输入
  • 拼接用户传入的文件路径进行文件读写、移动、复制、删除操作
  • 使用 FileInputStreamFileOutputStreamXSSFWorkbookPdfWriterServletOutputStream 等流对象
  • Spring MVC Controller 方法直接使用 Bean/VO 接收请求参数(public String add(User user)
  • 处理 ZIP 解压、文件上传 MultipartFile、Base64 转文件等场景
  • 对密码/密钥/随机数进行编码或加密操作
  • 编写或修改 MyBatis Mapper XML / @Select / @Update,使用 ${} 拼接参数
  • 将用户输入写入 Excel、HTML、PDF 或前端页面(存在 XSS 风险)
  • Controller 方法直接将用户输入回显到响应或视图(反射型 XSS)
  • 技术栈:Spring Boot + Spring MVC + MyBatis + Apache POI

收到扫描报告时:

  • 代码扫描报告(常见格式:Word .docx、PDF、HTML)
  • 扫描工具:fxnk、Fortify、Checkmarx、SonarQube 等
  • 中危缺陷数量通常在 1000+,需要分阶段实施

Solution

0. 预防性安全编码规范(写代码时直接遵循)

在生成或修改代码时,直接应用以下规范以避免中危缺陷:

| 场景 | 安全规范 | | ------------------- | ---------------------------------------------------------------------------------------------- | | 全局异常处理 | 通用 Exception 捕获后返回固定模糊提示(如 "系统繁忙,请稍后重试"),禁止返回 e.getMessage() 给前端 | | HTTP 响应头 | 用户传入的字符串写入响应头前,必须过滤 \r\nvalue.replaceAll("[\\r\\n]", "") | | 文件流操作 | 所有 FileInputStream / FileOutputStream / Workbook / PdfWriter 必须使用 try-with-resources | | 文件路径拼接 | 用户传入的路径使用 getCanonicalPath() 校验,确保结果路径在允许的根目录范围内 | | Spring MVC 参数绑定 | 公共基类的 @InitBinder 中禁用敏感字段:binder.setDisallowedFields(...) | | 文件上传 | 校验 MIME Type、扩展名白名单、文件头 Magic Number,上传后重命名为随机文件名 | | SQL 查询 | MyBatis 中禁止 ${} 拼接用户输入,统一使用 #{} 参数化查询;动态表名列名需先做白名单校验 | | XSS 防护 | 输出到 Excel/HTML/页面的用户数据先做 HTML 转义;前端输入做白名单校验,禁止直接回显未过滤的用户输入 |

1. 读取 .docx 扫描报告

若报告为 .docx 二进制文件,用 ZIP + XML 提取文本:

$temp = [IO.Path]::GetTempPath() + [Guid]::NewGuid().ToString()
[System.IO.Compression.ZipFile]::ExtractToDirectory('report.docx', $temp)
$xml = [xml](Get-Content "$temp\word\document.xml" -Encoding UTF8 -Raw)
$ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$ns.AddNamespace('w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main')
$text = ($xml.SelectNodes('//w:t', $ns) | ForEach-Object { $_.InnerText }) -join ''

2. 缺陷分类与优先级排序

按风险影响 + 修复难度排序:

| 优先级 | 缺陷类别 | 典型数量 | 修复难度 | | --- | ----------------------------------- | ----- | -------- | | P0 | SQL 注入 + 存储型 XSS + 反射型 XSS + 高危路径遍历 | ~500+ | 中 | | P1 | 信息泄露 + HTTP响应截断 | ~10 | 低 | | P2 | 流资源未释放 + 路径遍历 | ~70 | 中 | | P3 | 框架绑定 / Mass Assignment | ~300+ | 中 | | P4 | 数据库访问控制 | ~900+ | 高(需评估误报) | | P5 | 密码管理 / 加密算法 | ~30 | 高(需兼容评估) |

3. 常见缺陷修复代码模式

3.1 信息泄露(全局异常处理器)

问题handleException(Exception.class) 返回 e.getMessage(),可能暴露堆栈/SQL。 修复:返回统一模糊提示,详细异常只记录日志。

@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e) {
    log.error(e.getMessage(), e);
    return AjaxResult.error("系统繁忙,请稍后重试");
}

3.2 HTTP 响应截断(Content-Disposition)

问题:用户可控文件名直接拼接到响应头,未过滤 \r\n修复:在编码前过滤换行符。

fileName = fileName.replaceAll("[\\r\\n]", "");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

3.3 流资源未释放

问题FileInputStream / FileOutputStream / Workbook 等未在 finally 中关闭。 修复:统一使用 try-with-resources

// 修复前
FileInputStream fis = new FileInputStream(file);
XSSFWorkbook wb = new XSSFWorkbook(fis);
// ... 业务逻辑 ...
// 缺少关闭

// 修复后
try (FileInputStream fis = new FileInputStream(file);
     XSSFWorkbook wb = new XSSFWorkbook(fis)) {
    // ... 业务逻辑 ...
}

关键类需检查:FileUtilsExcelUtilsPdfUtilsHtmlUtilsCsvUtils

3.4 路径遍历

问题:用户传入的路径直接拼接根目录,可能导致 ../../etc/passwd修复:使用 getCanonicalPath() 校验结果路径必须在根目录内。

private void validatePath(String path) {
    try {
        File file = new File(rootPath, path);
        String canonicalPath = file.getCanonicalPath();
        String canonicalRoot = new File(rootPath).getCanonicalPath();
        if (!canonicalPath.startsWith(canonicalRoot)) {
            throw new BaseException("非法文件路径");
        }
    } catch (IOException e) {
        throw new BaseException("文件路径校验失败");
    }
}

3.5 Spring MVC 不安全的框架绑定(Mass Assignment)

问题:Controller 直接绑定请求参数到 Bean,攻击者可批量赋值敏感字段(如 admin=true)。 修复:在公共基类的 @InitBinder 中全局禁用敏感字段。

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.setDisallowedFields("admin", "role", "password", "createTime",
            "updateTime", "createBy", "updateBy", "deleted", "delFlag", "tenantId");
    // ... 其他类型转换注册
}

3.6 SQL 注入(MyBatis 动态 SQL)

问题:MyBatis Mapper 中使用 ${} 拼接用户输入,攻击者可注入任意 SQL。 修复:将 ${} 替换为 #{} 参数化查询;动态表名/列名必须走白名单校验。

// 修复前(危险)
@Select("SELECT * FROM ${tableName} WHERE id = ${id}")
User selectById(String tableName, Long id);

// 修复后
@Select("SELECT * FROM #{tableName} WHERE id = #{id}")  // 错误:#{} 不能用于表名
// 正确做法:表名走白名单,值用 # {}
private static final Set<String> ALLOWED_TABLES = Set.of("sys_user", "sys_dept");
public User selectById(String tableName, Long id) {
    if (!ALLOWED_TABLES.contains(tableName)) {
        throw new BaseException("非法表名");
    }
    return sqlSession.selectOne("SELECT * FROM " + tableName + " WHERE id = #{id}", id);
}

3.7 存储型 XSS(导出/模板渲染场景)

问题:将用户输入的数据直接写入 Excel、HTML、PDF,前端再次展示时触发脚本执行。 修复:在写入前对用户数据进行 HTML 转义,或设置 Excel 单元格为纯文本类型。

// 修复前
row.createCell(0).setCellValue(user.getRemark()); // 用户输入可能含 <script>

// 修复后
String safeRemark = HtmlUtils.htmlEscape(user.getRemark());
row.createCell(0).setCellValue(safeRemark);

// 或者设置单元格类型为 STRING,避免公式执行
Cell cell = row.createCell(0);
cell.setCellValue(user.getRemark());
cell.setCellType(CellType.STRING);

3.8 反射型 XSS(参数回显场景)

问题:Controller 直接将用户输入回显到页面/响应,未做过滤或转义。 修复:对回显参数做输入校验 + 输出编码,Spring Boot 可开启默认 HTML 转义。

// 修复前
@GetMapping("/search")
public String search(String keyword, Model model) {
    model.addAttribute("keyword", keyword); // 直接回显
    return "searchResult";
}

// 修复后
@GetMapping("/search")
public String search(@RequestParam String keyword, Model model) {
    // 白名单校验:仅允许中文、英文、数字、空格
    if (!keyword.matches("^[\\u4e00-\\u9fa5a-zA-Z0-9\\s]+$")) {
        throw new BaseException("搜索关键字包含非法字符");
    }
    model.addAttribute("keyword", keyword);
    return "searchResult";
}

3.9 高危路径遍历(文件名/存储路径校验)

问题:用户传入的 storeNamefileName 等直接作为文件路径或文件名,未校验 ../ 或绝对路径。 修复:对文件名提取后校验,禁止路径分隔符;完整路径使用 getCanonicalPath() 校验。

private void validateFileName(String fileName) {
    if (fileName == null || fileName.contains("..") || fileName.contains("/") || fileName.contains("\\")) {
        throw new BaseException("非法文件名");
    }
}

private void validatePath(String rootPath, String path) {
    try {
        File file = new File(rootPath, path);
        String canonicalPath = file.getCanonicalPath();
        String canonicalRoot = new File(rootPath).getCanonicalPath();
        if (!canonicalPath.startsWith(canonicalRoot)) {
            throw new BaseException("非法文件路径");
        }
    } catch (IOException e) {
        throw new BaseException("文件路径校验失败");
    }
}

4. 验证方式

修复后必须执行:

mvn clean compile -DskipTests

重点关注:文件上传/下载、Excel 导出、PDF 生成等核心流程的回归测试。

Verification

  • mvn clean compile -DskipTests 编译通过,无新增报错
  • 回归测试:登录、文件上传下载、Excel 导出、异常触发后的前端提示正常
  • 复扫:使用相同检测模板重新扫描,目标中危缺陷数量下降

Example

场景:扫描报告提示 ExcelUtils.setResponseHeader 存在 HTTP 响应截断。

// 修复前
fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

// 修复后
fileName = fileName.replaceAll("[\\r\\n]", "");
fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

Notes

  • 密码管理/加密算法类缺陷(DES 转 AES、MD5 升级等)涉及存量数据兼容性,需单独评估迁移方案(双算法支持 + 登录时自动迁移)。
  • 数据库访问控制类缺陷数量通常最多(900+),若系统已在 Controller/Service 层通过登录会话做了统一鉴权,多数为误报,优先修复暴露在外网或无 Token 的接口。
  • 扫描报告中的行号可能与当前代码分支不完全一致,需结合跟踪路径(调用链)定位实际爆发行。

References

  • OWASP Top 10 2021: https://owasp.org/Top10/
  • CWE-22 (Path Traversal): https://cwe.mitre.org/data/definitions/22.html
  • CWE-79 (Cross-site Scripting): https://cwe.mitre.org/data/definitions/79.html
  • CWE-89 (SQL Injection): https://cwe.mitre.org/data/definitions/89.html
  • CWE-116 (HTTP Response Splitting): https://cwe.mitre.org/data/definitions/116.html
  • CWE-20 (Improper Input Validation): https://cwe.mitre.org/data/definitions/20.html