djangoapachemod-python

How to secure media files in django in poduction?


I am trying to make a project and have some media files which should only be accessed by their owner. In production, media and static files are served by apache (or nginx but I am using apache).

I was looking for some solutions and I am not able to apply.

On djangosnippets website, I found this code,

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

But I am not able to install mod_python. Please tell me how to do that first

And I changed my .conf file for apache which is as below.

<VirtualHost *:80>



    ServerName podcast.com
    ServerAdmin srpatel980@gmail.com
    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>

There are some settings which are repeated, don't know why, please explain


Solution

  • X-Sendfile

    I do not know about the method you use above, but I have been using mod_xsendfile to do the same. Whats the principle: request to a url is handeled by a view that checks access rights ... the view returns a response with a key "X-Sendfile" & file ... this triggers Apache on the way back to serve the media file.

    I just show you the code without testing syntax .... please ask if something is not clear

    Apache httpd.conf

    LoadModule xsendfile_module modules/mod_xsendfile.so
    

    in Apache remove the usual "alias media ..."

    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