关于python:限制对Django中私有文件下载的访问

Restricting access to private file downloads in Django

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

实现此目标的最佳方法是什么?有什么想法吗?


不幸的是,由于django并非旨在提供文件服务,因此@Mikko的解决方案实际上无法在生产环境中运行。在生产环境中,文件需要由您的HTTP服务器(例如apache,nginx等)提供服务,而不是由您的应用程序/ django服务器(例如uwsgi,gunicorn,mod_wsgi等)提供服务。

这就是为什么限制文件访问并非易事的原因:HTTP服务器需要一种方法来询问应用程序服务器是否可以将文件提供给请求它的特定用户。如您所知,这需要同时修改您的应用程序和http服务器。

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

This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated files, but does not want to serve the actual bytes of the file itself. i.e. as serving large files is not what Django is made for.

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

2018更新:请注意,django-sendfile似乎不再被维护;也许它应该仍然可以正常工作,但是,如果您想要具有类似功能的更现代的软件包,请参阅https://github.com/edoburu/django-private-storage,如评论员@ surfer190所建议。尤其要确保您实现了"优化大文件传输"部分;实际上,您不仅所有大型文件都需要此传输。


如果您仅需要适度的安全性,我的方法将是:

1)用户上载文件时,请为其生成难以猜测的路径。例如,您可以为/ static文件夹中的每个上载文件创建一个具有随机生成名称的文件夹。您可以使用以下示例代码非常简单地执行此操作:

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

通过这种方式,很难猜测其他用户文件的存储位置。

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

uploads(id, user_id, file_path)

3)使用模型中的FileFields属性,以这种方式限制对文件的访问:

1
2
3
4
5
6
7
8
9
10
11
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)

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

文档中的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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...
    }
}

该文件的访问方式如下:

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

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

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

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

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

  • 确保文件位于特殊的专用文件夹文件夹中,并且不会通过Django的MEDIA_URLSTATIC_URL

    公开

  • 写一个视图,将

    • 在您的视图逻辑中检查用户是否有权访问文件

    • 使用Python的open()

      打开文件

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

例如,请参见download()