How to make cross-domain Ajax calls with CSRF token? - python

I have a typical login form in my Django app template (which uses Bootstrap directly, not through some plugin):
<form class="form-signin" method="post" action="/site_manager/login/" id="form-signin"> {% csrf_token %}
<h2 class="form-signin-heading">Please sign in</h2>
<div class="control-group">
<label class="control-label" for="login">Login:</label>
<div class="controls">
<input size="50" name="username" id="username" required="true" type="text" class="form-control" placeholder="Login" intermediateChanges=false>
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">Password:</label>
<div class="controls">
<input size="50" name="password" id="password" required="true" type="password" class="form-control" placeholder="Password" intermediateChanges=false>
</div>
</div>
<button name="submit" id="submit" value="Log in" type="submit" class="btn btn-primary pull-right">Sign in</button>
</form>
And here is the corresponding view which performs remote authentication through the requests module:
def login_view(request):
if request.POST:
username = request.POST.get('username')
password = request.POST.get('password')
headers = {'content-type': 'application/json', 'username':username, 'password':password}
r = requests.get(remote_server_url, auth=(username, password), headers=headers)
if r.status_code == 200:
user = authenticate(username=username, password=password)
if user == None:
user = User.objects.create_user(username.encode('ascii', 'ignore'), "", password)
user = authenticate(username=username, password=password)
login(request, user)
request.session.set_expiry(0)
return HttpResponseRedirect('<index_page>')
else:
# redirects back to login page with some error message
Once this login succeeds, I can query the CSRF token with Javascript, as explained here, and my plan is to make Ajax calls to the remote server (over SSL) for other purposes (RESTful queries, for example). That server, as the above code suggests, uses basic authentication. So I want to set the CSRF token in the header of every Ajax call, but this does not follow the same-origin principle:
var csrftoken = $.cookie('csrftoken'); // using the jQuery Cookie plugin
$.ajaxSetup({
headers: {
'Authorization': "Basic XXXXX"
}
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
});
$.ajax({
type: "GET",
dataType: "jsonp",
url: remote_server_url+'/api/v1/someRESTfulResource/',
contentType: 'application/json',
success: function(data){
// some operations with data
}
});
The remote_server_url used in the Ajax call and the authentication are the same, and it does not share the same domain with the Django application. It is a security risk, as I gather. I also do not want to make plain text password available in the Javascript code for the same reason. How could I make Ajax calls to the remote server with the user credentials securely?

I am doing this on my Javascript initialization for a page I want to do Ajax POSTS with django:
$(document).ajaxSend(function(event, xhr, settings) {
function sameOrigin(url) {
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isnt scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function safeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
}
});
I don't remember where I've found it, however after using it I can do Ajax posts like this:
$.post("{% url 'foo' %}", "data="+myjson, function(data) {
if(data['result']=="ok") {
window.location.replace('{% url "bar" %}');
} else {
alert("Data Err!");
}
}).error(function() {
alert("Ajax error!");
});

Related

Invalid CSRF or Missing Token in DJango

I have already checked this link on Stackoverflow but still facing same issue: CSRF verification failed. Request aborted. on django
About the issue
I am trying to submit ajax request to DJango.
Html Form
<form id="frmLogin" method="post">
{% csrf_token %}
<input type="text" name="username" />
<input type="password" autocomplete="false" name="password" />
<input type="submit" value="Submit" />
</form>
Ajax
$("form#frmLogin").validate({
rules: {
username: {
required: true
},
password: {
required: true
}
},
submitHandler: function(form) {
var data = {
"username": $(form).find("input[name='username']").val(),
"password": $(form).find("input[name='password']").val(),
"csrfmiddlewaretoken": $(form).find("input[name='csrfmiddlewaretoken']").val()
};
$.ajax({
method: "POST",
url: "/authenticate/",
cache: false,
async: true,
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
success: function(response) {
//$(form).remove();
}
})
}
});
View
from django.shortcuts import render
import json
from django.http import JsonResponse
def Authenticate(request):
if request.method == "POST":
#loginData = json.loads(request.body)
#email = userData["email"]
#password = userData["password"]
print("ok")
return JsonResponse({}, status = 200)
else:
print("not ok")
Payload Info
Error Message
Am I missing anything?
You need to add CSRF_TRUSTED_ORIGINS in settings.py file and add your origin to it as a trusted origin for unsafe requests like (POST request ) like this :
CSRF_TRUSTED_ORIGINS = ['https://your-domain.com']
Following block need to be passed to add headers to make POST request successful.
beforeSend: function(xhr, opts) {
xhr.setRequestHeader('X-CSRFToken', $(form).find("input[name='csrfmiddlewaretoken']").val());
}

CSRF_token is not valid in djnango Ajax multiple request from same page

