Can I use Django to prevent direct access to an image file? - python

I'd like to prevent my web users from simply right clicking an image and copying/sharing the URL. Certain authenticated users have access to certain images, and I'd like to enforce this as much as possible. Non authenticated users should have no access to image files.
It's generally recommended to avoid storing/fetching images from a DB, due to performance issues, from what I have read.
I've considered having a function that reads the file (server side, in python) and inserts it into the webpage (base64 encoded, possibly, or some other way) in the Django view functions. Combined with an .htaccess file that denies external requests, this would likely work, but it seems like it'd be resource intensive.
Is there any other options for enforcing this rule? I realize users can screenshot, save images, etc, but it's my responsibility to enforce these restrictions as much as possible, what are my best options?
edit: I have no experience using a CDN, but would be willing to use this if it's a viable option that covers these needs.

I'll bite.
Session Middleware - not elegant, but it will work
You'll want the images you don't want served publicly to not be served through your standard apache/django static files config.
your session middleware can then check all incoming requests for the path and if the path is your image directory (such as /privateimg/) and the user is not authenticated you can bounce them back out or replace it inline with another image (such as one that has a watermark).
You can check out the django docs on how session middleware works https://docs.djangoproject.com/en/dev/topics/http/sessions/
People can still pass your links around, but only authenticated users can actually see the contents of said links (called gating your content)
To elaborate:
settings.py
GATED_CONTENT = (
'/some_content_dir/', # This is a directory we want to gate
'.pdf', # maybe we want to gate an entire content type
)
MIDDLEWARE_CLASSES = (
... # Out of the box middleware...blah blah
'yourapp.somemodule.sessionmiddleware.GatedContent',
)
Then you have the following app structure
yourapp
|-somemodule
|-sessionmiddleware.py
Now to the meat (yum!)
sessionmiddleware.py
class GatedContent(object):
"""
Prevents specific content directories and types
from being exposed to non-authenticated users
"""
def process_request(self, request):
path = request.path
user = request.user # out of the box auth, YMMV
is_gated = False
for gated in settings.GATED_CONTENT:
if path.startswith(gated) or path.endswith(gated):
is_gated = True
break
# Validate the user is an authenticated/valid user
if is_gated and not user.is_authenticated():
# Handle redirect

You might be interested in XSendfile.
This is most [elegant and] performance choice IMO: actual files will be served by you webserver, while access control to this files will be done using your Django app.
You may google for "django xsendfile", there are lot of useful posts.

Related

How to hide an external url from my user in a Django project?

I have a model in my django project that uses the UrlField to keep external urls files. I use the #login_required in my view to prevent anonymous users to get access to this content. But, in this case, a logged-in user can retrieve this information and post anywhere for everyone to use.
My intention was to create a specific path on my urls.py, with the model id as one of the parameters, and in the view called by this url it would fetch the content in the UrlField and serve the user without display any external link. Something like a proxy or mask for external urls.
Does anyone know how to do this?
I've tried to use HttpResponseRedirect, but in the end the browser keep showing the url I want to hide.
you have to make your statics files server talk with django, maybe with this
https://github.com/johnsensible/django-sendfile

Best approach for loading settings that can be modified in Flask

