Snyk检测到路径遍历问题:已做输入清理仍未解决
路径遍历问题排查与代码修复
我用Snyk检测代码时触发了路径遍历告警,涉事代码行是:FileUtils.CopyFile(this.txtEnFileName.Text, fullEnPath, true, true);
我已经在该行上方添加了输入清理逻辑:移除无效路径/文件名字符、用Path.Combine安全组合路径、校验路径合法性,但重新扫描后问题仍存在,怀疑需要修改涉事代码块。完整代码如下:
// Sanitize inputs string sanitizedSubDirName = Path.GetInvalidPathChars().Aggregate(subDirName, (current, c) => current.Replace(c.ToString(), string.Empty)); string sanitizedProduct = Path.GetInvalidFileNameChars().Aggregate(productList[count], (current, c) => current.Replace(c.ToString(), string.Empty)); string sanitizedRootFileName = Path.GetInvalidFileNameChars().Aggregate(rootFileName, (current, c) => current.Replace(c.ToString(), string.Empty)); // Combine paths safely dstEnFileName = Path.Combine(sanitizedSubDirName, sanitizedProduct, sanitizedRootFileName + "EN.rtf"); dstFrFileName = Path.Combine(sanitizedSubDirName, sanitizedProduct, sanitizedRootFileName + "FR.rtf"); string fullEnPath = Path.GetFullPath(dstEnFileName); string fullFrPath = Path.GetFullPath(dstFrFileName); if (!fullEnPath.StartsWith(subDirName, StringComparison.OrdinalIgnoreCase) || !fullFrPath.StartsWith(subDirName, StringComparison.OrdinalIgnoreCase)) { throw new UnauthorizedAccessException("Path traversal detected"); } // Perform the file copy FileUtils.CopyFile(this.txtEnFileName.Text, fullEnPath, true, true); FileUtils.CopyFile(this.txtFrFileName.Text, fullFrPath, true, true);
问题根源
- 目标路径校验逻辑不严谨:直接用
fullEnPath.StartsWith(subDirName)存在漏洞——如果subDirName是C:\safe,构造出的fullEnPath是C:\safeFake\file.txt,字符串前缀匹配会误判为合法;另外如果subDirName是相对路径,转成绝对路径后和原字符串比对会完全失效。 - 源路径完全未校验:Snyk的告警大概率是针对
this.txtEnFileName.Text和this.txtFrFileName.Text这两个用户可控的源路径——攻击者可以输入../../malware.exe这类路径,让程序复制恶意文件,你之前的逻辑完全没覆盖这块风险。
修复方案
核心修改点
- 把所有路径转成绝对路径后再做校验
- 校验时在基准路径后追加目录分隔符,避免部分匹配的误判
- 新增源路径的合法性校验,限定在指定的安全目录内
修改后的代码
// 定义允许的源根目录和目标基准目录(根据实际业务场景调整) string allowedSourceRoot = Path.GetFullPath(@"C:\YourAllowedSourceDirectory"); string targetBaseDir = Path.GetFullPath(subDirName); // Sanitize inputs string sanitizedSubDirName = Path.GetInvalidPathChars().Aggregate(subDirName, (current, c) => current.Replace(c.ToString(), string.Empty)); string sanitizedProduct = Path.GetInvalidFileNameChars().Aggregate(productList[count], (current, c) => current.Replace(c.ToString(), string.Empty)); string sanitizedRootFileName = Path.GetInvalidFileNameChars().Aggregate(rootFileName, (current, c) => current.Replace(c.ToString(), string.Empty)); // Combine paths safely dstEnFileName = Path.Combine(sanitizedSubDirName, sanitizedProduct, sanitizedRootFileName + "EN.rtf"); dstFrFileName = Path.Combine(sanitizedSubDirName, sanitizedProduct, sanitizedRootFileName + "FR.rtf"); string fullEnPath = Path.GetFullPath(dstEnFileName); string fullFrPath = Path.GetFullPath(dstFrFileName); // 修复目标路径校验:用绝对路径+目录分隔符做精确匹配 string targetBaseWithSeparator = targetBaseDir + Path.DirectorySeparatorChar; if (!fullEnPath.StartsWith(targetBaseWithSeparator, StringComparison.OrdinalIgnoreCase) || !fullFrPath.StartsWith(targetBaseWithSeparator, StringComparison.OrdinalIgnoreCase)) { throw new UnauthorizedAccessException("Path traversal detected"); } // 新增源路径合法性校验 string sourceEnPath = Path.GetFullPath(this.txtEnFileName.Text); string sourceFrPath = Path.GetFullPath(this.txtFrFileName.Text); string sourceRootWithSeparator = allowedSourceRoot + Path.DirectorySeparatorChar; if (!sourceEnPath.StartsWith(sourceRootWithSeparator, StringComparison.OrdinalIgnoreCase) || !sourceFrPath.StartsWith(sourceRootWithSeparator, StringComparison.OrdinalIgnoreCase)) { throw new UnauthorizedAccessException("Invalid source path detected"); } // 执行文件复制(使用校验后的源路径) FileUtils.CopyFile(sourceEnPath, fullEnPath, true, true); FileUtils.CopyFile(sourceFrPath, fullFrPath, true, true);
额外说明
- 必须根据实际业务场景明确
allowedSourceRoot的取值,不能开放任意源目录权限 - 用绝对路径+目录分隔符的前缀匹配,能彻底避免部分字符串匹配带来的误判
- 静态扫描工具会检查所有用户可控的输入路径,包括源和目标,两个方向的校验都不能少
内容的提问来源于stack exchange,提问作者ohayoouuuu
相关产品推荐
相关产品推荐

