58人参与 • 2026-10-11 • Python
在 python 开发中,我们经常需要创建临时文件来存放中间数据、缓存计算结果、或作为原子写入的跳板。很多开发者的第一反应是手动拼一个 /tmp/mydata.txt,然后用完删除。这种做法看似简单,却隐藏着严重的安全漏洞和可靠性问题:文件名冲突导致数据被覆盖、符号链接攻击导致敏感文件被写入、多进程并发时互相踩踏、程序崩溃后临时文件永远残留。更糟的是,这些隐患在开发环境中几乎不会暴露,一旦部署到多用户的生产服务器,就会成为攻击者的突破口。
python 标准库提供的 tempfile 模块,正是为了解决这些问题而生。它能够安全地创建临时文件和目录,自动处理命名冲突、权限设置和清理。但即使有了这个强大的工具,如果使用不当,仍然会掉进各种陷阱。今天,我们就来彻底解剖 tempfile 模块的正确用法,让你从此告别“裸奔”的临时文件。

temp_path = '/tmp/myapp_cache.txt'
def save_cache(data):
with open(temp_path, 'w') as f:
f.write(data)
如果两个进程同时运行 save_cache,它们会互相覆盖对方的文件,导致数据损坏。如果多个用户在同一台机器上运行同一个程序,还会引发权限问题。
攻击者可以在 /tmp 下预先创建一个指向 /etc/passwd 的符号链接,命名为 myapp_cache.txt。当你的程序以 root 权限运行时,open(temp_path, 'w') 会跟随符号链接,直接覆盖系统关键文件。这是经典的 tmp 目录安全漏洞。
f = open('/tmp/temp_data.txt', 'w')
f.write(process())
# 如果 process() 抛出异常,f 永远不会关闭,临时文件永远留在磁盘上
import os
import random
def insecure_temp():
name = f'/tmp/tmp{random.randint(0, 999999)}'
fd = os.open(name, os.o_creat | os.o_excl | os.o_wronly)
return fd
虽然使用了 o_excl 避免了部分竞态,但随机数生成不当仍可能碰撞,而且需要手动处理关闭和删除。
try:
path = '/tmp/temp.txt'
with open(path, 'w') as f:
f.write(data)
do_something(path)
finally:
os.remove(path) # 如果 path 未定义,抛出 nameerror
path = 'c:\\temp\\temp.txt' # 硬编码 windows 路径,在 linux 上失败
open('/tmp/secret.txt', 'w').write(password)
# 默认权限可能是 0644,其他用户可读
| 函数 | 用途 |
|---|---|
tempfile.temporaryfile() | 创建匿名临时文件,关闭时自动删除 |
tempfile.namedtemporaryfile() | 创建有名字的临时文件,可指定是否自动删除 |
tempfile.mkstemp() | 创建临时文件,返回文件描述符和路径,需手动清理 |
tempfile.mkdtemp() | 创建临时目录,需手动清理 |
tempfile.gettempdir() | 获取系统临时目录 |
tempfile.gettempdirb() | 同上,返回字节串 |
tempfile.spooledtemporaryfile() | 内存缓冲的临时文件,超过阈值才写入磁盘 |
tempfile.temporarydirectory() | 上下文管理器,退出时自动删除目录树 |
tempfile 使用 os.urandom 生成随机字符串,结合进程 id 和时间戳,生成难以预测的文件名。然后使用 os.open 配合 o_creat | o_excl | o_rdwr 标志,确保文件是原子性创建的:如果同名文件已存在(无论是普通文件还是符号链接),创建会失败并重试。这从根本上阻止了符号链接攻击和竞态条件。
创建临时文件时,tempfile 使用 0o600(仅所有者可读写)作为默认权限,而不是 0o666 减去 umask。这保证了临时文件不会被其他用户读取或写入。
temporaryfile() 返回的文件对象在关闭时(close() 或 with 退出)自动删除底层文件。namedtemporaryfile() 默认在关闭时删除文件(可通过 delete=false 禁用)。temporarydirectory() 在上下文退出时递归删除整个目录树。tempfile 优先使用 tmpdir、temp、tmp 环境变量指定的目录,否则使用平台默认(unix 下是 /tmp,windows 下是用户临时目录)。这确保了跨平台兼容性。
with tempfile.namedtemporaryfile(mode='w', delete=true) as f:
f.write('data')
f.flush()
# 在 windows 上,文件仍被打开,无法被其他进程读取
with open(f.name, 'r') as f2: # permissionerror
f2.read()
在 windows 上,namedtemporaryfile 打开的文件默认不能被再次打开(除非指定 delete=false 并关闭)。解决方案:使用 delete=false,在 with 块外读取,然后手动删除;或者使用 temporaryfile 完全避免命名。
fd, path = tempfile.mkstemp() os.write(fd, b'data') os.close(fd) # 忘记 os.unlink(path),临时文件永远残留
mkstemp 和 mkdtemp 不会自动清理,必须手动删除。推荐使用 with 配合 temporarydirectory 或 namedtemporaryfile。
with tempfile.namedtemporaryfile(delete=false) as f:
f.write('data')
path = f.name
# 文件不会自动删除,需要手动 os.unlink(path)
在异常路径中,如果忘记删除,临时文件就会泄漏。使用 try/finally 或 temporarydirectory 来管理。
with tempfile.temporaryfile() as f:
f.write(b'data')
subprocess.run(['some_command', f.name]) # temporaryfile 没有 name 属性!
temporaryfile 在 unix 上甚至没有名字(匿名的),无法被外部命令访问。如果需要路径,应使用 namedtemporaryfile。
with tempfile.namedtemporaryfile(delete=false) as f:
f.write(b'data')
path = f.name
os.replace(path, '/other/filesystem/target.txt') # 可能跨设备失败
临时目录可能与目标目录不在同一个文件系统上,导致 os.replace 或 os.rename 抛出 oserror: [errno 18] invalid cross-device link。解决方案:在目标目录所在文件系统中创建临时文件,或使用 shutil.move(它会复制后删除,但非原子)。
temp = tempfile.namedtemporaryfile(delete=false)
def worker():
temp.write(...) # 多线程共享,数据混乱
临时文件通常不应在多个线程间共享,每个线程应创建自己的临时资源。
f = tempfile.namedtemporaryfile(delete=true) path = f.name f.close() # 文件被删除,path 变成无效路径
如果你需要保留路径,必须在文件打开期间使用它,或者在关闭前复制内容。
import tempfile
with tempfile.temporaryfile(mode='w+', encoding='utf-8') as f:
f.write('临时数据')
f.seek(0)
print(f.read())
# 文件自动关闭并删除
适用于不需要文件路径,只需要一个类文件对象的场景。
import tempfile
import os
with tempfile.namedtemporaryfile(mode='w', suffix='.txt', delete=false) as f:
f.write('数据')
path = f.name
try:
process(path)
finally:
os.unlink(path)
或者使用 delete=true(默认),但在关闭前完成所有需要路径的操作:
with tempfile.namedtemporaryfile(mode='w', suffix='.txt', delete=true) as f:
f.write('数据')
f.flush()
process(f.name) # 文件仍然存在
import tempfile
from pathlib import path
with tempfile.temporarydirectory() as tmpdir:
tmp_path = path(tmpdir)
(tmp_path / 'file1.txt').write_text('data')
(tmp_path / 'subdir').mkdir()
# 退出时整个目录树被删除
import tempfile
import os
fd, path = tempfile.mkstemp(suffix='.txt')
try:
with os.fdopen(fd, 'w') as f:
f.write('data')
process(path)
finally:
os.unlink(path)
注意 os.fdopen 接管文件描述符,避免泄漏。
import tempfile
with tempfile.spooledtemporaryfile(max_size=1024*1024) as f:
for i in range(1000):
f.write(f'line {i}\n'.encode())
f.seek(0)
content = f.read()
小数据保留在内存中,超过 max_size 后自动滚动到磁盘。适合不确定数据大小的场景。
import tempfile
import os
import json
def atomic_write(path, data):
dir_name = os.path.dirname(os.path.abspath(path))
fd, tmp_path = tempfile.mkstemp(dir=dir_name, suffix='.tmp')
try:
with os.fdopen(fd, 'w', encoding='utf-8') as f:
json.dump(data, f)
f.flush()
os.fsync(f.fileno())
os.replace(tmp_path, path) # 原子替换
except:
os.unlink(tmp_path)
raise
关键点:临时文件必须在目标目录中创建,以保证 os.replace 在同一文件系统内是原子的。
with tempfile.namedtemporaryfile(dir='/my/fast/ssd', delete=false) as f:
...
使用 dir 参数可以将临时文件放在指定目录中。
with tempfile.namedtemporaryfile(suffix='.csv', prefix='export_', delete=false) as f:
# 文件名类似 export_xxxxx.csv
...
tempfile.gettempdir() 返回当前系统临时目录。tmpdir 环境变量:在生产环境中,可以通过设置 tmpdir 将临时文件重定向到合适的分区。with 语句确保清理:始终用上下文管理器管理临时资源。lsof 检查未关闭的临时文件。delete=true 并配合 flush() 验证外部读取:在平台允许的情况下测试。tempfile 模块。temporaryfile 或 temporarydirectory 上下文管理器,它们自动清理。namedtemporaryfile,并注意 windows 上的限制。mkstemp 和 mkdtemp 必须手动清理,建议仅在需要精细控制时使用。spooledtemporaryfile 处理大小不确定的数据。tmpdir 环境变量,避免使用系统默认的小分区。try/finally 或上下文管理器。临时文件是程序运行中的“草稿纸”,它们不该被随意丢弃在公共区域,也不该使用可被预测的名字。tempfile 模块就像一个专业的秘书,帮你安全地生成文件、设置权限、并在合适的时候销毁它们。掌握它的正确用法,你就能在数据处理、原子写入、缓存和并发场景中做到既安全又优雅。从今天起,请把手工拼接 /tmp/xxx 的习惯彻底抛弃,让 tempfile 成为你处理临时资源的唯一入口。你的程序将因此更加健壮,也更加安全,再也不用担心那些在 /tmp 中“裸奔”的临时文件带来的隐患。
以上就是python tempfile创建临时文件的安全陷阱与最佳实践详解的详细内容,更多关于python tempfile创建临时文件的资料请关注代码网其它相关文章!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论