Hello all fellow StackOverFlow'ers,
I'm making an app in Flask that runs depending on settings that can be changed by administrator via a POST request in their admin-panel,
Actually, the only two things I came up with for doing this is using os.environ.get (Environment variables) [which i'm using now] or insert it to a PostreSQL Database config table and load it up
Anyway I will be storing settings such as a couple of API_URLs and their API_KEY, and some Conditions of checking like a success value where if condition in text .. else is applied to ... where admin can change them via the panel
I'm looking for the best performant approach for doing such thing.
Best regards.
If you're looking for the changes to only apply on a per-user basis (changes made by the admin only affect the admin), check out Flask's sessions. It works like a dictionary, but stores information in a cookie in the user's browser that can be programmatically accessed by Flask. Be warned that this data is stored in plain-text in the user's browser, so don't store anything sensitive here.
On the other hand, if you're looking for changes made by the admin to affect everybody visiting the website, you may just be able to store the settings in a variable, update them when the admin makes changes, and read them when responding to a request. If you want these settings to persist through a server restart, however, you'll need to write them to disk and then load them on server restart and save them to disk when they're changed. If this is a production-grade app and needs to be able to scale, I personally recommend using an SQLite file to store settings (or a SQL database if it really needs to scale), but this is a personal preference of mine. If this is just a personal app, storing settings in text files would be just fine.
Hope this helps!

Are there any ways other than authentication to tell the users apart?

In my app, the user can select a Youtube video that will be downloaded to MEDIA_ROOT folder and then also made available for the user to download.
Whenever the user chooses another video to download, the previous one is deleted from MEDIA_ROOT. So at any given moment there is only one video sitting in the MEDIA_ROOT folder for a particular user.
Is there any way - apart from implementing user authentication and associating the downloaded files with a user through foreign key, which I feel is an overkill for only this task - of telling the users apart whenever such download request is being made, so that one user's request does not cause the deletion of the file downloaded by some other user (as all the files sit in the same MEDIA_ROOT folder)?
Assuming you have some sort of web server, you can create custom links that redirect through your web server and pass IP information, etc, so you can distinguish a user before one downloads a video. That is certainly one way of doing it without authentication and since the app/platform that tracks user data is in-house you don't have to worry about foreign keys, etc.
You can use cookies to uniquely identify users. Web browsers will keep sending that cookie value to your web server for as long as the web browser's cookie store is not cleared. Make sure to generate an hard to guess value for the web browser to store and you to identify with so that one cannot bruteforce that value and get access to data meant for other users. The common way is to generate say a 32 characters string from a CSPRNG.
You can have a go at this question to find out how to set cookies in Python Django: Django Cookies, how can I set them?
I would personally use built in Django Cookie Sessions: https://docs.djangoproject.com/en/2.2/topics/http/sessions/#using-cookie-based-sessions

Django - Protecting Media files served with Apache with custom login_required decorator

I deployed a Django app using Apache, and I check for authentication in most views using a decorator.
#custom_decorator
def myView(request):
bla bla bla...
It's not the #login_required decorator that comes with Django, but it's almost the same thing, except that allows access only to users from certain groups. This works as intended.
Also, I'm serving media (user uploaded) files with Apache, like this:
Alias /media /path/to/media
<Directory /path/to/media>
Require all granted
</Directory
I can access the media files just fine, but the problem is that I can access them even if I'm not logged in, simply by typing the url manually, like:
mySite/media/myFile.png
Is there a way to limit access to the media files, hopefully using the custom decorator?
I stumbled across a similar question: How do you Require Login for Media Files in Django, but unfortunately the answer went way over my head.
Thanks in advance!
When you mention media path to the apache, those files are served directly by Apache (or Nginx or any other web server). Those requests do not even goes through your Django application. Hence you do not have a control over those requests or the data served by them.
One way is to create your separate API to serve the static/media files. In that API, use the same validation that you do for other content.
Even better, if you have AWS (Amazon Web Services) or GCP (Google Cloud Platform) account, store the static files on the S3 or Cloud Storage respectively and serve their URL of files via your API.
PS: Do not forget to remove the media path from the Apache configuration. Else, Apache will keeps on serving those file.
Alternatively, as mentioned in Sarafeim's answer to Restricting access to private file downloads in Django which requires modification in both sever and application side. 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. You may achieve this using django-sendfile which uses the X-SendFile mechanism. As per the django-sendfile's README:
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 sendfile mechanism, read: Django - Understanding X-Sendfile
Okay, so based on #MoinuddinQuadri answer and links, it seems that the easiest solution is to serve the files using a regular Django view, and apply the desired decorator, like this:
#custom_decorator
viewFile(request, objectID):
object = MyModel.object.get(id = objectID)
return HttpResponse(object.file, content_type = "image/png")
(In my case, I wanted to serve a FileField related to a Model, so in the view I pass the ID of the object instead of the file name).
Also, I commented out the corresponding code in the Apache conf:
### Alias /media /path/to/media
### <Directory /path/to/media>
### Require all granted
###</Directory
I had to change some templates to use the new view instead of the URL of the media file, but now it works as intended, locking out non-logged users.
However, this no longer uses Apache to serve the files, it uses Django itself, which according to the docs, is inneficient and not recommended.
Ideally you want to still serve the files using Apache and just use the view to protect its access, and for that you can use mod_xsendfile for Apache, or simply use Django Sendfile, which is a wrapper for the module just mentioned.
I tried the latter, but unfortunately it has problems with file names that have non-ascii characters. As my target are spanish-speaking users, I had to resort of just serving the files with Django, at least for now.
I used solution #1 in the post "Django protected media files". There are two other solutions described here as well: "Unpredictable Urls" and "X-Sendfile", but the one I'm describing was my choice.
As #Sauvent mentioned, this causes the files to be served by Django and not by a web server (e.g. Apache). But it's quick and easy if you're not dealing with a lot of traffic or large files.
Basically, add the following to your urls.py:
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
return serve(request, path, document_root, show_indexes)
urlpatterns = patterns('',
url(r'^{}(?P<path>.*)$'.format(settings.MEDIA_URL[1:]), protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
In my case I edited it to the following because my directories are set up differently and I use Login Required Middleware to ensure login is required everywhere (Django: How can I apply the login_required decorator to my entire site (excluding static media)?:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', "django.views.static.serve", {'document_root': settings.MEDIA_ROOT}),
)

django authentication .htaccess static

In my app users can upload files for other users.
To make the uploaded files accesible only for the addresse I need some kind of static files authentication system.
My idea, is to create the apache hosted directory for each user and to limit access to this derectory using .htaccess.
This means that each time new django user is created, I need to create a directory and the appropriate .htaccess file in it. I know I should do it using post_save signals on User model but I don't know how to create the .htaccess in the user's directory from python level. Can you help me with that?
Or perhaps you have better solution to my problem?
Use python to rewrite the .htaccess automatically?
Use a database with users and use a Apache sessions to authenticate?
Why not have an PrivateUploadedFile object that has a field for the file and a m2m relation for any Users who are allowed to read that file? Then you don't have to mess with Apache conf at all...
from django.contrib.auth.models import User
from django.db import models
import hashlib
def generate_obfuscated_filename(instance, filename):
hashed_filename = hashlib.sha1(str(filename)) #you could salt this with something
return u"your/upload/path/%s.%s" % (hashed_filename, filename.split(".")[-1]) #includes original file format extension
class PrivateUploadedFile(models.Model):
file = models.FileField(upload_to=generate_obfuscated_filename)
recipients = models.ManyToManyField('User')
uploader = models.ForeignKey('User', related_name="files_uploaded")
def available_to(self, user):
#call this as my_uploaded_file_instance.available_to(request.user) or any other user object you want
return user in self.recipients.all() #NB: not partic. efficient, but can be tuned
Came across this django-sendfile which can be used to serve static files. Might be helpful.
Have Django handle authentication and authorization as normal, then use Apache's mod_xsendfile to have Apache handle sending the actual file. Remember to have the files uploaded to a place that cannot be accessed directly, ideally outside Apache's document root.
This question has a good example of how to implement this behaviour, but it basically boils down to setting response['X-Sendfile'] = file_path in your view.
django-sendfile does the same thing, but for several different web servers (and convenience shortcuts), and django-private-files is the same, but also implements PrivateFileField
Add a view that controls the authentication of the user, and serve the file via django's static files serving tools:
def get_file(request, some_id):
# check that the user is allowed to see the file
# obtain the file name:
path = path_from_id(some_id)
# serve the file:
return django.views.static.serve(request, path, document_root=your_doc_root)
This is a perfectly secure solution, but perhaps not ideal if you serve an enormous of files in that way.
Edit: the disclaimer on the django page does not apply here. Obviously, it would be inefficient to serve all your files with static.serve. It is however secure in the sense that you only serve the files to the users that are allowed to.

Categories