问题 在Django中限制对私有文件下载的访问


我的django应用程序中有多个FileFields,可以属于不同的用户。 我正在寻找一种限制不是文件所有者的用户访问文件的好方法。

实现这一目标的最佳方法是什么?有任何想法吗?


6835
2018-01-27 09:14


起源

我建议你关注 如何通过保护仅经过身份验证的用户可以看到的URL来使文件成为私有文件 但你必须实现自己的方法来限制其他用户不要访问...! - Raja Simon


答案:


不幸的是,自从Django以来,@ Mikko的解决方案实际上无法在生产环境中工作 不是为提供文件而设计的。在生产环境中,文件需要由HTTP服务器(例如apache,nginx等)提供服务  由您的application / django服务器(例如uwsgi,gunicorn,mod_wsgi等)。

这就是限制文件访问的原因 不是很容易:您需要一种方法让HTTP服务器向应用程序服务器询问是否可以将文件提供给请求它的特定用户。您可以理解这需要修改您的应用程序  你的http服务器。

上述问题的最佳解决方案是django-sendfile(https://github.com/johnsensible/django-sendfile)它使用X-SendFile机制来实现上述功能。我正在复制项目的描述:

这是用于将文件发送到Web客户端的Web服务器特定方法的包装器。当Django需要检查权限相关文件但不想提供文件本身的实际字节时,这很有用。即服务大文件不是Django的用途。

要了解有关senfile机制的更多信息,请阅读以下答案: Django - 了解X-Sendfile

2018年更新:请注意,django-sendfile似乎不再维护;可能它仍然应该工作,但如果你想要一个具有类似功能的更现代的包看看 https://github.com/edoburu/django-private-storage 作为评论者@ surfer190提议。特别要确保实现“优化大文件传输”部分;你实际上需要这个所有传输不仅适用于大文件。


8
2018-01-27 09:42



虽然,django-sendfile是一个很好的包。如果您使用,它的自定义代码较少 github.com/edoburu/django-private-storage - surfer190
@ surfer190你提到的软件包现在似乎是一个很好的解决方案因为django-sendfile似乎不再维护了:(我会更新答案也包括这个软件包。但是请注意,当我第一次回答问题时django-private - 存储实际上并不存在! - Serafeim


如果您需要中等安全性,我的方法如下:

1)当用户上传文件时,为其生成难以猜测的路径。例如,您可以为/ static文件夹中的每个上载文件创建一个随机生成名称的文件夹。您只需使用以下示例代码即可完成此操作:

file_path = "/static/" + os.urandom(32).encode('hex') + "/" + file_name

通过这种方式,很难猜出其他用户的文件存储在哪里。

2)在数据库中将所有者链接到文件。示例模式可以是:

uploads(id, user_id, file_path)

3)使用模型中FileFields的属性来限制对文件的访问,这样:

class YourModel(models.Model)
    _secret_file = models.FileField()

    def get_secret_file(self):
        # check in db if the user owns the file
        if True:
            return self._secret_file
        elif:
            return None # or something meaningful depanding on your app

    secret_file = property(get_secret_file)

2
2018-01-27 16:54



非常感谢非常有帮助 - soField


通常,您不会通过Apache,Nginx或您正在使用的任何Web服务器直接通过正常的静态文件路由私有文件。而是编写一个处理权限检查的自定义Django视图,然后将该文件作为流式下载返回。

  • 确保文件位于特殊的私人文件夹文件夹中,而不是通过Django公开 MEDIA_URL要么 STATIC_URL

  • 写一个会有的观点

    • 检查用户是否可以访问视图逻辑中的文件

    • 用Python打开文件 open()

    • 返回HTTP响应,该响应将文件的句柄作为参数 http.HttpResponse(_file, content_type="text/plain")

例如见 download()  这里


1
2018-01-27 09:35





这最好由服务器处理,例如nginx的 安全链接模块 (nginx必须用。编译 --with-http_secure_link_module

文档中的示例:

location /some-url/ {
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr some-secret";

    if ($secure_link = "") {
        return 403;
    }

    if ($secure_link = "0") {
        return 410;
    }

    if ($secure_link = "1") {
        // authorised...
    }
}

该文件将被访问如下:

/some-url/some-file?md5=_e4Nc3iduzkWRm01TBBNYw&expires=2147483647

(这将是有时间限制的并且绑定到该IP地址处的用户)。

生成令牌以传递给用户将使用如下内容:

echo -n 'timestamp/some-url/some-file127.0.0.1 some-secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =

0
2017-10-02 18:06