Does Django Automatically Sanitise urls for directory traversal attacks? - django

I have a view in my Django web app like this :
def download_func(request, url):
file_url = '/home/mylaptop/myproject/' + url
file = open (file_url, "rb").read()
response = HttpResponse(file, content_type="application/octect-
stream")
response['Content-Disposition'] = 'attachment; filename = %s' %url
return response
and a url conf like this :
urlpatterns = [
...
url(r'^media/videos/(?P<url>[-._\w]+)/$', views.download_func),
...
]
Downloading a video that is uploaded before is perfectly working, but my question is: "Is there the risk of directory traversal attack in my app?"
if "yes" how can I fix it (or is there a better way of writting view for downloading while development?)
I have been testing directory traversal on it and understood that Django automatically sanitises urls containing "../../" is that right?
thanks

Related

In django, how can i upload file via url?

Hi i'm making my own webserver using django.
i just want to upload local file to django server.
i google every method but i can't get answer.
every method using form or html but i don't want to using form and html
example : from www.localfolder/example.txt to /media/examplefolder.
i don't know how to do.. any help?
this is my code.
#csrf_exempt
def download_file(request, file):
fl_path = 'media/'
filename = str(file)
fl = open(fl_path, 'r')
mime_type, _ = mimetypes.guess_type(fl_path)
response = HttpResponse(fl, content_type=mime_type)
response['Content-Disposition'] = "attachment; filename = %s" % filename
return response
What did you search for when you googled? These were the top 2 results for Django files
https://docs.djangoproject.com/en/3.0/topics/http/file-uploads/
https://docs.djangoproject.com/en/3.0/topics/files/
Seems to have everything you are looking for.

Django output csv file, filename is not setting as the value of Content-Disposition

I want to download a csv file with custom filename in a django project, but somehow the downloaded filename just display as "download.csv" instead of using the value of filename in Content-Disposition. I also tried to print csv_response['Content-Disposition'] out, but I'm getting a very strange string =?utf-8?b?YXR0YWNobWVudDsgZmlsZW5hbWU9Iuivvueoi+aKpeWQjeaDheWGtV8yMDE5MTEyODA3NDI0Ny5jc3Yi?=
the code snippet is :
#action(detail=False, methods=['GET'])
def download(self, request):
registrations = self.filter_queryset(self.get_queryset())
csv_response = HttpResponse(content_type='text/csv')
csv_response['Content-Disposition'] = 'attachment; filename="some_custom_name_{time}.csv"'.format(
time=time.strftime("%Y%m%d%H%M%S", time.localtime())
)
writer = csv.writer(csv_response)
writer.writerow([
some content,
])
for registration in registrations:
term_title = '{order} th'.format(order=registration.term.order)
course_title = registration.course.title
writer.writerow([
registration.user.email,
course_title,
term_title,
str(registration.confirmation_code),
str(registration.payment_due),
str(registration.payment_paid),
str(registration.source),
str(registration.created_at),
str(registration.updated_at),
str(registration.payment_source),
])
return csv_response
the django I am using is 2.2
any ideas why this is happening? I am a newb.
Thx in advance
The response header in chrome Dev tools:
I resolved the problem, by following the answer in the below post:
HttpResponse Django does not change file name
I guess that it is that because the string of Content-Disposition needs to be encoded, and if no, then somehow cannot operate on that, by using urlquote, it is solved.
Explanation about urlquote is here
UPDATE:
Also, a simpler way to resolve this without importing urlquote is to add encode(), like below:
csv_response['Content-Disposition'] = 'attachment; filename="some_custom_name_{time}.csv"'.format(
time=time.strftime("%Y%m%d%H%M%S", time.localtime())
).encode()
Change to this:
csv_response['Content-Disposition'] = 'attachment; filename="some_custom_name_{}.csv"'.format(
time.strftime("%Y%m%d%H%M%S", time.localtime())
)

Create download link file in django

I created a file in project, generation pdf from html. For this i have this method:
def generation_html_to_pdf(self):
path_pdf = None
with NamedTemporaryFile(delete=False, suffix=".pdf", dir='pdf_files') as tf:
path_pdf = tf.name
pdfkit.from_file('templates/first_page.html', tf.name)
return path_pdf
Then, in pdf_files folder i have the pdf file. I want to get a download link for this file:
my view
path_to_pdf = generation_html_to_pdf()
download_link = 'http://' + request.get_host() + path_to_pdf
json_inf_pdf = {'download_link': download_link}
return JsonResponse(json_inf_pdf, status=200)
i have json like this:
{"download_link": "http://127.0.0.1:8000/home/alex/projects/test_project/pdf_files/tmpe0nqbn01.pdf"}"
when i click in this link i have error:
Page not found (404)
You need to create download view and url. Function like this to create link:
def download_link(request):
''' Create download link '''
download_link = 'http://{}/{}'.format(request.get_host(), 'download/my_filename')
json_inf_pdf = {'download_link': download_link}
return JsonResponse(json_inf_pdf, status=200)
and to download pdf:
def download_file(request, my_filename):
''' Download file '''
# Open template
from django.conf import settings
template_url = os.path.join(settings.BASE_DIR, 'templates', 'first_page.html')
template_open = open(template_url, 'r')
# Read template
from django import template
t = template.Template(template_open.read())
c = template.Context({})
# Create pdf
pdf = pdfkit.from_string(t.render(c))
# Create and return response with created pdf
response = HttpResponse(pdf)
response['Content-Type'] = 'application/pdf'
response['Content-disposition'] = 'attachment ; filename = {}'.format(my_filename)
return response
and url:
path('/download/<str:my_filename>', views.download_file, name="download_pdf')
I can't guarantee that this will work in your case without modification, since I can't tell which html-to-pdf library you're using and without seeing your other code. It's just a basic implementation idea.

Django download file empty

I am writing a simple function for downloading a certain file, from the server, to my machine.
The file is unique represented by its id. The file is locatd corectly, and the download is done, but the downloaded file (though named as the one on the server) is empty.
my download function looks like this:
def download_course(request, id):
course = Courses.objects.get(pk = id).course
path_to_file = 'root/cFolder'
filename = __file__ # Select your file here.
wrapper = FileWrapper(file(filename))
content_type = mimetypes.guess_type(filename)[0]
response = HttpResponse(wrapper, content_type = content_type)
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(course)
return response
where can i be wrong? thanks!
I answered this question here, hope it helps.
Looks like you're not sending any data (you don't even open the file).
Django has a nice wrapper for sending files (code taken from djangosnippets.org):
def send_file(request):
"""
Send a file through Django without loading the whole file into
memory at once. The FileWrapper will turn the file object into an
iterator for chunks of 8KB.
"""
filename = __file__ # Select your file here.
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Length'] = os.path.getsize(filename)
return response
so you could use something like response = HttpResponse(FileWrapper(file(path_to_file)), mimetype='application/force-download').
If you are really using lighttpd (because of the "X-Sendfile" header), you should check the server and FastCGI configuration, I guess.
Try one of these approaches:
1) Disable GZipMiddleware if you are using it;
2) Apply a patch to django/core/servers/basehttp.py described in
https://code.djangoproject.com/ticket/6027

