Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restricting access to private file downloads in Django

Tags:

I have multiple FileFields in my django app, which can belong to different users. I am looking for a good way to restrict access to files for user who aren't the owner of the file.

What is the best way to achieve this? Any ideas?

like image 670
dease Avatar asked Jan 27 '15 09:01

dease


People also ask

How do I restrict users in Django?

¶ Django admin allows access to users marked as is_staff=True . To disable a user from being able to access the admin, you should set is_staff=False .

What is manage file in Django?

This document describes Django's file access APIs for files such as those uploaded by a user. The lower level APIs are general enough that you could use them for other purposes. If you want to handle “static files” (JS, CSS, etc.), see How to manage static files (e.g. images, JavaScript, CSS).

How do I upload and download in Django?

Add template folder in the Django folder and provide its path in django_folder > settings.py. Add file named as urls.py in the app folder and provide its path in django_project > urls.py. Add the function in app_folder > views.py. Provide the URL path in app> urls.py to the functions created in views.py.


2 Answers

Unfortuanately @Mikko's solution cannot actually work on a production environment since django is not designed to serve files. In a production environment files need to be served by your HTTP server (e.g apache, nginx etc) and not by your application/django server (e.g uwsgi, gunicorn, mod_wsgi etc).

That's why restricting file acccess is not very easy: You need a way for your HTTP server to ask the application server if it is ok to serve a file to a specific user requesting it. As you can understand thiss requires modification to both your application and your http server.

The best solution to the above problem is django-sendfile (https://github.com/johnsensible/django-sendfile) which uses the X-SendFile mechanism to implement the above. I'm copying from the project's description:

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.

To understand more about the senfile mechanism, please read this answer: Django - Understanding X-Sendfile

2018 Update: Please notice that django-sendfile does not seem to be maintained anymore; probably it should still be working however if you want a more modern package with similar functionality take a look at https://github.com/edoburu/django-private-storage as commenter @surfer190 proposes. Especially make sure that you implement the "Optimizing large file transfers" section; you actuallyu need this for all transfers not only for large files.

2021 Update: I'm returning to this answer to point out that although it hasn't been updated for like 4 years, the django-sendfile project still works great with the current Django version (3.2) and I'm actually using it for all my projects that require that particular functionality! There is also an actively-maintained fork now, django-sendfile2, which has improved Python 3 support and more extensive documentation.

like image 125
Serafeim Avatar answered Nov 17 '22 00:11

Serafeim


If you need just moderate security, my approach would be the following:

1) When the user uploads the file, generate a hard to guess path for it. For example you can create a folder with a randomly generated name for each uploaded file in your /static folder. You can do this pretty simply using this sample code:

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

In this way it will be very hard to guess where other users' files are stored.

2) In the database link the owner to the file. An example schema can be:

uploads(id, user_id, file_path)

3) Use a property for your FileFields in the model to restrict access to the file, in this way:

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) 
like image 36
sc3w Avatar answered Nov 17 '22 00:11

sc3w