应使用 path.combine 拼接路径而非字符串硬拼,它自动适配平台分隔符;判断绝对路径用 path.ispathrooted 而非字符串开头匹配;读写前须用 path.getfullpath 归一化并校验是否在允许根目录内。

用 Path.Combine 替代字符串拼接
硬拼 "dir\" + filename 或 "dir/" + filename 在跨平台时必然出错:Windows 用反斜杠,Linux/macOS 用正斜杠,且某些 API(如 File.OpenRead)在 Linux 下遇到 会直接抛 ArgumentException。
Path.Combine 是唯一推荐的路径拼接方式,它自动适配当前运行平台的分隔符:
string path = Path.Combine("logs", "app", "2024", "error.txt");
在 Windows 上生成 "logs\app\2024\error.txt",在 Linux 上生成 "logs/app/2024/error.txt",语义一致、行为可靠。
- 永远不要对
Path.Combine的输出做字符串替换(比如把/换成\),它已是最简且合法路径 - 传入参数中若含完整路径(如以
/或C:\开头),Path.Combine会丢弃前面所有段——这是设计行为,不是 bug - 若需拼接 URI 或 URL,请用
Uri类,而非Path
判断路径是否为绝对路径:用 Path.IsPathRooted,别用字符串开头判断
写 path.StartsWith("/") || path.StartsWith("C:") 是典型跨平台陷阱:Linux 路径以 / 开头,Windows 可能是 C:、Z:,还可能是 UNC 路径 \servershare,甚至 WSL 下的 /mnt/c/...。
Path.IsPathRooted(path) 封装了全部平台逻辑,返回 true 即表示该路径已含根(无论格式),可直接用于 I/O 操作:
if (Path.IsPathRooted(userInput)) {
// 安全使用,无需再拼 base dir
File.ReadAllText(userInput);
} else {
// 相对路径,需补前缀
string fullPath = Path.Combine(baseDir, userInput);
}
-
Path.IsPathRooted在 .NET 5+ 中已完全支持 WSL 和容器环境 - 注意:空字符串、
"."、".."均返回false,符合预期 - 不要用
Path.GetFullPath做判断依据——它可能抛异常(如路径不存在),且副作用大(解析符号链接、展开..)
读写文件前,先用 Path.GetFullPath 归一化并校验范围
用户输入或配置项中的路径可能含 ..、多余 .、双斜杠等,不处理就直接拼接可能导致越界访问(如被诱导读取 /etc/passwd)。
正确做法是归一化 + 白名单校验:
string input = "../config/secrets.json";
string candidate = Path.GetFullPath(Path.Combine(baseDir, input));
if (!candidate.StartsWith(baseDir, StringComparison.Ordinal)) {
throw new SecurityException("Path escape attempt detected");
}
File.ReadAllText(candidate);
-
Path.GetFullPath会消除..和.,将路径转为绝对、规范形式(如/home/user/../tmp→/tmp) - 必须用
StringComparison.Ordinal做前缀检查——避免 Unicode 规范化或大小写干扰(Linux 路径区分大小写) - 仅靠
Path.IsPathRooted不够:相对路径经GetFullPath后也可能跳出沙箱,必须显式限制根目录
跨平台路径调试:打印 Environment.OSVersion 和 Path.DirectorySeparatorChar
当路径行为异常时,最快速定位方式不是猜系统,而是当场查值:
Console.WriteLine($"OS: {Environment.OSVersion}");
Console.WriteLine($"Separator: '{Path.DirectorySeparatorChar}'");
Console.WriteLine($"Alt separator: '{Path.AltDirectorySeparatorChar}'");
Console.WriteLine($"Is Unix: {OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()}");
-
Path.DirectorySeparatorChar在 Windows 是'\',Linux/macOS 是'/';Path.AltDirectorySeparatorChar总是'/',可用于容错解析(但写路径时仍应优先用主分隔符) -
OperatingSystem.IsLinux()比Environment.OSVersion.Platform == PlatformID.Unix更准确,后者在 macOS 上可能误判 - Docker 容器内跑 .NET 应用时,
Environment.OSVersion显示的是容器 OS,不是宿主机——这点常被忽略
Path.Combine 和 IsPathRooted,只要没做 GetFullPath + StartsWith 校验,就仍可能被 ../../../etc/shadow 这类输入穿透。










