AgentNirmites
AgentNirmites

Reputation: 175

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 [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>

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

Upvotes: 0

Views: 799

Answers (2)

Razenstein
Razenstein

Reputation: 3717

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

Upvotes: 1

Alexandr Tatarinov
Alexandr Tatarinov

Reputation: 4034

One way to do it is using a so-called X-Sendfile. In simple words:

  1. User requests URL to get a protected file (so you need to keep your public and protected files separately, and then either proxy a request to Django for protected files, or serve files directly from the apache/nginx if they are public)
  2. Django view decides which file to return based on URL, and checks user permission, etc.
  3. Django returns an HTTP Response with the 'X-Sendfile' header set to the server's file path
  4. The web server finds the file and returns it to the requester

The setup will be different for nginx and apache, according to my findings you need to install mod_xsendfile for apache, nginx supports it out of the box. Hopefully, that helps, ask any additional questions if needed.

Upvotes: 1

Related Questions