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.
Related
After I submit a form with an error with django, all fields of the form are cleaned. I want to keep the information on the fields, because it will help the users when they are submiting data.
This is my views.py of the aplication:
def new(request):
context = {}
if request.method == 'POST':
form = NewSubject(request.POST)
if form.is_valid():
context['is_valid'] = True
form.save()
form = NewSubject()
else:
context['is_valid'] = False
else:
form = NewSubject()
context['form'] = form
return render(request, 'subjects/new.html', context)
I suggest you to use ajax .Because in that we can write different cases to handle if the submission is successful or not. if successful input.val('') else
display error and not clean input field .
$('#post-form').on('submit', function(event){
event.preventDefault();
console.log("form submitted!") // sanity check
create_post();)}; function create_post() {
console.log("create post is working!")
$.ajax({
url : "/lrequestConfirmed/", // the endpoint
type : "POST", // http method
data : {
datef: $('#datepicker').val(),
type: $('#type').val(),
reason: $('#reason').val()
}, // data sent with the post request
// handle a successful response
success : function(json) {
$('#datepicker').val(''); // remove the value from the input
$('#reason').val('');
$('#results').html("<div class='alert alert-success alert-dismissable'><a href='#'' class='close' data-dismiss='alert' aria-label='close'>×</a><strong>Success!</strong> Your request has been recored</div>");
console.log(json); // log the returned json to the console
console.log("success"); // another sanity check
},
// handle a non-successful response
error : function(xhr,errmsg,err) {
$('#results').html("<div class='alert alert-danger alert-dismissable'><a href='#'' class='close' data-dismiss='alert' aria-label='close'>×</a><strong>Oops!</strong> Something went wrong</div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
Like Bear Brown said, the data keep on the fields after an error, but how I wasn't using the pure forms from Django, I need to do some adjustments.
I created a hidden div with the origial Django forms and passed the data for my fields using JavaScript.
This is an example how I proceed:
The original Django forms has the id based on the field name on forms. So, if you define the name of the field on forms.py like "name", the id of the field will be "id_name":
function error(){ document.getElementById('name').value = document.getElementById('id_name').value;}
This is how the fields on form are called. After it's render, it will contains the data of the form field and have an id, so I get the element by id ("id_name") and tranfer the information for my personalizated field.
<div style="display: none;">
{{ form.name }}
</div>
This is the field with my stylization where the user will edit the data and make his own modifications.
<input id="name" class="form-control" type="text" maxlength="100" name="name" placeholder="Ex.: Metemática" onchange="slugDefine()" onkeyup="slugDefine()" /><br>
Thank you for your help!
If you do not want to use Javascript, you can use the value keyword in your HTML template. But ensure you have a return statement in your else statement when the form is not valid.
def new(request):
context = {}
if request.method == 'POST':
form = NewSubject(request.POST)
if form.is_valid():
context['is_valid'] = True
form.save()
form = NewSubject()
else:
context['is_valid'] = False
return render(request, 'subjects/new.html', context)
else:
form = NewSubject()
context['form'] = form
return render(request, 'subjects/new.html', context)
Then, in your HTML template, you can add the value="{{ form.billing_email.value }}" to your div tag. For example,
<div class="">
<label for="billing_email" class="">
Billing contact email:
</label>
<input
type="email"
class=""
name="billing_email"
id="billing_email"
placeholder="Text"
value="{{ form.billing_email.value }}"
/>
{% for error in form.billing_email.errors %}
<div class="">{{ error }}</div>
{% endfor %}
</div>
I'm a beginner learning FLASK. I'm making an app and for it I've created a DB model User, and an HTML/ JS form that takes input. What I want is to use the form information to create a new entry in the database but I am unsure on how to do it. I tried to do this
#app.route('/add_to_db')
def add_to_db():
email = request.form['email']
activated = 0;
user = models.User(email= email, activated = 0)
db.session.add(user)
db.session.commit()
HTML Code:
<form onsubmit="return validateEmail(document.getElementById('email').value)" action="{{ url_for("add_to_db") }}" method="post">
Please input your email adress: <input id="email">
<input type="submit">
</form>
<script>
function validateEmail(email) {
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
return(re.test(email));
}
</script>
But this gave me a 405 Method not allowed error.
A 405 error means "method not allowed". As you are sending form data, you are using a POST request and need to allow POST requests. By default only GET requests are allowed. Change the line #app.route('/add_to_db') to #app.route('/add_to_db', methods=['POST']).
I've provided CSRF in views.py and included csrf_token in templates, but still search is not working due to “csrf_token failure”.
My views.py code is:
args = {}
args.update(csrf(request))
args['articles']= Article.objects.all()
args['lang'] = language
args['session_language']=session_language
return render_to_response('articles.html', args)
and template code is
<h3>Search</h3>
{% csrf_token %}
<input type='text' id='search' name='search' />
The csrf_token is just the string that is required, you need to do something with it. For starters, capturing it within your template in script takes will mean javascript is "aware" of it.
{# Place this somewhere in your template #}
<script type="text/javascript">
var csrf_token = "{{ csrf_token }}";
</script>
After this, how you submit the AJAX request is up to you, but you can now use this as part of your POST submission.
You'll need to pass the csrf token value in every POST request made.
I am using Daxice library to create AJAX calls in my Django app.
When I create a POST method on a form I get the mentioned error:
Forbidden (403)
CSRF verification failed. Request aborted.
My settings.py have:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.request',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.csrf',
)
My urls.py
from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
dajaxice_autodiscover()
urlpatterns = patterns('',
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
url(r'^$', 'apps.views.home'),
)
urlpatterns += staticfiles_urlpatterns()
My views.py:
from django.http import HttpResponse
from django.template import loader, Context
from django.core.context_processors import csrf
def home(request):
t = loader.get_template('index.html')
html = t.render(Context( ))
return HttpResponse(html)
My template index.html:
{% load dajaxice_templatetags %}
<html>
<head>
<title>My base template</title>
<script src="http://code.jquery.com/jquery-latest.min.js"
type="text/javascript"></script>
{% dajaxice_js_import %}
<script type="text/javascript">
function shout(data){
alert(data.message)
}
</script>
</head>
<body>
<form method="POST" action="">{% csrf_token %}
Page: <input type="text" name="page"><br>
From: <input type="text" name="from"> (From < To) <br>
To: <input type="text" name="to"> (returns results before that day)<br>
<input type="submit" onclick="Dajaxice.apps.hello(shout);" value="Submit">
</form>
<br>
<br>
<input type="button" onclick="Dajaxice.apps.hello(shout);" value="Get message from server!">
</body>
</html>
And my ajax.py:
import simplejson
from dajaxice.decorators import dajaxice_register
#dajaxice_register(method='GET')
#dajaxice_register(method='POST', name='other_post')
def hello(request):
return simplejson.dumps({'message':'Hello from Python!'})
If I click the button, the message gets alerted. When I submit the form I get this error. How can I fix it?
Finally I believe I have fixed all the possibilities for the CSRF display in the debug page:
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function uses RequestContext for the template, instead of Context.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
Alright I think I got it. In the line:
<form>
...
<input type="submit" onclick="Dajaxice.apps.hello(shout);" value="Submit"></form>
...
</form>
If the type is button it works. It should be something with the submit behavior of the server request. I'm not an expert to explain why is this happening, so if somebody can explain I would gladly give the vote.
According to docs you can send csrf-token on every ajax-post request if you run this script first:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin 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 isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
It is written in jquery. If you want vanilla js, visit docs
Dajaxice does CSRF handling for you, you don't need any custom jQuery beforeSend code.
But you need to grant Dajaxice access to your CSRF cookie. Therefore:
Make sure CSRF_COOKIE_HTTPONLY is set to False in your settings.py!
I have a Ajax and template like this:
<html>
<body>
<script language="javascript" type="text/javascript">
<!--
//Browser Support Code
function ajaxFunction(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
document.myForm.time.value = ajaxRequest.responseText;
}
}
url = '/home'
ajaxRequest.open("GET", url, false);
ajaxRequest.send(null);
}
//-->
</script>
<form name='myForm'>
{% csrf_token %}
Name: <input type='text' onChange="ajaxFunction();" name='username' /> <br />
Time: <input type='text' name='time' id='time' value="" />
</form>
</body>
</html>
And I have simple views like this:
from django.shortcuts import render_to_response, HttpResponse
import simplejson
from django.template.context import RequestContext
import datetime
def home(request):
if request.GET:
a = datetime
return HttpResponse(simplejson.dumps(a), mimetype='application/json')
#return render_to_response('home.html', {'a':a}, context_instance=RequestContext(request))
else:
return render_to_response('home.html', context_instance=RequestContext(request))
Ajax is loading when I press enter but instead of the particular variable all the template is loading in the input box. What's wrong?
This line:
if request.GET:
checks to see if there are any GET parameters. There aren't, because you're not sending any. The URL is just /home. You could use if request.method == 'GET', but I think you're checking for the wrong thing here: a normal request (not Ajax) will also be GET.
What you should do is send the HTTP_X_REQUESTED_WITH header as "XmlHttpRequest", then check request.is_ajax() in the view. Or, as recommended, use a library like jQuery - which sets that for you automatically.
The proper way to detect request type is if request.method == 'GET', instead of if request.GET.