解决Coverity检测的OS命令注入问题:Java 11文件名验证优化
解决Coverity检测出的OS命令注入问题
问题背景
我正试图解决Coverity检测出的“OS_Command_injection”问题。文件名来自用户上传的Multipart文件,验证代码如下:
String filename = item.getName(); String regex = "^[a-zA-Z0-9_-]*$"; if(filename.matches(regex)) { if(filename.contains("../") || filename.contains("..") || filename.contains("./") || filename.contains("/")) { throw new BRException("Invalid filename"); } Path filePath = Paths.get(location, filename); try ( OutputStream out = new FileOutputStream(filePath.toString()); ) { IOUtils.copy(stream, out); params.put("filename", filename); params.put("filePath", filePath); } } else { throw new FSException("filename is Invalid"); }
后续调用Shell脚本进行病毒扫描时触发了OS命令注入告警,scanFile方法的相关代码(编辑后)如下:
scanScriptPath = "/scantool/scan.sh"; // 解析为shell脚本路径 StringBuilder scriptNfile = new StringBuilder("").append(scanScriptPath.toString()).append(" ").append(filePath.toString()); String[] cmd = {"sh","-c",scriptNfile.toString() }; ProcessBuilder pb = new ProcessBuilder(cmd); // OS_Command_Sink Process proc = pb.start(); proc.waitFor();
Coverity错误信息:
OS Command Injection (OS_CMD_INJECTION)7. os_cmd_sink: Calling scanFile. Passing the tainted value filename to the process-invoking API may allow an attacker to modify the intention of the command. (The virtual call resolves to com.xyz.scanFile(java.lang.String, java.lang.String, java.lang.String)
已通过正则验证文件名并检查路径遍历字符,但问题仍未解决,请问还遗漏了什么?
问题根源
当前命令执行方式存在核心风险:
- 使用
sh -c拼接命令字符串:这种方式会让shell解析整个命令串,即便文件名符合正则,只要filePath包含shell特殊字符(如空格、;、&、$等),攻击者就能构造恶意文件名注入额外命令。Coverity的污点分析会标记这种拼接方式为高风险,因为输入被当作shell命令串的一部分,本质上还是存在注入可能。 - 冗余的路径检查:正则
^[a-zA-Z0-9_-]*$已经排除了/、..等字符,后续的contains检查完全多余,不是问题核心。
解决方案
彻底规避命令注入的关键是避免拼接命令字符串,改用参数化传递:
修改后的scanFile代码
scanScriptPath = "/scantool/scan.sh"; // 脚本路径 // 直接将脚本和文件路径作为独立参数传入,不拼接字符串 String[] cmd = {scanScriptPath.toString(), filePath.toString()}; ProcessBuilder pb = new ProcessBuilder(cmd); Process proc = pb.start(); proc.waitFor();
原理说明
ProcessBuilder会把数组中的每个元素当作独立的命令/参数,不会经过shell解析。脚本scan.sh会直接将filePath作为第一个参数接收,彻底避免shell对特殊字符的解析,从根源上杜绝命令注入风险。同时保留原有的文件名正则验证,确保文件名合法。
额外优化建议
- 移除冗余的路径检查代码,正则已限制文件名仅含合法字符。
- 使用Java NIO的
Files.copy替代FileOutputStream+IOUtils.copy,代码更简洁规范:
Files.copy(stream, filePath, StandardCopyOption.REPLACE_EXISTING);
内容的提问来源于stack exchange,提问作者sanjeevRm
相关产品推荐
相关产品推荐

