临时文件生成必须用path.gettempfilename()确保原子性,禁用手动拼接;写入后须显式收紧权限;生命周期需代码严格控制,敏感内容禁存系统临时目录。

临时文件名生成必须用 Path.GetTempFileName()
手动拼接路径(比如 Path.Combine(Path.GetTempPath(), "tmp_" + Guid.NewGuid()))极容易引发竞争条件:两个线程/进程可能在 File.Exists() 检查后、File.Create() 执行前同时通过判断,最终覆盖或误删对方文件。
Path.GetTempFileName() 内部使用原子性系统调用(Windows 上是 GetTempFileNameW),确保文件创建与命名一步完成,且默认设置为只读+当前用户独占权限。它还会自动清理已超 10 天的旧临时文件(仅限 .NET Framework;.NET Core/.NET 5+ 不自动清理,需自行处理)。
- 不要传入自定义前缀或后缀——该方法不支持,强行拼接会破坏原子性
- 返回路径指向一个已创建、句柄已关闭的空文件,可直接
File.OpenWrite()或File.CreateText() - .NET 6+ 中若需更可控行为,改用
Path.GetRandomFileName()+FileStream手动创建,并显式设置FileOptions.DeleteOnClose(仅 Windows 支持)
临时文件内容写入后必须显式设置访问控制
即使用了 Path.GetTempFileName(),新文件在 NTFS 上默认继承父目录权限,可能被同组其他用户读取(尤其在服务器多租户场景)。Linux/macOS 下则依赖 umask,不可靠。
必须在写入完成后立即收紧权限:
- Windows:用
File.SetAccessControl(path, new FileSecurity())配合FileSystemRights.Read+AccessControlType.Deny给BUILTIN\Users或Everyone - Cross-platform 安全写法:优先用
FileStream构造时传入FileOptions.None并配合FileAttributes.Temporary(提示系统缓存优化,不保证安全),再叠加File.SetAttributes(path, FileAttributes.Temporary | FileAttributes.ReadOnly) - 敏感内容禁止用
StreamWriter默认构造(无编码指定),务必显式指定Encoding.UTF8防止 BOM 泄露或解析歧义
临时文件生命周期必须由代码严格控制
依赖操作系统或“下次重启自动清理”是高危行为。常见错误包括:try/finally 里只删文件没处理 UnauthorizedAccessException,或异步任务中文件句柄未释放就尝试删除。
- 始终用
using (var fs = File.Create(path)) { ... }确保流及时释放 - 删除前检查
File.Exists(),删除后验证!File.Exists()(防止符号链接攻击或权限绕过) - 若需跨进程共享临时数据,改用
MemoryMappedFile或命名管道,而非磁盘文件 - 日志类临时文件建议加时间戳前缀,并定期扫描
Path.GetTempPath()下 24 小时未访问的*.tmp文件批量清理(避免影响主线程)
敏感临时文件禁止写入系统临时目录
Path.GetTempPath() 在多用户系统中通常是全局可列目录(如 C:\Windows\Temp 或 /tmp),攻击者可枚举文件名、预测随机数、甚至硬链接劫持。
- 高敏感场景(如解密密钥、JWT payload 缓存)应写入用户专属目录:
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyApp", "temp") - 容器环境必须挂载
tmpfs卷到自定义路径,禁用宿主机/tmp - 永远不要把密码、token、私钥等字符串写入临时文件——改用
SecureString(.NET Framework)或Memory<t></t>+ArrayPool<byte>.Shared</byte>手动清零










