221 votes

Vérification de Django CSRF échouent avec une demande POST Ajax

Je pourrais utiliser un peu d'aide conformité avec Django csrf mécanisme de protection par l'intermédiaire de mon AJAX post. J'ai suivi les instructions ici:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/

J'ai copié l'AJAX exemple de code qu'ils ont sur cette page exactement:

http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax

J'ai mis une alerte de l'impression de getCookie('csrftoken") devant le xhr.setRequestHeader appel et c'est effectivement rempli avec quelques-unes des données. Je ne suis pas sûr de la façon de vérifier que le jeton est correct, mais je suis encouragé par le fait que c'est de la trouver et de l'envoi de quelque chose.

Mais django est toujours en rejetant ma AJAX post.

Voici mon code Javascript:

$.post("/memorize/", data, function (result) {
    if (result != "failure") {
        get_random_card();
    }
    else {
        alert("Failed to save card data.");
    }
});

Voici l'erreur que je vois de Django:

[23/Fév/2011 22:08:29] "POST /mémoriser/ HTTP/1.1" 403 2332

Je suis sûr que je suis absent quelque chose, et c'est peut-être simple, mais je ne sais pas ce que c'est. J'ai cherché autour de SORTE et j'ai vu quelques informations sur la désactivation des CSRF vérifier ma vue par la csrf_exempt décorateur, mais je trouve que désagréable. J'ai essayé et cela fonctionne, mais je préfère obtenir mon POSTE de travail, la façon de Django a été conçu pour l'attendre, si possible.

Juste au cas où il est utile, voici l'essentiel de ce que mon point de vue est en train de faire:

def myview(request):

    profile = request.user.profile

    if request.method == 'POST':
        """
        Process the post...
        """
        return HttpResponseRedirect('/memorize/')
    else: # request.method == 'GET'

        ajax = request.GET.has_key('ajax')

        """
        Some irrelevent code...
        """

        if ajax:
            response = HttpResponse()
            profile.get_stack_json(response)
            return response
        else:
            """
            Get data to send along with the content of the page.
            """

        return render_to_response('memorize/memorize.html',
                """ My data """
                context_instance=RequestContext(request))

Merci pour vos réponses!

207voto

Bryan Points 978

Si vous utilisez la fonction de $.ajax, vous pouvez simplement ajouter le jeton csrf dans le corps de données :

196voto

Jakub Gocławski Points 1586

Véritable solution

Ok, j'ai réussi à retracer l'origine du problème. Il se trouve dans le Javascript (comme je l'ai suggéré ci-dessous) code.

Ce que vous avez besoin est cela:

$.ajaxSetup({ 
     beforeSend: function(xhr, settings) {
         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 = jQuery.trim(cookies[i]);
                     // 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;
         }
         if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
             // Only send the token to relative URLs i.e. locally.
             xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
         }
     } 
});

au lieu du code publié dans l'officiel docs: http://docs.djangoproject.com/en/1.2/ref/contrib/csrf/#ajax

Le code de travail, vient de ce Django entrée: http://www.djangoproject.com/weblog/2011/feb/08/security/

Donc la solution générale est: "l'utilisation ajaxSetup gestionnaire au lieu de ajaxSend gestionnaire". Je ne sais pas pourquoi cela fonctionne. Mais ça fonctionne pour moi :)

Post précédent (sans réponse)

Je suis en train de vivre le même problème en fait.

Il se produit après la mise à jour de Django 1.2.5 - il n'y a pas d'erreur avec l'AJAX requêtes POST dans Django 1.2.4 (AJAX n'était pas protégé en aucune façon, mais il a très bien fonctionné).

Tout comme l'OP, j'ai essayé le code JavaScript posté dans la documentation de Django. Je suis à l'aide de jQuery 1.5. Je suis également en utilisant le "django.middleware.csrf.CsrfViewMiddleware" middleware.

J'ai essayé de suivre le middleware code et je sais qu'il échoue sur ceci:

request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

et puis

if request_csrf_token != csrf_token:
    return self._reject(request, REASON_BAD_TOKEN)

ce "si" est vrai, parce que "request_csrf_token" est vide.

Fondamentalement, cela signifie que l'en-tête n'est PAS définie. Donc, il y a quelque chose de mal avec ce JS ligne:

xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

?

J'espère que les détails fournis vont nous aider à résoudre le problème :)

79voto

Kambiz Points 626

Ajoutez cette ligne à votre code jQuery :

et de fait.

20voto

fatgeekuk Points 235

Le problème est que django est attendent à ce que la valeur du cookie qui sera transmis dans le cadre de la forme de données. Le code de la réponse précédente est prise en javascript pour dénicher la valeur du cookie et de le mettre en forme des données. C'est une belle façon de le faire à partir d'un point de vue technique, mais il ressemble un peu verbeux.

Dans le passé, je l'ai fait plus simplement par l'obtention de l'javascript pour mettre le jeton de la valeur dans les données post.

Si vous utilisez {% csrf_token %} dans votre modèle, vous recevrez une forme cachée champ émis qui porte la valeur. Mais, si vous utilisez {{ csrf_token }} vous obtenez juste le nu de la valeur du jeton, de sorte que vous pouvez l'utiliser en javascript comme ceci....

csrf_token = "{{ csrf_token }}";

Ensuite, vous pouvez l'inclure, avec le nom de la clé dans la table de hachage vous ensuite de soumettre les données de l'appel ajax.

8voto

GivP Points 971

Si votre formulaire enregistre correctement dans Django sans JS, vous devriez être en mesure de l’améliorer progressivement avec l’ajax sans piratage ni passage désordonné du jeton csrf. Juste sérialiser tout le formulaire et qui ira chercher automatiquement toutes vos formulaire champs dont le champ masqué csrf :

J’ai testé avec Django 1.3 + et jQuery 1.5 +. Évidemment, cela ne fonctionnera pas pour n’importe quel formulaire HTML, pas seulement les applications Django.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X