Django CSRF tag for AJAX POST calls.

Το Django μας παρέχει (προαιρετικά) το CSRF middleware του οποίου η δουλειά είναι να αποτρέπει τις επιθέσεις CSRF. Για να το πετύχει αυτό, στα response object βρίσκει τις φόρμες με POST method και “εισάγει” ένα ακόμα κρυφό field που περιέχει ένα token μοναδικό για το session. Στα POST request επιβεβαιώνει οτι το token υπάρχει και είναι σωστό.

Όλα ωραία και καλά, αλλα αυτός ο τρόπος έχει το πρόβλημα ότι όταν θέλουμε να κάνουμε AJAX κλήσεις με POST requests δεν θα δουλέψουν γιατί το CSRF middelware δεν θα βρεί tokens.

Για να λύσω αυτό το πρόβλημα με τον πιό απλό τρόπο έφτιαξα ένα template tag που παρέχει ένα token στα template μου.

Έτσι, για παράδειγμα στο YUI όταν κάνω το request είναι κάπως έτσι:

1
2
3
4
{%csrf_token as token    %}
params = "ham="+ham+"&eggs="+eggs + "&csrfmiddlewaretoken={{token}}" ;
var transaction = YAHOO.util.Connect.asyncRequest("POST", {%url spam%},
    callback, params);

Μιας και βασίζεται στην ύπαρξη του sessionid στο cookie, αυτό το tag δεν θα μας δώσει token όταν ο χρήστης έχει απενεργοποιήσει τα cookies (κάτι που σπάει ένα σκασμό site οπότε δεν με πολυνοιάζει) και όταν είναι το πρώτο request στο site μας. (Μπορούμε όμως να ελέγξουμε αν υπάρχει το token και να πράξουμε κατάλληλα)

Ορίστε και το tag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from django.conf import settings
from django.utils.hashcompat import md5_constructor
import re

class CsrfNode(template.Node):
"""
Tag that provides a CSRF token in the context.
Usage: {% csrf_token as token %}
If the user has no cookies (cookies disabled, or this is
the first ever page view on the site, this tag will provide
an empty token. You can test that (eg to refresh the page)
"""

def __init__(self,var_name):
    self.var_name = var_name

def render(self,context):
    SESSION_COOKIE_NAME = getattr(settings,
            "SESSION_COOKIE_NAME","sessionid")
    sess_key = context['request'].COOKIES.get(SESSION_COOKIE_NAME,None)
    if not sess_key:
        # User has no cookie. Empty token.
        token = ""
    else:
        val = settings.SECRET_KEY + sess_key
        token = md5_constructor( val  ).hexdigest()
    context[self.var_name] = token
    return ''

@register.tag(name='csrf_token')
def do_csrf_token(parser,token):
    try:
        tag_name , params = token.contents.split(None,1)
    except ValueError:
        msg = "%r tag requires arguments" % token.contents.split()[0]
        raise template.TemplateSyntaxError(msg)
    m = re.search(r'^as (\w+)$', params)
    if not m:
        msg = "%r tag had invalid arguments" % tag_name
        raise template.TemplateSyntaxError(msg)
    var_name = m.groups()[0]
    return CsrfNode(var_name)

Ελπίζω να είναι bug free :p

Comments !

blogroll

social