如何修复Gitlab SAST检测到的Java文件对象路径遍历漏洞?
修复Gitlab SAST检测到的Java路径遍历漏洞
问题场景
通过输入字符串创建File对象并写入文件,已用FilenameUtils.normalize()处理filePath修复了部分漏洞,但FileOutputStream fos = new FileOutputStream( file , false );仍被检测出路径遍历问题,尝试getCanonicalPath()未解决。
原代码
private BufferedWriter createFile( String filePath, String fileName ) { try { File dir = new File( FilenameUtils.normalize(filePath) ); if ( ! dir.exists() ) { if (! dir.mkdirs() ) { log("** WARNING: The file " + fileName + " cannot be created because the path " + filePath + " could not be created **\n"); return null; } } File file = new File(dir.getPath() + File.separatorChar + fileName); if ( file.exists() ) file.delete(); FileOutputStream fos = new FileOutputStream( file , false ); ps = new PrintStream(fos); return new BufferedWriter(new FileWriter(file)); } catch (Exception e) { log("** Warning: could not create file : " + filePath + File.separatorChar + fileName + " **\n"); e.printStackTrace(); return null; } }
漏洞根源
当前漏洞的核心是**fileName参数未做任何校验和规范化**:即使filePath被处理,若fileName包含../、..\或跨系统的路径分隔符,拼接后仍能跳出目标目录,实现路径遍历。此外,字符串拼接路径的方式容易引入分隔符问题,且未验证最终文件是否确实落在允许的目录范围内。
修复方案
1. 规范化并校验fileName
使用FilenameUtils.getName()提取纯文件名(自动去除路径部分),确保fileName只是单纯的文件名而非路径,从根源上阻断遍历可能。
2. 安全拼接路径
用File类的构造方法new File(dir, fileName)代替字符串拼接,避免手动处理分隔符的错误。
3. 验证文件路径范围
通过getCanonicalPath()获取目录和文件的规范绝对路径,检查文件路径是否以目录路径开头,确保文件不会被创建到目标目录之外。
4. 清理冗余资源
原代码中创建的FileOutputStream和PrintStream未被使用,且ps作为类成员容易引发资源泄漏,直接删除这部分代码。
修改后的完整代码
private BufferedWriter createFile(String filePath, String fileName) { try { // 规范化目录路径并获取规范绝对路径 String normalizedDirPath = FilenameUtils.normalize(filePath); File dir = new File(normalizedDirPath); String dirCanonicalPath = dir.getCanonicalPath(); if (!dir.exists()) { if (!dir.mkdirs()) { log("** WARNING: The file " + fileName + " cannot be created because the path " + filePath + " could not be created **\n"); return null; } } // 提取纯文件名,过滤路径遍历字符 String safeFileName = FilenameUtils.getName(fileName); // 安全拼接文件路径 File file = new File(dir, safeFileName); // 获取文件的规范绝对路径 String fileCanonicalPath = file.getCanonicalPath(); // 验证文件是否严格在目标目录范围内 if (!fileCanonicalPath.startsWith(dirCanonicalPath + File.separator)) { log("** WARNING: Invalid file name or path traversal detected for file: " + fileName + " **\n"); return null; } if (file.exists()) { file.delete(); } // 直接创建BufferedWriter,移除冗余资源 return new BufferedWriter(new FileWriter(file)); } catch (Exception e) { log("** Warning: could not create file : " + filePath + File.separatorChar + fileName + " **\n"); e.printStackTrace(); return null; } }
关键修改说明
safeFileName = FilenameUtils.getName(fileName):自动剥离fileName中的路径部分(比如../secret.txt会被处理为secret.txt),彻底避免路径遍历。- 路径范围验证:通过
getCanonicalPath()获取真实的文件系统路径,确保文件路径严格隶属于目标目录,防止任何绕过规范化的遍历手段。 - 移除冗余资源:原代码中
fos和ps未被使用,且ps作为类成员可能导致资源未关闭,直接移除后消除泄漏风险。
内容的提问来源于stack exchange,提问作者Poonam
相关产品推荐
相关产品推荐