serving files using django - is this a security vulnerability

I'm using the following code to serve uploaded files from a login secured view in a django app.
Do you think that there is a security vulnerability in this code? I'm a bit concerned about that the user could place arbitrary strings in the url after the upload/ and this is directly mapped to the local filesystem.
Actually I don't think that it is a vulnerability issue, since the access to the filesystem is restricted to the files in the folder defined with the UPLOAD_LOCATION setting.
UPLOAD_LOCATION = is set to a not publicly available folder on the webserver
url(r'^upload/(?P<file_url>[/,.,\s,_,\-,\w]+)', 'project_name.views.serve_upload_files', name='project_detail'),
#login_required
def serve_upload_files(request, file_url):
import os.path
import mimetypes
mimetypes.init()
try:
file_path = settings.UPLOAD_LOCATION + '/' + file_url
fsock = open(file_path,"r")
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path)
print "file size is: " + str(file_size)
mime_type_guess = mimetypes.guess_type(file_name)
if mime_type_guess is not None:
response = HttpResponse(fsock, mimetype=mime_type_guess[0])
response['Content-Disposition'] = 'attachment; filename=' + file_name
#response.write(file)
except IOError:
response = HttpResponseNotFound()
return response
EDIT: Updated the source according Ignacio Vazquez-Abrams comments:
import os.path
import mimetypes
#login_required
def serve_upload_files(request, file_url):
mimetypes.init()
try:
file_path = os.path.join(settings.UPLOAD_LOCATION, file_url)
#collapse possibly available up-level references
file_path = os.path.normpath(file_path)
#check if file path still begins with settings.UPLOAD_LOCATION, otherwise the user tampered around with up-level references in the url
#for example this input: http://127.0.0.1:8000/upload/..\test_upload.txt results having the user access to a folder one-level higher than the upload folder
#AND check if the common_prefix ends with a dir separator, Because although '/foo/barbaz' starts with '/foo/bar'
common_prefix = os.path.commonprefix([settings.UPLOAD_LOCATION, file_path])
if common_prefix == settings.UPLOAD_LOCATION and common_prefix.endswith(os.sep):
fsock = open(file_path,"r")
file_name = os.path.basename(file_path)
mime_type_guess = mimetypes.guess_type(file_name)
if mime_type_guess is not None:
response = HttpResponse(fsock, mimetype=mime_type_guess[0])
response['Content-Disposition'] = 'attachment; filename=' + file_name
else:
response = HttpResponseNotFound()
else:
print "wrong directory"
response = HttpResponseNotFound()
except IOError:
response = HttpResponseNotFound()
return response
A few tips:
Use os.path.join() to join the path together.
Use os.path.normpath() to get the actual path with no ".." references.
Use os.path.commonprefix() against UPLOAD_LOCATION and the generated path, and verify that the result starts with UPLOAD_LOCATION.
Make sure that UPLOAD_LOCATION ends with a dir separator.
TL;DR: Use os.path.