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

时间:2012-06-11 11:03:11

标签: python

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

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

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

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')

3 个答案:

答案 0 :(得分:81)

修改:另请参阅Dave Jones' answer:从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()都会失败。

     只有在内核2.6或更高版本上使用NFSv3或更高版本时,才支持

O_EXCL。在未提供NFS O_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!")

答案 1 :(得分:53)

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

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

对于Python 3.2及更低版本(包括Python 2.x),请参阅the accepted answer

答案 2 :(得分:2)

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

import os
if not os.path.exists('file'):
    open('file', 'w').close()