Jiroscopes
Jiroscopes

Reputation: 555

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>

Upvotes: 1

Views: 4357

Answers (3)

Mario Nikolaus
Mario Nikolaus

Reputation: 2406

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.

Upvotes: 0

bozdoz
bozdoz

Reputation: 12870

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.

Upvotes: 1

Vitor Freitas
Vitor Freitas

Reputation: 3610

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

Upvotes: 3

Related Questions