Flask - Нужно ли использовать secure_filename () при загрузке в S3 / Google Cloud? - PullRequest
0 голосов
/ 24 января 2020

В документации Flask для загрузки файлов они рекомендуют использовать secure_filename() для очистки имени файла перед его сохранением.

Вот их пример:

uploaded_file = request.files['file']
if uploaded_file:
    filename = secure_filename(uploaded_file.filename) # <<<< note the use of secure_filename() here
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return redirect(url_for('display_file',
                            filename=filename))

Документация гласит:

Теперь проблема в том, что существует принцип под названием «никогда не доверяйте вводу пользователя» . Это также верно для имени файла загруженного файла. Все представленные данные формы могут быть подделаны, а имена файлов могут быть опасными. На данный момент просто помните: всегда используйте эту функцию для защиты имени файла перед сохранением его непосредственно в файловой системе.

С помощью внешнего хранилища (S3 или Google Cloud) я не буду использовать Flask для хранения файла на веб-сервере. Вместо этого я переименую загружаемый файл (с моим собственным UUID), а затем загружу его в другое место.

Пример:

blob = bucket.blob('prompts/{filename}'.format(filename=uuid.uui4()))
blob.upload_from_string(uploaded_file.read(), content_type=uploaded_file.content_type)

В этом сценарии я прав, что вы делаете это не сначала нужно вызвать secure_filename()?

Казалось бы, поскольку я (а) считываю содержимое файла в строку, а затем (б) используйте мое собственное имя файла, мой вариант использования не уязвим для обхода каталога или мошеннических атак типа команды (например, "../../../../home/username/.bashrc"), но я не уверен на 100%.

1 Ответ

0 голосов
/ 24 января 2020

Вы правы.

Вам нужно использовать функцию secure_filename, только если вы используете значение request.files['file'].filename для построения пути к файлу, предназначенного для вашей файловой системы - например, в качестве аргумента os.path.join.

Поскольку вы используете UUID для имени файла, значение пользовательского ввода в любом случае игнорируется.

Даже без S3 было бы также безопасно НЕ использовать secure_filename, если вы использовали UUID как сегмент имени файла пути к файлу в вашей локальной файловой системе. Например:

uploaded_file = request.files['file']
if uploaded_file:
    file_uuid = uuid.uuid4()
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], file_uuid))
    # Rest of code

В любом случае вы сохраните UUID где-нибудь в базе данных. Сохраняете ли вы изначально предоставленное значение request.files['file'].filename вместе с вашим выбором.

Это может иметь смысл, если вы хотите, чтобы пользователь видел исходное имя файла, когда они загрузили его. В этом случае определенно целесообразно в любом случае пропустить значение через secure_filename, поэтому никогда не бывает ситуации, когда веб-интерфейс отображает список для пользователя, который включает файл с именем ../../../../ohdear.txt


secure_filename строка документа также указывает на некоторые другие функции:

Передайте ему имя файла, и он вернет его защищенную версию. Это имя файла может быть безопасно сохранено в обычной файловой системе и передано: fun c: os.path.join. Возвращаемое имя файла - только строка ASCII для максимальной переносимости. В системах windows функция также гарантирует, что файл не будет назван в честь одного из файлов специального устройства.

>>> secure_filename("My cool movie.mov")
'My_cool_movie.mov'
>>> secure_filename("../../../etc/passwd")
'etc_passwd'
>>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
'i_contain_cool_umlauts.txt'
...