I'm trying to use a script for multiple file uploads, like swfupload or uploadify on my django application but no matter what I try, I always get a 403 forbidden error for the upload URL. If I try to run the 'same' code (just different links to same files) independently, it works like a charm.
Any idea if I'm missing something on my main code or is there some kind of setting that I don't know about?
I use uploadify in my django project, get 403 error too, because django has CSRF protection. so i change this function in my views.py solve this problem.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def ajax_flash_upload(request):
This is totally related with CSRF protection. In my case I solved that issue such that,
views.py
def photo_upload(request):
if request.method == 'POST':
for field_name in request.FILES:
....
....
return HttpResponse("ok", mimetype="text/plain")
else:
return render_response(request, 'wpphotos/post/photo_upload.html', {"csrf_token": get_token(request)},context_instance=RequestContext(request))
Because flash useses its own session while uploading, you should set csrf_token value in your middleware such that
swfupload.py
from django.conf import settings
from django.core.urlresolvers import reverse
class SWFUploadMiddleware(object):
def process_request(self, request):
if (request.method == 'POST') and (request.path == reverse('project_name.module_name.views.photo_upload')) and \
request.POST.has_key(settings.SESSION_COOKIE_NAME):
request.COOKIES[settings.SESSION_COOKIE_NAME] = request.POST[settings.SESSION_COOKIE_NAME]
if request.POST.has_key('csrftoken'):
request.COOKIES['csrftoken'] = request.POST['csrftoken']
For the last step, you should set csrftoken as post parameter in your javascript for SWFUpload settings such that
photo_upload.html
window.onload = function() {
swfupload = new SWFUpload({
post_params: {
"csrfmiddlewaretoken": "{{csrf_token}}"
},
upload_url: "/module_name/post/photo_upload/",
flash_url: "/media/flash/swfupload.swf",
file_size_limit : "2.5 MB",
....
....
....
});
};
This is probably related to the flash cookie bug: your client has an authentication cookie that the flash is not including in its request to the server. Since the request doesn't have the auth cookie, it gets rejected with a 403.
Just add an extra data when initializing Uploadify (make your changes on "swf" and "uploader" settings):
$('#file_upload').uploadify({
'formData' : { 'csrfmiddlewaretoken' : '{{csrf_token}}' },
'swf' : '/static/js/uploadify.swf',
'uploader' : '{% url upload %}',
// Put your other options here
});
Thank you very much, brsbilgic. I've tried your solution, and it worked!
By the way, the middleware snippet should be modified to:
if request.POST.has_key('csrfmiddlewaretoken'):
request.COOKIES['csrftoken'] = request.POST['csrfmiddlewaretoken']
Related
I have a Django web application and I'm trying to make an ajax call for uploading a large image. If I can upload the image I am trying to upload, I'll read the image with the pythonocc library and return the volume information. Since this process takes a long time, I am trying to do it using the django-background-tasks library. According to what I'm talking about, the ajax request that I am trying to take the image and send it to the view is as follows.
var data = new FormData();
var img = $('#id_Volume')[0].files[0];
data.append('img', img);
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
$.ajax({
method: 'POST',
url: '{% url 'data:upload' %}',
cache: false,
processData: false,
contentType: false,
type: 'POST',
data: data,
}).done(function(data) {
});
my view that will work after Ajax request;
def uploadFile(request):
if request.method == 'POST':
file = request.FILES.get('img')
filename = file.name
key = 'media/' + filename
s3_resource = boto3.resource('s3')
bucket = s3_resource.Bucket('bucket')
bucket.put_object(Key=key, Body=file)
new_data = Data(author=request.user)
new_data.save()
data_id = new_data.id
initial = CompletedTask.objects.filter(verbose_name=verbose)
request.session['u_initial'] = len(initial)
verbose = str(request.user) + '_3D'
read_3D(data_id, key, filename, verbose_name = verbose) ###background-task function is called
return redirect('data:uploadStatus')
The view that calls this background task function should also redirect the view, which will show the status of the upload.
def upload_status(request):
customer= str(request.user)
verbose = customer + '_3D'
initial = request.session['u_initial']
if request.is_ajax():
running, completed = get_upload_status(verbose)
context = {
"initial": initial,
"running": running,
"completed": completed,
}
return JsonResponse(context)
return render(request, "file_pending.html")
However, when I made the Ajax request, the view gave The view data.views.uploadFile didn't return an HttpResponse object. It returned None instead. error because it doesn't enter if request.method == 'POST' condition. When I remove that condition line from view, it cannot get the img file. By the way, this uploadFile view was working without any problems. Strangely, when I added the upload_status view and redirect it, it stopped working. To revert, I deleted that part and reverted it back, but it still didn't work.
How can I fix this problem? Am I missing something?
A. You should restrict your view method to the actual method you are expecting using the require_http_methods decorator
from django.views.decorators.http import require_POST
#require_POST
def uploadFile(request):
file = request.FILES.get('img')
filename = file.name
key = 'media/' + filename
s3_resource = boto3.resource('s3')
# ....
return redirect('data:uploadStatus')
That way calls to this endpoint not using POST will receive a 405 Method not allowed response. It's easier to debug and clarifies what is going wrong.
B. Make sure that your Ajax call in the frontend follows the redirect.
The cache parameter is not necessary with POST (unless you plan for IE8)
About contentType=false and processData=false: it still looks to me like this is a regular multipart-formdata call. Don't do this unless you know why you are doing it. Your Django code looks like it will handle the regular form data just fine, no need to tinker with the default behaviour.
C. You have to implement an error callback, as well:
}).done(function(data) {
}).fail(function(xhr, text, error) {
console.log(text, error)
});
I suppose what happens is some error that you are not handling in the JS code.
You have not added your settings, so I cannot be sure, but I suppose that you have not setup your CSRF configuration for AJAX correctly. One way to fix this would be to add the CSRF cookie to the AJAX request, as well. Both the CSRF form field as well as the cookie have to be present.
See https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax
I'm trying to run an api using postman. My application is developed in django 1.11.6 using python 3.5.
My app is installed on an ubuntu server. I have no login mechanism to create a csrf token.
These are the steps that I follow:
Click on "import" tab on the upper left side.
Select the Raw Text option and paste my cURL command.
Hit import and I have the command in your Postman builder
Press send button.
My curl command is:
curl -i -H 'Accept: application/json; indent=4' -X POST https://127.0.0.1/users/:register/ -d "id=111&firstname=zinonas&yearofbirth=2007&lastname=Antoniou&othernames="
The error I get is Forbidden (403) - CSRF verification failed. Request aborted.
When I run the curl command via cygwin, it's working properly.
This is the view function that I'm using:
class ApiUserRegister(APIView):
permission_classes = ()
serializer_class = RegisterUserSerializer
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)
# Check format and unique constraint
serializer.is_valid(raise_exception=True)
data = serializer.data
if User.objects.filter(id=data['id']).exists():
user = User.objects.get(id=data['id'])
is_new = "false"
resp_status = status.HTTP_200_OK
else:
user = User.objects.create(id=data['id'],
firstname=data['firstname'],
yearofbirth=data['yearofbirth'],
lastname=data['lastname'],
othernames=data['othernames'])
user.save()
is_new = "true"
resp_status = status.HTTP_201_CREATED
resp = {"user": serializer.get_serialized(user),
"isnew": is_new}
return Response(resp, status=resp_status)
In settings.py I have:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}
Try this.
from django.views.decorators.csrf import csrf_exempt
class ApiUserRegister(APIView):
permission_classes = ()
serializer_class = RegisterUserSerializer
#csrf_exempt
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)
To make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation.
1st option
2nd option
simple just make sure to put as_view()
urlpatterns = [
path('sign_up', views.SignUp.as_view()),
]
update your class to be like this
from braces.views import CsrfExemptMixin
class your_class(CsrfExemptMixin, ......yours_here)
def post(...):
[....]
this will tell django to allow requests without csrf
Django sets csrftoken cookie on login. After logging in, we can see the csrf token from cookies in the Postman. (see image)
CSRFtoken from cookies
We can grab this token and set it in headers manually.
But this token has to be manually changed when it expires. This process becomes tedious to do it on an expiration basis.
Instead, we can use Postman scripting feature to extract the token from the cookie and set it to an environment variable. In Test section of the postman, add these lines.
var xsrfCookie = postman.getResponseCookie("csrftoken"); postman.setEnvironmentVariable('csrftoken', xsrfCookie.value);
This extracts csrf token and sets it to an environment variable called csrftoken in the current environment.
Now in our requests, we can use this variable to set the header.(see image)
Set {{csrftoken}} in your header
When the token expires, we just need to log in again and csrf token gets updated automatically.
Thanks to #chillaranand from hackernoon.com to original post
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.csrf import csrf_protect
#csrf_protect
#csrf_exempt
def home(request):
Add "#csrf_protect, #csrf_exempt" Before the method
In urls file, try this:
urlpatterns = [
url(r'^your_uri/', views.YourView.as_view()),
]
this will tell django to allow requests without csrf
I use Django 1.9.7 & Python 3.5
I implement creating user mechanism and tried to test with POSTMAN(chrome application), but it doesn't work and it shows something like belows:
Forbidden (CSRF cookie not set.): /timeline/user/create/
This is the code :
urls.py
from django.conf.urls import url
From. import views
app_name = 'timeline'
urlpatterns = [
# ex) /
url(r'^$', views.timeline_view, name='timeline_view'),
# ex) /user/create
url(r'^user/(?P<method>create)/$', views.user_view, name='user_view'),
]
views.py
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, HttpResponse
from timeline.models import *
def timeline_view(request):
return HttpResponse('hello world')
def user_view(request, method):
if method == 'create' and request.method == 'POST':
print("hi")
username = request.POST.get('username')
username = request.POST.get('username')
user = User.objects.create_user(username, password=password)
user.first_name = request.POST.get('name','')
user.save()
profile = UserProfile()
profile.user = user
profile.save()
return HttpResponse('create success')
else:
return HttpResponse('bad request', status=400)
POSTMAN :
I tried Django CSRF Cookie Not Set but I think this post is for past version.
for testing i used the #csrf_exempt decorator.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def user_view(request, method):
...
now you should be able to call this function without the csrf cookie.
(last time i tried it, i was using django 1.8.7)
source:
https://docs.djangoproject.com/en/1.9/ref/csrf/#edge-cases
You should put CSRFToken in request headers.
After sending request via postman, look at the response Cookies section, take csrftoken value and put in Headers section of request, like this:
key:X-CSRFToken
value: jSdh6c3VAHgLShLEyTjH2N957qCILqmb #your token value
Sometimes Version problem in 'Postman' :
I have face the same problem. While sending the data using the oldest version of postman in POST method.
That time I have received the empty json data in server side.
And I have fix this problem, Once I uninstall the oldest version of postman and installed with latest version.
Use this below statement on top of each and every view function definition (views.py). We don't need to use CRF related statements.
#api_view(["POST", "GET"])
eg:
#api_view(["POST", "GET"])
def GivenInput():
return Response(e.args[0],status.HTTP_400_BAD_REQUEST)
Note*:
But I didn't know that any alternative way to make it global throughout the file.
I'm using DJango 1.8 on a linode server, and have the following view:
import json
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
def home_view(request):
r = { 'response': 'OK', 'data': None }
return HttpResponse(json.dumps(r), content_type = "application/json")
#csrf_exempt
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
return HttpResponse(json.dumps(r), content_type = "application/json")
My urls look like this:
urlpatterns = [
url(r'^django/admin/', include(admin.site.urls)),
url(r'^django/$', home_view),
url(r'^django/ping/$', ping),
url(r'^django/echo/$', echo),
]
If I go to my linode site at
http://mylinodesite/django/ping/
I get:
{"data": "ping", "response": "OK"}
Great. If I use jquery and do a
$.get("http://mylinodesite/django/ping/")
I get
No 'Access-Control-Allow-Origin' header is present on the requested resource.
From what I understand the #csrf_exempt is supposed to get rid of the CSRF header stuff. What gives?
Daniel, turns out you're partially right. It's CORS but it cannot be fixed on the jQuery side. Here's my Django view code that does what I want. Note that it adds the Access-Control-Allow-Origin header to allow requests from all (*) for GET only.
This is also just a demonstration of how to do this all in one file. One could create middleware to do this for all requests if needed, but this works and is a good example of how to do it all in one place so you can see what is going on, and here is the full gist of the entire view file:
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
response = HttpResponse("['ok']", content_type="application/json")
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'GET'
return response
This has nothing whatsoever to do with CSRF, which is for POST actions only and is enforced by Django.
You are doing a cross-domain GET action. Browsers forbid this by default because of what is called the same-origin policy: a JS script can only make requests to the same domain it is loaded from. So, you are being prevented by the browser itself.
To enable requests to named domains, you can use something called CORS, which uses a header in the request. See the jQuery Ajax documentation.
when I'm using following Python code to send a POST request to my Django website I'm getting 403: Forbidden error.
url = 'http://www.sub.example.com/'
values = { 'var': 'test' }
try:
data = urllib.urlencode(values, doseq=True)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
except:
the_page = sys.exc_info()
raise
When I'm opening any other website it works properly.
example.com is Django website too, and it works properly too.
I think, that's Django config problem, can anyone tell me what should I do to provide access to my script?
Look here https://docs.djangoproject.com/en/dev/ref/csrf/#how-to-use-it.
Try marking your view with #csrf_exempt. That way, Django's CSRF middleware will ignore CSRF protection. You'll also need to use from django.views.decorators.csrf import csrf_exempt. See: https://docs.djangoproject.com/en/dev/ref/csrf/#utilities
Please be advised that by disabling CSRF protection on your view, you are opening a gate for CSRF attacks.
If security is vital to you then consider using #csrf_exempt followed by #requires_csrf_token (see: https://docs.djangoproject.com/en/dev/ref/csrf/#unprotected-view-needs-the-csrf-token). Then, in your script pass this token and that's it.
Does the view that you are posting to have a Django Form on it? If so, I wonder if it's giving a csrf error. I think that manifests itself as a 403. In that case, you'd need to add the {{ csrf_token }} tag. Just a thought.
The response is 403 because django requires a csrf token (included in the post data) in every POST request you make.
There are various ways to do this such as:
Acquiring the token from cookie and the method has been explained in article enter link description here
or
You can access it from DOM using {{ csrf_token }}, available in the template
So now using the second method:
var post_data = {
...
'csrfmiddlewaretoken':"{{ csrf_token }}"
...
}
$.ajax({
url:'url',
type:'POST'
data:post_data,
success:function(data){
console.log(data);
},
error:function(error){
console.log(error);
}
});
Or you can allow the permission to make this post request.
Note: Should be used in the cases where you don't need to authenticate the users for posting anything on our server, say, when a new user registers for the first time.
from rest_framework.permissions import AllowAny
class CreateUser(APIView):
permission_classes = (AllowAny,)
def post(self, request, format=None):
return(Response("hi"))
Further Note that, If you want to make that post request form a different domain (in case when the front of the application is in React or angular and the backend is in Django), make sure the add following in the settings file:
Update the INSTALLED_APPS to use 'coreHeaders' :
INSTALLED_APPS = [
'corsheaders',
]
White list your front end domain by adding following to settings file again:
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
)
Django documentation provides several ways to ensure that CSRF tokens are included. See https://docs.djangoproject.com/en/1.11/ref/csrf/ for details.
I got this error when an authentication Token was expired or when no Token was sent with the request. Using a renewed token fixed the problem.
curl -X POST -H "Authorization: Token mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
or
curl -X POST -H "Authorization: JWT mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
depending on Token type.
I too had this problem, because I Tried to access the Main endpoint from another endpoint using '../url' URL Jumping.
My Solution was to add another path for the same viewset;
router.register('main/url',ViewSet,'name');
router.register('secondary/url',ViewSet,'name')
But in Your Case You are Trying to access it from a completely different Location, From Django's Point of view So You need to mark you ViewSet with #crsf_exempt middleware which will Disable Security Protocols Related to CRSF.