Загрузка файлов с помощью Turbogears 2 - PullRequest
5 голосов
/ 04 марта 2010

Я пытался выработать «лучший метод» для управления загрузкой файлов с помощью Turbogears 2 и до сих пор не нашел ни одного примера. Я нашел способ на самом деле загрузить файл, но я не уверен, насколько он нам надежен.

Кроме того, что было бы хорошим способом получить имя загруженных файлов?

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        file.filename.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = self.request.params["file"].filename 
    permanent_file.close()

Итак, при условии, что я правильно понимаю, позволит ли что-то подобное избежать основной проблемы с именами? id = UUID.

    file = request.POST['file']
    permanent_file = open(os.path.join(asset_dirname,
        id.lstrip(os.sep)), 'w')
    shutil.copyfileobj(file.file, permanent_file)
    file.file.close()
    this_file = file.filename
    permanent_file.close()

Ответы [ 5 ]

2 голосов
/ 20 апреля 2015

Я просто хочу, чтобы любой, кто приходит сюда в поисках ответов, знал, что Великолепная библиотека Аллесандро Молина Депо представляет собой лучший ответ на этот вопрос.

Он решает проблемы как с именами, так и с копированием, и прекрасно вписывается в ваше приложение TurboGears. Вы можете использовать его с MongoDB GridFS, как в этом примере:

from depot.manager import DepotManager

# Configure a *default* depot to store files on MongoDB GridFS
DepotManager.configure('default', {
    'depot.backend': 'depot.io.gridfs.GridFSStorage',
    'depot.mongouri': 'mongodb://localhost/db'
})

depot = DepotManager.get()

# Save the file and get the fileid
fileid = depot.create(open('/tmp/file.png'))

# Get the file back
stored_file = depot.get(fileid)
print stored_file.filename
print stored_file.content_type

или вы можете легко создавать поля вложений в ваших SQLAlchemy моделях, например:

from depot.fields.sqlalchemy import UploadedFileField

class Document(Base):
    __tablename__ = 'document'

    uid = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(Unicode(16), unique=True)

    content = Column(UploadedFileField)

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

doc = Document(name=u'Foo', content=open('/tmp/document.xls'))
DBSession.add(doc)

Депо поддерживает как LocalFileStorage, MongoDB GridFSStorage, так и Amazon S3Storage. И, по крайней мере, для файлов, хранящихся локально и на S3, fileid будет сгенерировано uuid.uuid1().

1 голос
/ 05 марта 2010

@ mhawke - вы правы, вы должны справиться с этим - зависит от того, что вы делаете с файлом, если не имеет значения, есть ли конфликт имен, например, вам нужна только последняя версия некоторых данных Theres, вероятно, не проблема, или если имя файла на самом деле не важно, просто содержимое файла, но это все еще плохая практика.

Вы можете использовать именованный временный файл в директории tmp, а затем переместить файл после проверки в его окончательное местоположение. Или вы можете проверить, что имя файла еще не существует, например:

file.name = slugify(myfile.filename)
name, ext = os.path.splitext(file.name)
while os.path.exists(os.path.join(permanent_store, file.name)):
    name += '_'
    file.name = name + ext

raw_file = os.path.join(permanent_store, file.name)

Метод slugify будет использован для очистки имени файла ...

1 голос
/ 04 марта 2010

Я не очень разбираюсь в Turbogears и в том, может ли он что-нибудь предоставить, чтобы избежать следующего, но мне кажется, что этот код чреват опасностью. Злоумышленник может перезаписать (или создать) любой файл, к которому у процесса Python Turbogears есть доступ для записи.

Что если asset_dirname равно /tmp, содержимое file.filename равно ../../../../../../../etc/passwd и содержимое файла root::0:0:root:/root:/bin/bash? В среде UNIX этот код (ожидающие разрешения) откроет файл /tmp/../../../../../../../etc/passwd в режиме усечения, а затем скопирует в него содержимое загруженного файла - эффективно перезаписывая файл пароля вашей системы и указывая пользователя root без пароля. Предположительно, существуют неприятные вещи, которые можно сделать и на машине с Windows.

ОК, это крайний пример, который требует, чтобы python работал как root (никто этого не делает, не так ли?). Даже если python работает как пользователь с низким уровнем привилегий, ранее загруженные файлы могут быть перезаписаны по желанию.

Подводя итог, не доверяйте пользовательскому вводу, в этом случае предоставленное пользователем имя файла доступно в file.filename.

0 голосов
/ 08 марта 2010

Werkzeug имеет очень хорошую вспомогательную функцию для защиты имен файлов, которая называется secure_filename . Я думаю, что вы можете принять и использовать его.

0 голосов
/ 04 марта 2010

Разве турбогенераторы - это просто пилоны с дополнениями?Вы можете проверить помощь там:

http://wiki.pylonshq.com/display/pylonsdocs/Form+Handling#file-uploads

Однако, она все еще содержит потенциальную уязвимость, о которой упоминал Мхавке:

os.path.join(permanent_store, myfile.filename.lstrip(os.sep))

То же, что и выше, еслиимя файла как-то было ../../../../../etc/passwd, тогда вы могли бы заменить этот файл ...

Таким образом, вы могли просто получить фактическое имя файла примерно так:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop())
...