当且仅当python不存在时才安全地创建文件

Hen*_*all 88 python

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

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

有什么方法可以解决这个问题吗?

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')
Run Code Online (Sandbox Code Playgroud)

me_*_*and 84

编辑:另请参阅Dave Jones的回答:从Python 3.3中,您可以使用x标志open()来提供此功能.

原答案如下

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

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

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

如果指定了这两个标志,则不遵循符号链接:如果pathname是符号链接,则open()无论符号链接指向何处都会失败.

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

所以它并不完美,但AFAIK是你能够避免这种竞争条件的最接近的.

编辑:使用的其他规则os.open()代替open()仍然适用.特别是,如果你想使用返回的文件描述符进行读取或写入,你需要的一个O_RDONLY,O_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!")
Run Code Online (Sandbox Code Playgroud)

  • +1 显然是正确的答案。我个人很想知道实际上有多少人对 NFS 警告有问题——我(可能是鲁莽地)认为它是一个过时的环境,我的代码不应该在上面运行。 (2认同)
  • @zigg:NFSv3是从1995年开始的,因此将旧版本视为过时似乎是公平的. (2认同)

Dav*_*nes 59

作为参考,Python 3.3 'x'open()函数中实现了一个新模式来覆盖这个用例(仅创建,如果文件存在则失败).请注意,'x'模式是单独指定的.使用'wx'的结果ValueError'w'是多余的(你可以,如果调用成功被写入反正文件唯一要做的事情;它不会存在,如果调用成功)

>>> f1 = open('new_binary_file', 'xb')
>>> f2 = open('new_text_file', 'x')
Run Code Online (Sandbox Code Playgroud)

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

  • 你正在使用python 3.2; 'x'模式在3.3及以上,但它是跨平台的.顺便说一句,你只使用'x'而不是'wx' - 写模式是多余的,因为你可以用文件做的唯一事情是写入它 (4认同)
  • 打开现有文件进行写入是合理的,但是'x'模式的全部要点是*当且仅当文件不存在时才打开文件*,当文件确实存在时失败并显示错误。这就是为什么它带有'w'标志是多余的;如果成功,则保证文件为空(因此从其中读取的内容很少:)。 (2认同)