Вопрос по python – Безопасно создайте файл, если и только если он не существует с python

82

Я хочу записать в файл, основываясь на том, существует ли этот файл или нет, только запись, если он еще не существует (на практике я хочу продолжать пробовать файлы, пока не найду тот, который не существует).

В следующем коде показан способ, которым потенциальный злоумышленник может вставить символическую ссылку, как предлагается вэта почта между проверкой файла и записываемого файла. Если код запускается с достаточно высокими разрешениями, это может перезаписать произвольный файл.

Есть ли способ решить эту проблему?

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 Здесь это не поможет. Konrad Rudolph
Проверьте атомарное письмо с Pythonstackoverflow.com/questions/2333872/… Mikko Ohtamaa
Не могли бы вы записать файл во временную папку, а затем выполнить команду копирования без перезаписи? Eric
Ах хорошо. Я понял, в чем дело ... вы пишете ТОЛЬКО если файл существует? Mikko Ohtamaa

Ваш Ответ

3   ответа
2

import os
if not os.path.exists('file'):
    open('file', 'w').close() 
,
Это правда. Это называется TOCTOU!
Если другой процесс создает и записывает в файл послеif Заявление, этот код очистит файл.
Да, это будет. Важным моментом в этом вопросе был аспект безопасности. Проблема заключается в том, что между определением наличия файла и его использованием или созданием что-то может измениться, что приведет к плохому результату (как в первоначальном вопросе). Henry Gomersall
52

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

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

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

Подойдет - хотя придется подождать, пока я не вернусь к компьютеру немного позже.
Вы используете Python 3.2; "х" Режим в 3.3 и выше, но это кроссплатформенный. Между прочим, вы используете только "x" "; вместо "wx" - режим записи избыточен, так как единственное, что вы можете сделать с файлом, это все равно записать в него
Смотрите комментарий выше :-)
Python 3.6:ValueError: must have exactly one of create/read/write/append mode
Хорошее предложение. К сожалению, это, похоже, только для 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'
81

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

Похожие вопросы