100

我希望根据该文件是否已经存在来写入文件,只有在它不存在时才写入(实际上,我希望继续尝试文件,直到找到不存在的文件)。

以下代码显示了一种潜在攻击者可以插入符号链接的方式,正如本文所建议的那样在文件测试和正在写入的文件之间。如果代码以足够高的权限运行,这可能会覆盖任意文件。

有没有办法解决这个问题?

import os
import errno

file_to_be_attacked = 'important_file'

with open(file_to_be_attacked, 'w') as f:
    f.write('Some important content!\n')

test_file = 'testfile'

try:
    with open(test_file) as f: pass
except IOError, e:

    # Symlink created here
    os.symlink(file_to_be_attacked, test_file)

    if e.errno != errno.ENOENT:
        raise
    else:
        with open(test_file, 'w') as f:
            f.write('Hello, kthxbye!\n')
4

3 回答 3

97

编辑:另见Dave Jones 的回答:从 Python 3.3 开始,您可以使用x标志open()来提供此功能。

原答案如下

是的,但不使用 Python 的标准open()调用。您需要os.open()改用它,它允许您为底层 C 代码指定标志。

特别是,您想使用O_CREAT | O_EXCL. open(2)O_EXCL我的 Unix 系统上的手册页中:

确保此调用创建文件:如果此标志与 一起指定O_CREAT,并且路径名已经存在,open()则将失败。如果未指定,则的行为O_EXCL未定义。O_CREAT

当指定这两个标志时,不遵循符号链接:如果路径名是符号链接,则open()无论符号链接指向何处都会失败。

O_EXCL 只有在内核 2.6 或更高版本上使用 NFSv3 或更高版本时,才在 NFS 上受支持。在不提供 NFSO_EXCL支持的环境中,依赖它来执行锁定任务的程序将包含竞争条件。

所以它并不完美,但 AFAIK 是最接近避免这种竞争条件的方法。

编辑:使用os.open()而不是其他规则open()仍然适用。特别是,如果您想使用返回的文件描述符进行读取或写入,您还需要O_RDONLYO_WRONLYO_RDWR标志之一。

所有O_*标志都在 Python 的os模块中,因此您需要import os使用os.O_CREAT等。

例子:

import os
import errno

flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY

try:
    file_handle = os.open('filename', flags)
except OSError as e:
    if e.errno == errno.EEXIST:  # Failed as the file already exists.
        pass
    else:  # Something unexpected went wrong so reraise the exception.
        raise
else:  # No exception, so the file must have been created successfully.
    with os.fdopen(file_handle, 'w') as file_obj:
        # Using `os.fdopen` converts the handle to an object that acts like a
        # regular Python file object, and the `with` context manager means the
        # file will be automatically closed when we're done with it.
        file_obj.write("Look, ma, I'm writing to a new file!")
于 2012-06-11T11:53:15.560 回答
79

作为参考,Python 3.3'x'在函数中实现了一个新模式open()来覆盖这个用例(仅创建,如果文件存在则失败)。请注意,'x'模式是自行指定的。在 a中使用'wx'results是多余的(如果调用成功,您唯一能做的就是写入文件;如果调用成功,它就不会存在):ValueError'w'

>>> f1 = open('new_binary_file', 'xb')
>>> f2 = open('new_text_file', 'x')

对于 Python 3.2 及以下版本(包括 Python 2.x),请参阅接受的答案

于 2013-08-27T20:26:09.073 回答
-1

如果文件不存在,此代码将轻松创建一个文件。

import os
if not os.path.exists('file'):
    open('file', 'w').close() 
于 2013-03-14T04:19:22.660 回答