I'm trying to perform a OTP-based login in my project. So I have a form with email and password field in the login form and when I submit it through AJAX request I check for the email and password in the database, if the values are correct then I will send an OTP through email and display a new OTP field on the same page for Entering the OTP for verifications.
Till now everything is working fine but after entering the OTP when I try to re-submit the form, I got an error csrf_token is not valid. As far as I know csrf_token is only generated on page refresh or page load but in my case the first csrf_token that was generated on page load was used in first post request.
So now how can I send the second post request with same csrf_token or can I generate the new csrf_token.
Code snippets are as follows:
<form id="loginForm" method="post" action="send_otp">
<div class="col-12">
<label class="text-16 lh-1 fw-500 text-dark-1 mb-10">Email</label>
<input id="email" type="text" name="email" placeholder="Name">
</div>
<div class="col-12">
<label class="text-16 lh-1 fw-500 text-dark-1 mb-10">Password</label>
<input id="password" type="password" name="password" placeholder="Password">
</div>
<div id="otp_field" class="col-12">
</div>
<div class="col-12">
<button type="submit" name="submit" id="send_otp" class="button -md -green-1 text-dark-1 fw-500 w-1/1">Login</button>
</div>
</form>
Sending the request and adding the OTP input field on success through Ajax:
$("#loginForm").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
/* get the action attribute from the <form action=""> element */
var $form = $(this),
url = $form.attr('action');
/* Send the data using post with element id name and name2*/
var posting = $.post(url, {
"csrfmiddlewaretoken":"{{ csrf_token }}",//$('input[name="csrfmiddlewaretoken"]').val(),
"email": $('#email').val(),
"pass": $('#password').val()
});
/* Alerts the results */
posting.done(function(data) {
$('#email'). attr('disabled','disabled');
$('#password'). attr('disabled','disabled');
$('#otp_field').append('<label class="text-16 lh-1 fw-500 text-dark-1 mb-10">Enter OTP</label><input id="otp" type="text" name="otp" placeholder="Enter OTP">')
});
posting.fail(function() {
});
});
How can I achieve it?
Add the javascript from this django-doc to your page.
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
Pass csrfmiddlewaretoken:getCookie('csrftoken')
{% csrf_token %} returns an input tag, which you don't want, and have misspelled.
From another question:
Try adding the #csrf_protect decorator just before your login function.
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def login(request):
csrfContext = RequestContext(request)
return render_to_response('foo.html', csrfContext)
If the form is not in foo.html then you need to add the #csrf_protect method to the view function that is generating it.
https://stackoverflow.com/a/12731412/18020941

I am trying to inject an already rendered Django template into a section of my page using AJAX

I am following by this tutorial on how to get live updates on django without refreshing the page.
The tutorial uses flasks render_template to get the html rendered which is then injected to a page section.
I am trying to do the same in Django, But django just directly renders it in the browser... I don't want that. I just want django to send the rendered html response to AJAX which could then inject that to a section on my live page.
Here is the code :
views.py
class ManageView(LoginRequiredMixin, View):
template_name = "dashboard/manage.html"
context = {}
def get(self, request, app_id, folder_id=None):
app = App.objects.get(pk=int(app_id))
self.context["app"] = app
if folder_id:
try:
self.context["folder"] = Folder.objects.get(id=folder_id)
except:
self.context["folder"] = app.folder
else:
self.context["folder"] = app.folder
return render(request, self.template_name, self.context)
def post(self, request, app_id, folder_id=None):
try:
files = request.FILES.getlist('files_to_upload')
folder_name = request.POST.get("folder")
master = request.POST.get("master")
if master:
master = Folder.objects.get(id=master)
if folder_name:
Folder.objects.create(name=folder_name, owner=request.user.customer, folder=master)
if files:
for file in files:
if file.size < settings.MAX_FILE_SIZE:
File.objects.create(folder=master, item=file, name=file.name, size=file.size)
app = App.objects.get(pk=int(app_id))
self.context["app"] = app
if folder_id:
try:
self.context["folder"] = Folder.objects.get(id=folder_id)
except:
self.context["folder"] = app.folder
else:
self.context["folder"] = app.folder
return render(request, 'dashboard/filesection.html', self.context)
except DatabaseError:
return render(request, "dashboard/index.html", self.context)
urls.py
urlpatterns = [ url(r'^manage/(?P<app_id>[0-9]+)/(?P<folder_id>.+)', test, name='browse'), ]
dashboard/manage.html
<div class="modal-body">
<form id="app-launch" enctype="multipart/form-data" method="post">
{% csrf_token %}
<div class="form-row">
<div class="input-group mb-3">
<div class="custom-file">
<input type="hidden" value="{{ folder.id }}" name="master">
<input type="hidden" value="{{ app.id }}" name="file_app_id">
<input type="file" class="custom-file-input" name="files_to_upload" id="file_upload" accept=".py,.js,.json,.txt,.css,.html,.pdf,.htm,.doc,.docx,.log,.ppt,.pptx" multiple>
<label class="custom-file-label" for="inputGroupFile02">Choose file</label>
</div>
<div class="input-group-append">
<button class="input-group-text btn btn-primary" id="">Upload</button>
<button class="input-group-text btn btn-primary fileButton" id="">Upload</button>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">Cancel</button>
</div>
</div>
app.js AJAX calls
$(document).ready(function() {
$(document).on('click','fileButton', function(e) {
e.preventDefault()
// const axios = require('axios');
var formData = new FormData();
var ins = document.getElementById('file_upload').files.length;
for (var x = 0; x < ins; x++) {
formData.append("files_to_upload", document.getElementById('file_upload').files[x]);
}
const csrftoken = getCookie('csrftoken');
var app_id = $('input[name="file_app_id"]').val();
var folder_id = $('input[name="master"]').val();
formData.append('master', folder_id);
req = $.ajax({
type: 'POST',
url: `/manage/${app_id}/${folder_id}`,
data: formData,
processData: false,
contentType: false,
headers: {
"X-CSRFToken": csrftoken,
}
});
req.done(function (data) {
$('#refreshSection').html(data)
})
});
});
AJAX POST and everything works, it just that the django is refreshing and rendering that section template on the browser which i don't want.
[Solved]
Its was a mistake from my side. I missed e.preventDefault()
which is really dumb.

