如何在生产中保护 django 中的媒体文件?

How to secure media files in django in poduction?

提问人:AgentNirmites 提问时间:5/27/2021 更新时间:5/27/2021 访问量:530

问:

我正在尝试制作一个项目并拥有一些只能由其所有者访问的媒体文件。 在生产中,媒体和静态文件由 apache(或 nginx,但我使用的是 apache)提供。

我一直在寻找一些解决方案,但我无法申请。

在 djangosnippets 网站上,我找到了这段代码,

from mod_python import apache
from django.core.handlers.base import BaseHandler
from django.core.handlers.modpython import ModPythonRequest


class AccessHandler(BaseHandler):
    def __call__(self, req):
        from django.conf import settings

        # set up middleware
        if self._request_middleware is None:
            self.load_middleware()
        # populate the request object
        request = ModPythonRequest(req)
        # and apply the middleware to it
        # actually only session and auth middleware would be needed here
        for middleware_method in self._request_middleware:
            middleware_method(request)
        return request


def accesshandler(req):
    os.environ.update(req.subprocess_env)

    # check for PythonOptions
    _str_to_bool = lambda s: s.lower() in ("1", "true", "on", "yes")

    options = req.get_options()
    permission_name = options.get("DjangoPermissionName", None)
    staff_only = _str_to_bool(
        options.get("DjangoRequireStaffStatus", "on")
    )
    superuser_only = _str_to_bool(
        options.get("DjangoRequireSuperuserStatus", "off")
    )
    settings_module = options.get("DJANGO_SETTINGS_MODULE", None)

    if settings_module:
        os.environ["DJANGO_SETTINGS_MODULE"] = settings_module

    request = AccessHandler()(req)
    if request.user.is_authenticated():
        if superuser_only and request.user.is_superuser:
            return apache.OK
        elif staff_only and request.user.is_staff:
            return apache.OK
        elif permission_name and request.user.has_perm(
            permission_name
        ):
            return apache.OK
    return apache.HTTP_UNAUTHORIZED

但是我无法安装mod_python。请先告诉我该怎么做

我更改了apache的.conf文件,如下所示。

<VirtualHost *:80>



    ServerName podcast.com
    ServerAdmin [email protected]
    DocumentRoot /home/username/Documents/secret_media

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    Alias /static /home/username/Documents/secret_media/static
    <Directory /home/username/Documents/secret_media/static>
        Require all granted
    </Directory>

    Alias /media /home/username/Documents/secret_media/media
    <Directory /home/username/Documents/secret_media/media>
        Require all granted
    </Directory>

    <Directory /home/username/Documents/secret_media/secret_media>
        <Files wsgi.py>
            Require all granted
        </Files>
    </Directory>






    WSGIScriptAlias / /home/username/Documents/secret_media/secret_media/wsgi.py
    WSGIDaemonProcess secret_media python-path=/home/username/Documents/secret_media python-home=/home/username/Documents/secret_media/venv
    WSGIProcessGroup secret_media

    LoadModule auth_basic_module modules/mod_auth_basic.so
    LoadModule authz_user_module modules/mod_authz_user.so



    <Location "/media/images">
        PythonPath /home/username/Documents/secret_media
        PythonOption DJANGO_SETTINGS_MODULE secret_media.settings
        PythonAccessHandler secret_media.wsgi.py #this should point to accesshandler
        SetHandler None
    </Location>



</VirtualHost>

有些设置是重复的,不知道为什么,请解释

Django Apache mod-python

评论


答:

1赞 Alexandr Tatarinov 5/27/2021 #1

一种方法是使用所谓的 X-Sendfile。 简单来说:

  1. 用户请求 URL 来获取受保护的文件(因此您需要将公共文件和受保护文件分开保存,然后向 Django 代理请求受保护的文件,或者直接从 apache/nginx 提供文件(如果它们是公共的)
  2. Django 视图根据 URL 决定返回哪个文件,并检查用户权限等。
  3. Django 返回一个 HTTP 响应,其中 'X-Sendfile' 标头设置为服务器的文件路径
  4. Web 服务器找到该文件并将其返回给请求者

nginx 和 apache 的设置会有所不同,根据我的发现,您需要为 apache 安装 mod_xsendfile,nginx 支持开箱即用。希望这会有所帮助,如果需要,请提出任何其他问题。

评论

0赞 AgentNirmites 5/28/2021
非常感谢您的回答。我理解逻辑,但不知道如何实现。
1赞 Razenstein 5/27/2021 #2

X-发送文件

我不知道你上面使用的方法,但我一直在使用mod_xsendfile来做同样的事情。原理是什么:对 url 的请求由检查访问权限的视图处理......该视图返回一个带有键“X-Sendfile”和文件...这会触发 Apache 返回以提供媒体文件。

我只是向你展示代码而不测试语法....如果有什么不清楚的地方,请询问

阿帕奇 httpd.conf

LoadModule xsendfile_module modules/mod_xsendfile.so

在 Apache 中删除通常的“别名媒体...”

Apache httpd-vhosts.conf 文件

<VirtualHost *:80>
    
    # ... all your usual Django Config staff


    # remove the usual alias /media/  
    # Alias /media/ d:/WEBSPACES/dieweltdahinter_project/media/

    XSendFile on
    XSendFilePath D:/your_path/media/
    <Directory "D:/your_path/media">
           Order Deny,Allow
           Allow from all
    </Directory>
</VirtualHost>

urls.py

urlpatterns = [
     .....,

   re_path(r'^media/(?P<folder>[A-Za-z0-9\-_]+)/(?P<filename>[A-Za-z0-9\-_]+).(?P<extension>[A-Za-z0-9]+)\/?$', app_views.media_xsendfile, name='media-xsendfile'),
  
     .....
]

views.py


# add decorators to manage access 
#
def media_xsendfile(request, folder='', filename=None, extension=None): 
 
    # add an kind of user check ....
   
    # only server certain type of files
    if extension in ('jpg', 'png', 'gif'):
        response['Content-Type'] = 'image/'+extension
    elif extension == 'mp3':
        response['Content-Type'] = 'audio/mpeg'
    else:
        return

    if not folder == '':
        folder = '/'+folder+'/'

   response = HttpResponse()
   # this is the part of the response that Apache reacts upon:
   response['X-Sendfile'] = smart_str(settings.MEDIA_ROOT + folder + filename + "." + extension)
   # need to handover an absolute path to your file to Apache!!

   return response

评论

0赞 AgentNirmites 5/28/2021
非常感谢,我现在已经解决了这个问题。如果这里有人有类似的问题,您可能需要获取 mod_xsendfile.so 模块,您可以在此处获得: tn123.org/mod_xsendfile 下载 .c 文件并键入命令 要执行该命令,您还需要让 apache-dev 工具正常工作。如果没有,它会要求得到它。顺便说一句,我正在使用 Ubuntu 20.04apxs -cia mod_xsendfile.c