Can't figure this one out. The CSRF verification works fine in all my Django template views. Here I'm trying to post from a python client using techniques I've found in other posts. The client.get(url) call does provide the token (the debugger shows, for example: client.cookies['csrftoken'] = 'POqMV69MUPzey0nyLmifBglFDfBGDuo9') but requests.post() fails with error 403, CSRF verification failed. What's going on?
My Django view (with some dummy stuff in the methods):
class CameraUpload(View):
template_name = "account/templates/upload.html"
def get(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
def post(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
And the client that's trying to do the post:
import requests
url = 'http://127.0.0.1:8000/account/camera_upload/'
client = requests.session()
client.get(url)
csrftoken = client.cookies['csrftoken']
payload = {
'csrfmiddlewaretoken': csrftoken,
'tag': '69'
}
r = requests.post(url, data=payload)
EDIT:
Tried adding the referer as per this link so code now looks like:
r = requests.post(url, data=payload, headers=dict(Referer=url))
but same problem exists.
You should be using your session (client) for the post:
r = client.post(url, data=payload, headers=dict(Referer=url))
Related
I amusing nextjs as my frontend. I am struggling to validate server site captcha response. I want data will be not inserted in my database until captcha verification sucess. I tried drf-recaptcha package. Here is my code:
settings.py
INSTALLED_APPS = ['rest_framework_recaptcha',] #included in my installed app
DRF_RECAPTCHA_SECRET_KEY = '< my_secrect key>'
serializers.py
from rest_framework_recaptcha import ReCaptchaField
class ContactSerializer(serializers.ModelSerializer):
recaptcha = ReCaptchaField()
class Meta:
model = Contact
fields = '__all__'
my API views.py
#api_view(['POST', 'GET'])
def contact_forms_api(request):
if request.method == 'POST':
''' Begin reCAPTCHA validation '''
recaptcha_response = request.POST.get('g-recaptcha-response')
url = 'https://www.google.com/recaptcha/api/siteverify'
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
result = json.load(response)
''' End reCAPTCHA validation '''
if result['success']:
data = request.data
serializer = ContactSerializer(data=data)
if serializer.is_valid():
serializer.save()
print(serializer.data)
return Response({
'status': True,
'message': 'sucess'
})
return Response({
'status': False,
'message': serializer.errors
})
here is my nexjs code:
I am using this package in my frontend application. here my code sometings look like:
<form onSubmit={SubmitContact}>
<input type="text"/>
<ReCAPTCHA
sitekey="my_captcha site ke=y"
onChange={handleRecaptcha}
/>
<form>
here is my SubmitContact function look like this:
const res = await axios.post(url,data,{headers: headers}
)
inside data variable I am passing user data and passing this headers = { 'Content-Type': 'application/json', } as header.
Conclution: I know this question is very long but I think I need to explain everything so you can understand my problems.
Right now anyone can submit data using my contact froms but I want they must be verify captcha and the captcha response must be verified from serverside.
This question already has answers here:
#csrf_exempt does not work on generic view based class
(4 answers)
Closed 3 years ago.
I wrote a test file to check if the URL works or not and it keeps printing Forbidden (CSRF cookie not set.) could please check what's the problem
#post handler
#csrf_exempt
def post(self, request, *args, **kwargs):
valid_json = is_json(request.body)
if not valid_json:
error_data = json.dumps({'message': 'Invalid data sent, please send using JSON format'})
return self.render_to_response(error_data, status=400)
data = json.loads(request.body)
form = SupervisorForm(data)
if form.is_valid():
obj = form.save(commit=True)
obj_data = obj.serialize()
return self.render_to_response(obj_data, status=201)
if form.errors:
data_error = json.dumps(form.errors)
return self.render_to_response(data_error, status=400)
json_data = json.dumps({'message': 'Not Allowed'})
status_code = HTTP_400_BAD_REQUEST
return self.render_to_response(json_data, status_code)
def post():
data = {
'supervisor_name':'name',
'supervisor_phone': '76786875',
'supervisor_email': 'sdsds#sdsd.com',
'supervisor_image': 'path to local image',
}
json_data = json.dumps(data)
json_loads = json.loads(json_data)
print(type(json_data))
print(type(json_loads))
print(help(requests.put))
r = requests.put('http://127.0.0.1:8000/api', json = json.dumps(data))
return r.json()
You have probably configured your Django to use a CSRF token but have not set it up for your API. Are you able to disable CSRF in your configuration? Otherwise, you'd have to set it up in accordance with the documentation
CSRF is important for websites that are at a high risk of getting hacked through scripts/iframes. CSRF is what prevents your bank account from sending money to a hacker via email/popup scripts. Unless you're building a website that has confidential data scoped to the user (e.g. Facebook, Venmo, PayPal) CSRF is not necessary.
I'm trying to test an UpdateView that adds a message to the redirected success page. It seems my issue comes from messages because of pytest returns:
django.contrib.messages.api.MessageFailure: You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware
My test code is:
def test_authenticated_staff(self, rf):
langues = LanguageCatalog.objects.create(
lang_src='wz',
lang_dest='en',
percent='4'
)
req = rf.get(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}))
data = {'lang_src': 'it',
'lang_dest': 'en',
'percent': '34'}
req = rf.post(reverse(
"dashboard.staff:lang-update", kwargs={'pk': langues.pk}), data=data)
req.user = UserFactory()
resp = views.LangUpdateView.as_view()(req, pk=langues.pk)
I precise that the MessageMiddleware is present in MIDDLEWARE settings. I use Django==2.0.13.
I found the solution. In order to test a such request, you need first to annotate it with a session and then a message. Actually it means to add these lines:
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
# in your test method:
"""Annotate a request object with a session"""
middleware = SessionMiddleware()
middleware.process_request(req)
req.session.save()
"""Annotate a request object with a messages"""
middleware = MessageMiddleware()
middleware.process_request(req)
req.session.save()
# and then (in my case)
resp = views.LangUpdateView.as_view()(req, pk=langues.pk)
You can also move manual request annotation int a separate context manager that can be reused within multiple tests, the code would look like this then:
import contextlib
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
#contextlib.contextmanager
def middleware(request):
"""Annotate a request object with a session"""
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
"""Annotate a request object with a messages"""
middleware = MessageMiddleware()
middleware.process_request(request)
request.session.save()
yield request
def test_authenticated_staff(self, rf):
langues = LanguageCatalog.objects.create(
lang_src='wz',
lang_dest='en',
percent='4'
)
req = rf.get(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}))
data = {'lang_src': 'it',
'lang_dest': 'en',
'percent': '34'}
req = rf.post(reverse("dashboard.staff:lang-update", kwargs={'pk': langues.pk}), data=data)
req.user = UserFactory()
with middleware(req): # << !
resp = views.LangUpdateView.as_view()(req, pk=langues.pk)
I'm currently using the Requests library to send a file to a remote server from a form(InMemoryUploadedFile). I initially sent the 'file'(file = self.request.FILES.get('file')) as part of my payload, and when I ran the code I received a JSON error response from the server that says:
{"outcome":"error","message":"string contains null byte"}
Upon further reading(https://docs.djangoproject.com/en/1.9/ref/files/uploads/) it seems like it would make sense to read the file. So I decided to read the file using the .chunks() method(in case you have files larger than 2.5MB), but now I'm getting a:
{"outcome":"error","message":"invalid byte sequence in UTF-8"}
And if I use .multiple_chunks() I get a server 500 error.
Does anyone have any ideas what steps could be taken to resolve this issue?
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_message = 'Document was successfully added'
def form_valid(self, form):
pk = self.kwargs['pk']
user = get_object_or_404(User, pk=pk)
file = self.request.FILES.get('file')
if not self.post_to_server(file, user.id):
messages.error(self.request, "Upload failed", extra_tags='alert alert-danger')
return render(self.request, self.template_name, {'form': form})
def post_to_server(self, file, cid):
url = 'https://exampleapi.herokuapp.com/api/files/'
headers = {'token': 'secret-token93409023'}
payload = {'file': file.chunks(), 'client_id': cid}
r = requests.post(url, data=payload, headers=headers)
print(r.text)
if r.status_code == requests.codes.ok:
return True
else:
return False
I figured it out, it had to do with me sending the file as part of the payload, which doesn't multipart encode the upload. So what I had to do was send the file as the correct 'file' parameter which does properly multipart encode the upload.
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_message = 'Document was successfully added'
def form_valid(self, form):
pk = self.kwargs['pk']
user = get_object_or_404(User, pk=pk)
file = self.request.FILES.get('file')
if not self.post_to_server(file, user.id):
messages.error(self.request, "Upload failed", extra_tags='alert alert-danger')
return render(self.request, self.template_name, {'form': form})
def post_to_server(self, file, cid):
url = 'https://exampleapi.herokuapp.com/api/files/'
headers = {'token': 'secret-token93409023'}
# Remove files as part of payload
payload = {'client_id': cid}
files = {'file': file}
# Place 'files' as file paramter to be properly multipart encoded
r = requests.post(url, files=files, data=payload, headers=headers)
print(r.text)
if r.status_code == requests.codes.ok:
return True
else:
return False
My website uses Django's default auth module to do user authentications. Usually, user just needs to fill out a html form in the login page with his/her username and password and click submit, then the CSRF-protected data will be posted to /auth/login/, the Django auth endpoint.
But now for some reason I also need to do this on my server. This is the same server as the backend authentication server. After researching and trials and errors, I finally got:
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_protect
import requests
class fb_login(View):
"login view for Facebook OAuth"
#method_decorator(csrf_protect)
def get(self, request):
''' code that gets username and password info. from user's FB profile
is not shown here '''
# login code
url = 'http://localhost:8000/auth/login/'
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
csrftoken = client.cookies['csrftoken']
form = {'username': uname, 'password': pw}
header = {'X-CSRFToken': csrftoken}
resp = requests.post(url, data=form, headers=header)
I also tried to add csrf token as part of the form field:
form = {'username': uname, 'password': pw , 'csrfmiddlewaretoken': csrftoken}
resp = requests.post(url, data=form)
I pretty much just followed the Django doc. But I still get the 403 error saying CSRF verification failed, CSRF cookie not set. I wonder if I missed something here? I've double-checked the process against the Django doc but I cannot find anything that might be wrong or missing.
As suggested by #rodrigo, cookies are also needed to pass the CSRF security check. Here's a simple code snippet:
csrftoken = requests.get(url).cookies['csrftoken']
form = {'username': uname, 'password': pw}
header = {'X-CSRFToken': csrftoken}
cookies = {'csrftoken': csrftoken}
resp = requests.post(url, data=form, headers=header, cookies=cookies)