How Can I send json file by ajax to django?

Can I send .json with data like (login,password from input html) to django who download this file and check login/password? Then I want return results by django to js,jquery. I dont wan use forms-django in form-html and dont want change urls.
code html:
<form id='login_form' action="" method="POST">
{% csrf_token %}
<div id='Login_form'>
<p>Login: </p> <input type="text" onblur="CheckEmptyElements();" id="flogin">
<p>Password: </p> <input type="password" onblur="CheckEmptyElements();" id="lpass">
<div id="butt_form">
<button type="button" class="butt_forme" id="button" onclick="CheckBeforeSend();">Enter</button>
</div>
</div>
</form>
code js:
var LoginInput = document.getElementById('flogin');
if(LoginInput.value.match(/^[a-zA-Z]+['.']+[a-zA-Z]+[0-9]+$/) == null)
{
if(document.getElementById('Windows').childElementCount > 2)
{
document.getElementById('Windows').children[0].remove();
}
AddPicture("paperJsConn");
}
else // <--- Check login/pass in base django
{
document.getElementById("button").type = "submit";
$.ajax({
type:"POST",
url: '',
data: {
"login":
"password":
},
success: function(response){
//animation and change site if correct login/pass or reloaded site.
}
});
}
and views.py:
def Logget(request):
if request.is_ajax and request.method == "POST":
//take login from json and check in base by loop
//if find check password
if: // correct
// return .json or message that is correct.
else:
// return .json or message that is not correct.
return render(request, 'index.html')
Someone help please?

Add CSRF token to hard coded Django form

I am building a static page with Jekyll that has a hard coded form, I am sending the form data to a Django server, I am having trouble generating a CSRF token. The only way I could get the data to save to the database was if I used a static csrf token which is hacky and pointless.
Is there a better way this can be done?
This is what I want:
<form method="POST" action="http://djangoserver" >
{% csrf_token %} <!-- Doesn't work in Jekyll -->
<input type="text" name="name" required id="id_name" maxlength="100>
</form>
But obviously Jekyll doesn't know what that token is, and the POST doesn't send it to the Django Server.
This works, but it is vulnerable and hacky, I need the same effect that actually generates a unique token every time.
<form method="POST" action="http://djangoserver" >
<input type="hidden" name="csrfmiddlewaretoken" value=" some long stuff" >
<input type="text" name="name" required id="id_name" maxlength="100>
</form>
The {% csrf_token %} won't work because it's a Django template tag. Hardcoding a csrfmiddlewaretoken wouldn't work either because this value change so to provide the security.
I had a similar issue on my blog which is Jekyll as well. On a contact page I added the normal HTML form with the action pointing to my Django backend. For this view, I removed the CSRF token verification using the #csrf_exempt decorator.
To avoid abuse, I added a Google Recaptcha verification.
See below an example:
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
import requests # http://docs.python-requests.org
#require_POST
#csrf_exempt
def ask(request):
recaptcha_response = request.POST.get('g-recaptcha-response')
data = {
'secret': settings.GOOGLE_INVISIBLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)
result = r.json()
if result['success']:
# process form...
else:
# invalid recaptcha
If this is not on the same domain, I would recommend setting up Django REST Framework.
If it is on the same domain, then do what is recommended on the Django Docs: you can get the CSRF token with JavaScript (note that I've changed the function to be used without jQuery):
// WITHOUT jQuery
function getCookie (name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
Update the form (note the id):
<form id="name-form" method="POST" action="http://djangoserver" >
<input type="text" name="name" required id="id_name" maxlength="100>
</form>
Add the csrftoken input:
var form = document.getElementById('name-form'),
input = document.createElement('input');
input.name = "csrfmiddlewaretoken";
input.type = "hidden";
input.value = getCookie('csrftoken');
// ^ could be a different string depending on your settings.py file
form.appendChild(input);
Hope that helps.
What you are trying is impossible, the only way to make Jekyll static pages somewhat dynamic is to use JavaScript.
You could implement what you want by making API in your Django that will create CSRF token and return it and then you can append it to your form. This way you will always have dynamic CSRF, however I don't recommend sending CSRF tokens across network as it is unsafe.

Categories