Безопасно создайте файл, если и только если он не существует с 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')
 Mikko Ohtamaa11 июн. 2012 г., 13:23
Ах хорошо. Я понял, в чем дело ... вы пишете ТОЛЬКО если файл существует?
 Mikko Ohtamaa11 июн. 2012 г., 13:19
Проверьте атомарное письмо с Pythonstackoverflow.com/questions/2333872/…
 Konrad Rudolph11 июн. 2012 г., 13:19
@Mikko Здесь это не поможет.
 Eric11 июн. 2012 г., 13:33
Не могли бы вы записать файл во временную папку, а затем выполнить команду копирования без перезаписи?

Ответы на вопрос(3)

'x' режим вopen() функция для покрытия этого варианта использования (только создание, сбой, если файл существует). Обратите внимание, что'x' Режим указывается самостоятельно. С помощью'wx' приводит кValueError как'w' является избыточным (единственное, что вы можете сделать, если вызов успешен, так или иначе, это запись в файл; он не может существовать, если вызов успешен):

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

Для Python 3.2 и ниже (включая Python 2.x), пожалуйста, обратитесь кпринятый ответ.

 14 июл. 2017 г., 19:46
Смотрите комментарий выше :-)
 14 июл. 2017 г., 19:44
Python 3.6:ValueError: must have exactly one of create/read/write/append mode
 14 июл. 2017 г., 19:50
Подойдет - хотя придется подождать, пока я не вернусь к компьютеру немного позже.
 05 мар. 2015 г., 17:28
Хорошее предложение. К сожалению, это, похоже, только для POSIX (не работает в Windows):Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32 >>> open("c:/temp/foo.csv","wx") ValueError: invalid mode: 'wx'
 08 мар. 2015 г., 14:19
Вы используете Python 3.2; "х" Режим в 3.3 и выше, но это кроссплатформенный. Между прочим, вы используете только "x" "; вместо "wx" - режим записи избыточен, так как единственное, что вы можете сделать с файлом, это все равно записать в него

import os
if not os.path.exists('file'):
    open('file', 'w').close() 
,
 27 мар. 2016 г., 07:47
Это правда. Это называется TOCTOU!
 06 апр. 2017 г., 12:40
Если другой процесс создает и записывает в файл послеif Заявление, этот код очистит файл.
 Henry Gomersall14 мар. 2013 г., 10:49
Да, это будет. Важным моментом в этом вопросе был аспект безопасности. Проблема заключается в том, что между определением наличия файла и его использованием или созданием что-то может измениться, что приведет к плохому результату (как в первоначальном вопросе).
Решение Вопроса

Edit: Смотрите такжеДейв Джонс & apos; ответ: из Python 3.3, вы можете использоватьx флаг дляopen() чтобы обеспечить эту функцию.

Original answer below

Да, но без использования стандарта Pythonopen() вызов. Вам нужно будет использоватьos.open() вместо этого, что позволяет вам указывать флаги для базового кода C.

В частности, вы хотите использоватьO_CREAT | O_EXCL, Со страницы руководства дляopen(2) подO_EXCL в моей системе Unix:

Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname already exists, then open() will fail. The behavior of O_EXCL is undefined if O_CREAT is not specified.

When these two flags are specified, symbolic links are not followed: if pathname is a symbolic link, then open() fails regardless of where the symbolic link points to.

O_EXCL is only supported on NFS when using NFSv3 or later on kernel 2.6 or later. In environments where NFS O_EXCL support is not provided, programs that rely on it for performing locking tasks will contain a race condition.

Так что это не идеально, но AFAIK это самое близкое, что вы можете избежать, чтобы избежать этого состояния гонки.

Редактировать: другие правила использованияos.open() вместоopen() все еще применяется. В частности, если вы хотите использовать возвращенный дескриптор файла для чтения или записи, вам понадобится один изO_RDONLY, O_WRONLY или жеO_RDWR флаги тоже.

ВсеO_* флаги в Pythonos модуль, так что вам нужноimport os и использоватьos.O_CREAT и т.п.

Example:
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!")
 11 июн. 2012 г., 14:20
@zigg: NFSv3 с 1995 года, поэтому кажется справедливым считать старые версии устаревшими.
 11 июн. 2012 г., 13:59
+1 за заведомо правильный ответ. Мне лично любопытно узнать, сколько людей на самом деле имеют проблемы с предупреждением NFS. Я (возможно, безрассудно) отвергаю его как устаревшую среду, в которой мой код никогда не должен выполняться.
 11 июн. 2012 г., 14:38
Лично я бы больше беспокоился о версии ядра. Если вы запускаете что-либо, хоть немного напоминающее современную систему, у вас не должно быть проблем, но RHEL 3 (все еще в расширенной фазе поддержки), например, работает на ядре 2.4. Кроме того, я не исследовал, обеспечивают ли они атомарную запись в Windows на FAT или NTFS, что является потенциально серьезным ограничением.
 Henry Gomersall11 июн. 2012 г., 14:42
@me_and Страница Python наopen flag constants предполагает, что это прекрасно работает с Windows. Я попробую это в ближайшее время!
 11 июн. 2012 г., 15:36
Правда, но я нигде не видел (включаяMSDN) что явно говорит, что эти флаги даютatomic создание файла. Возможно, я излишне параноидален, но я бы хотел увидеть это «атомное». Ключевое слово, прежде чем доверять этому для всего, что критично для безопасности.

Ваш ответ на вопрос