os.CreateTemp的dir参数不能传"",否则会调用os.TempDir()而受TMPDIR/TEMP环境变量控制,导致临时文件被导向任意可写路径(如/etc/),且容器中可能返回空或不可写路径;必须显式指定可信目录并提前创建。

Go 里没有“自动安全”的临时路径,所有看似省事的默认行为都可能被环境变量、权限配置或竞态条件绕过。必须显式控制 dir、pattern、perm,并覆盖所有退出路径做清理。
os.CreateTemp 的 dir 参数为什么不能传 ""
传空字符串 "" 会让 Go 调用 os.TempDir(),而该函数受 TMPDIR(Unix)或 TEMP(Windows)环境变量控制——攻击者可提前设置 TMPDIR=/etc,导致临时文件落到系统关键目录下。
更糟的是:容器或 CI 环境中,os.TempDir() 可能返回空值或不可写路径,不显式检查就 panic。
- 生产环境应固定私有路径,如
filepath.Join("/var/tmp", "myapp"),并提前os.MkdirAll(dir, 0755) - 测试中可用
t.TempDir(),它返回已存在、空且自动清理的路径,无需手动os.MkdirAll - Windows 下
dir必须已存在,os.CreateTemp不会递归创建父目录
pattern 中的 * 是占位符,不是通配符
os.CreateTemp 第二个参数里的 * 不是 shell 风格的 glob,而是唯一性占位符:Go 用随机字节替换它来防冲突。漏写、多写、放错位置都会直接 panic。
-
os.CreateTemp("/tmp", "log-*.txt")✅ 生成/tmp/log-aB3f9.txt -
os.CreateTemp("/tmp", "log.txt")❌ panic:pattern contains no asterisk -
os.CreateTemp("/tmp", "log-*.txt.*")❌ panic:pattern contains multiple *s -
os.CreateTemp("/tmp", "subdir/*.log")❌ 在当前目录创建字面名为subdir/*.log的文件,不是子目录
权限必须在创建时指定,别碰 os.Chmod
默认权限是 0600(仅属主读写),安全但有时需共享读取(如日志被外部工具 tail)。错误做法是创建后再 os.Chmod(file.Name(), 0644)——中间存在竞态窗口,其他进程可能读取或篡改文件。
- Go 1.19+ 支持三参数版本:
os.CreateTemp(dir, "log-*.log", 0644) - 旧版本只能事后
os.Chmod,但必须接受竞态风险;建议升级 - 别设
0666:多用户系统上等于主动暴露敏感数据 - Windows 忽略 perm 参数,权限继承父目录;若强依赖,创建后补
os.Chmod(dir, 0700)
清理逻辑必须覆盖 panic 和早期 return
os.CreateTemp 返回的是已打开的 *os.File,关闭句柄(file.Close()) ≠ 删除文件。不清理,文件就永久残留——尤其在测试中跑十次,留十个文件。
- 别只写
defer os.Remove(file.Name()):如果file.Write失败后return,defer 不触发 - 推荐包一层:
defer func() { os.RemoveAll(dir) }(),确保 panic 或任意 return 都执行 - 测试中优先用
t.Cleanup(func() { os.Remove(f.Name()) }),比 defer 更可靠 - 临时目录必须用
os.RemoveAll,os.Remove对非空目录直接报directory not empty
最易被忽略的点:临时文件写入中途失败(比如 file.Sync() 报 ENOSPC),文件已创建但没被删——所有可能提前退出的分支,都要各自处理清理,不能只靠入口处一个 defer。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











