I was wondering if there is a way to secure an image or a file to be hidden when it is not authenticated.
Suppose there is an image in my website which can only be seen if that user is authenticated. But the thing is I can copy the url or open the image in the new tab.
http://siteis.com/media/uploaded_files/1421499811_82_Chrysanthemum.jpg
And again, even if I am not authenticated, I can view that particular image by going to that url. So, my my problem is, how do I secure the files, so that only authenticated users will see?
Update:
view:
def pictures(request, user_id):
user = User.objects.get(id=user_id)
all = user.photo_set.all()
return render(request, 'pictures.html',{
'pictures': all
})
models:
def get_upload_file_name(instance, filename):
return "uploaded_files/%s_%s" %(str(time()).replace('.','_'), filename)
class Photo(models.Model):
photo_privacy = models.CharField(max_length=1,choices=PRIVACY, default='F')
user = models.ForeignKey(User)
image = models.ImageField(upload_to=get_upload_file_name)
settings:
if DEBUG:
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "myproject", "static", "static-only")
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "myproject", "static", "media")
STATICFILES_DIRS = (
os.path.join(os.path.dirname(BASE_DIR), "myproject", "static", "static"),
)
Update:
template:
{% if pictures %}
{% for photo in pictures %}
<img src="/media/{{ photo.image }}" width="300" alt="{{ photo.caption }}"/>
{% endfor %}
{% else %}
<p>You have no picture</p>
{% endif %}
url:
url(r'^(?P<user_name>[\w@%.]+)/photos/$', 'pictures.views.photos', name='photos'),
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
It would be better to handle just the authentication, and let your webserver handle the serving of files. It's probably good to put them in a different directory than your settings.MEDIA_ROOT
, to prevent your webserver from serving the files before you handle the request, e.g. project_root/web-private/media/
.
import os
@login_required
def protected_file(request, path):
# set PRIVATE_MEDIA_ROOT to the root folder of your private media files
name = os.path.join(settings.PRIVATE_MEDIA_ROOT, path)
if not os.path.isfile(name):
raise Http404("File not found.")
# set PRIVATE_MEDIA_USE_XSENDFILE in your deployment-specific settings file
# should be false for development, true when your webserver supports xsendfile
if settings.PRIVATE_MEDIA_USE_XSENDFILE:
response = HttpResponse()
response['X-Accel-Redirect'] = filename # Nginx
response['X-Sendfile'] = filename # Apache 2 with mod-xsendfile
del response['Content-Type'] # let webserver regenerate this
return response
else:
# fallback method
from django.views.static import serve
return serve(request, path, settings.PRIVATE_MEDIA_ROOT)
As your webserver is way better at serving static files than Django, this will speed up your website. Check django.views.static.serve
for an idea how to sanitize file names etc.
By securing any media file not to serve by anonymous user, better way url protection.
Code ( Updated ):
from django.conf.urls import patterns, include, url
from django.contrib.auth.decorators import login_required
from django.views.static import serve
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import HttpResponse
@login_required
def protected_serve(request, path, document_root=None):
try:
obj = Photobox.objects.get(user=request.user.id)
obj_image_url = obj.image.url
correct_image_url = obj_image_url.replace("/media/", "")
if correct_image_url == path:
return serve(request, path, document_root)
except ObjectDoesNotExist:
return HttpResponse("Sorry you don't have permission to access this file")
url(r'^{}(?P<path>.*)$'.format(settings.MEDIA_URL[1:]), protected_serve, {'file_root': settings.MEDIA_ROOT}),
Note: previously any logged in user can access any page, now this update restrict non user to view other files...
The easiest option is to serve the file from django, and then add the @login_required
decorator to the view, like this:
import os
import mimetypes
from django.core.servers.basehttp import FileWrapper
from django.contrib.auth.decorators import login_required
@login_required
def sekret_view(request, path=None):
filename = os.path.basename(path)
response = HttpResponse(FileWrapper(open(path)),
content_type=mimetypes.guess_type(path)[0])
response['Content-Length'] = os.path.getsize(path)
return response
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With