How to serve files with access control in Django?

6

When studying Django, the typical way to handle file upload was to create a media folder on the server - setting MEDIA_ROOT and MEDIA_URL to settings.py - where every uploaded file would go. In models, a FileField or ImageField is created, whose upload_to is relative to MEDIA_ROOT . In production, the webserver itself (eg Apache) is expected to serve content from the /media URL, leaving only dynamic content to Django.

So far so good, the problem is that I would like to restrict the access of uploaded files to logged in users, according to some access control criteria. What is the right way to do this? Is Django or Apache the responsibility of doing this access control? (and if it is from Apache, how do you make use of the Django permissions system?)

For reference, here is my virtual host (use Django 1.4.14):

Alias /media/ /var/www/vhosts/example.com/httpdocs/media/
Alias /static/ /var/www/vhosts/example.com/httpdocs/static/

WSGIDaemonProcess exemplo threads=15 processes=5
WSGIProcessGroup exemplo
WSGIScriptAlias / /var/www/vhosts/example.com/exemplo.wsgi

P.S. For performance reasons, I would prefer that not all as% of% have access control - the case of user uploaded files that are universally accessible is more frequent than the case where the file is restricted. I could designate a subfolder for them (eg /media ) and let Django take care of that folder, but I do not know how to do this with only /media/restrito and Alias . Maybe I need WSGIScriptAlias too, I do not know ... Anyway, I'm well lost, any reference on the subject would be very welcome.

    
asked by anonymous 21.05.2015 / 21:55

2 answers

2

One solution I found to serve (non-static) files is using X-Sendfile . Basically, the view of the application (in the Django case) verifies that the user is logged in and sends the request with a header (for Apache or Nginx) stating that it is authorized to download.

  

This feature is documented at this link:    link

In Django, settings:

import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DIR_PROTEGIDO = os.path.join(settings.BASE_DIR, 'protegido')

views:

def arquivo(request):  
    # verifica se o usuário está logado        
    arquivo = '.../arquivo.jpg'
    response = HttpResponse()
    response['Content-Type']=''
    response['X-Sendfile']= os.path.join(settings.DIR_PROTEGIDO, arquivo)
    return response

And in Apache:

XSendFile on
XSendFilePath "//arquivos/protegidos"
<Directory "//arquivos/protegidos">
    Order Deny,Allow
    Allow from all
</Directory>

I did not get to use it and I do not know if it is efficient, but from what I read it seems to be a good solution to serve files (NOT static) with prior authorization, whenever someone tries to access the URL of the file without the last header by Django will fail, in which case view will be intermediate in this process.

References I found:

Solution in Django:

20.06.2015 / 23:53
1

I have two suggestions. The first one uses more server resources and guarantees more privacy, and the second one, which is used by Facebook to store images, performs better, but uses altematic URL pattern.

CASE 1: Use language (or server) to restrict user-based static file access

  • Place the files in a normally unreachable location, for example, a folder above its equivalent of www or public_html
  • Use your programming language, where you have full control to know which user is authenticated, for when a URL is accessed, it checks the user and, if allowed, reads the private image and exposes it.
  • CASE 2: Store image in accessible place by anyone, but URL difficult to predict

  • Place the image in a place accessible to anyone, but it must have a very complex URL.
  • Do not use sequential number! .
  • Simple md5 is not random enough, also do not use .
  • Store this random URL and just view it for users you want to have access to.
  • My recommendation: If in doubt, use CASE 1. CASE 2 is only interesting in more peculiar cases, such as Facebook. Another CASO 2 situation is to allow access to files without authentication, only with URL sent by email, common in emails for billing tickets.

        
    20.06.2015 / 23:12