6

我一直在尝试找出使用 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()
4

6 回答 6

3

我只想让任何来这里寻找答案的人都知道,Allesandro Molina的伟大图书馆Depot是这个问题的最佳答案。

它解决了命名和复制问题,并将很好地整合到您的 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)

Depot 同时支持MongoDB和AmazonLocalFileStorage的. 而且,至少对于存储在本地和 S3 中的文件,将由.GridFSStorageS3Storagefileiduuid.uuid1()

于 2015-04-20T09:39:58.093 回答
2

我不太了解 Turbogears 以及它是否可以提供任何东西来避免以下情况,但在我看来,这段代码充满了危险。恶意用户可能会覆盖(或创建)Turbogears python 进程具有写入权限的任何文件。

如果asset_dirname/tmpfile.filename../../../../../../../etc/passwd的内容和文件的内容root::0:0:root:/root:/bin/bash呢?在 UNIX 环境中,此代码(权限待定)将以/tmp/../../../../../../../etc/passwd截断模式打开文件,然后将上传文件的内容复制到其中 - 有效地覆盖系统的密码文件并指定没有密码的 root 用户。据推测,Windows 机器也可以做一些令人讨厌的事情。

好的,这是一个极端的例子,它要求 python 运行root(没有人这样做,是吗?)。即使python作为低权限用户运行,之前上传的文件也可以随意覆盖。

总而言之,不要相信用户输入,在这种情况下,用户提供的文件名在file.filename.

于 2010-03-04T02:04:53.957 回答
2

@mhawke - 你是对的,你必须处理它 - 取决于你对文件所做的事情,如果是否存在名称冲突并不重要,例如你只关心某些数据的最新版本,那么可能没有问题,或者如果文件名实际上并不重要,只是文件内容,但它仍然是不好的做法。

您可以在 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 方法将用于整理文件名...

于 2010-03-05T08:07:43.223 回答
0

涡轮齿轮箱不只是带有附加装置的挂架吗?你可以在那里查看帮助:

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

但是,这仍然包含 mhawke 提到的潜在安全漏洞:

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

与上面相同,如果文件名以某种方式存在,../../../../../etc/passwd那么您可以替换该文件...

所以你可以像这样得到实际的文件名:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop())
于 2010-03-04T09:06:35.940 回答
0

Werkzeug 有一个非常好的帮助函数来保护文件名,称为secure_filename。我认为您可以采用和使用它。

于 2010-03-08T10:05:47.603 回答
-1

关于如何去,我会支持已经给出的好的答案。

这是我关于存储文件命名的 2 便士。

确实使用原始名称保存文件可能会导致漏洞。如果有的话,我对原始名称的唯一用途是提示 mime 类型检测。

无论如何,要保存的文件应该被赋予唯一的名称,通过记录身份或类似的东西,并保存在应用程序目录所有者控制的地方,他是普通用户,或者在其他一些存储服务中,如上所述仓库等

这是跨语言良好系统设计的问题:)。

于 2019-09-12T13:20:40.167 回答