web-dev-qa-db-fra.com

Comment transmettre un jeton CSRF dans un AJAX poster une demande de formulaire?

J'utilise Scala Play! 2.6 Cadre, mais cela peut ne pas être le problème. J'utilise leur routage Javascript - et cela semble fonctionner correctement, mais il y a des problèmes. J'ai un formulaire qui, une fois rendu, produit ceci, avec un jeton CSRF:

<form method="post" id="myForm" action="someURL">

<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

Et voici à peu près, mon AJAX:

$(document).on('submit', '#myForm', function (event) {

 event.preventDefault();
   var data = {
    textvalue: $('#sometext').val()
   }
 var route = jsRoutes.controllers.DashboardController.postNewProject()
 $.ajax({
    url: route.url,
    type: route.type,
    data : JSON.stringify(data),
    contentType : 'application/json',
    success: function (data) { ...      },
    error: function (data) { ...  }
        })

});

Mais lorsque je publie ceci, je reçois une réponse NON AUTORISÉE de mon serveur et ma console dans IntelliJ me dit que la vérification CSRF échoue. Comment pourrais-je transmettre le jeton CSRF dans la demande?

9
NateH06

Ok, après avoir combattu pendant quelques heures et essayé de déchiffrer la documentation-contexte souvent manquante de Play sur le sujet , je l'ai. 

Donc, à partir de leurs documents:

Pour permettre une protection simple des requêtes autres que celles du navigateur, lisez uniquement les vérifications demandes avec des cookies dans l'en-tête. Si vous faites des demandes avec AJAX, vous pouvez placer le jeton CSRF dans la page HTML, puis l'ajouter à la demande en utilisant l'en-tête Csrf-Token.

Et puis il n'y a pas de code ni d'exemple. Merci jouer. Très descriptif. Quoi qu'il en soit, voici comment:

dans votre view.html.formTemplate, vous pourriez écrire dans IntelliJ:

@()
<form method="post" id="myForm" action="someURL">

@helper.CSRF.formField  <!-- This auto-generates a token for you -->
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

Et cela sera rendu comme ceci lors de la livraison au client:

<form method="post" id="myForm" action="someURL">

<input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">
  <input type="text" id="sometext">
  <button type="submit"> Submit! </button>

</form>

Ok, presque là-bas, nous devons maintenant créer notre appel AJAX. J'ai tous les miens dans un fichier main.js séparé, mais vous pouvez aussi le mettre dans votre view.html.formTemplate si vous le souhaitez.

$(document).on('submit', '#myForm', function (event) {

 event.preventDefault();
   var data = {
    myTextToPass: $('#sometext').val()
   }
 // LOOK AT ME! BETWEEN HERE AND
 var token =  $('input[name="csrfToken"]').attr('value')
    $.ajaxSetup({
        beforeSend: function(xhr) {
            xhr.setRequestHeader('Csrf-Token', token);
        }
    });
// HERE
 var route = jsRoutes.controllers.DashboardController.postNewProject()
 $.ajax({
    url: route.url,
    type: route.type,
    data : JSON.stringify(data),
    contentType : 'application/json',
    success: function (data) { ...      },
    error: function (data) { ...  }
        })

});

Avec cette ligne: var token = $('input[name="csrfToken"]').attr('value')Vous extrayez le jeton CSRF généré automatiquement dans votre champ de formulaire et saisissez sa valeur dans une variable à utiliser dans votre Javascript. 

L'autre élément important de tout ce que AJAX est ici:

$.ajaxSetup({
            beforeSend: function(xhr) {
                xhr.setRequestHeader('Csrf-Token', token);
            }
        });

À l'aide du $.ajaxSetup, vous pouvez définir le contenu de l'en-tête. Voici ce que vous devez déduire de leur documentation:

ajoutez-le à la demande en utilisant l'en-tête Csrf-Token.

Bonne chance! Faites-moi savoir si c'est clair. 


Note: lorsque vous utilisez lusca , utilisez X-CSRF-Token au lieu de Csrf-Token.

12
NateH06

ajoutez-le à la demande en utilisant l'en-tête Csrf-Token.

Merci NateH06 pour le nom de l'en-tête! J'essayais d'envoyer un jeton csrf pour un bouton "supprimer" avec un appel de fonction ajax et j'étais bloqué sur ce qui suit:

@import helper._
....
<button id="deleteBookBtn" class="btn btn-danger"
        data-csrf-name="@helper.CSRF.getToken.name"
        data-csrf-value="@helper.CSRF.getToken.value"
        data-delete-url="@routes.BooksController.destroy(book.id)"
        data-redirect-url="@routes.HomeController.index()">Delete</button>

Je n'ai pas pu ajouter de js en ligne au sein de l'événement onclick() à cause d'un jeu de paramètres CSP défini sur le jeu 2.6.

Refusé d'exécuter le gestionnaire d'événements en ligne car il enfreint la directive suivante relative à la politique de sécurité du contenu: "default-src 'self'".

Et sur le fichier JS:

function sendDeleteRequest(event) {
  url = event.target.getAttribute("data-delete-url")
  redirect = event.target.getAttribute("data-redirect-url")
  csrfTokenName = event.target.getAttribute("data-csrf-name")
  csrfTokenValue = event.target.getAttribute("data-csrf-value")
  $.ajax({
    url: url,
    method: "DELETE",
    beforeSend: function(request) {
      //'Csrf-Token' is the expected header name, not $csrfTokenName
      request.setRequestHeader(/*$csrfTokenName*/'Csrf-Token', csrfTokenValue);
    },
    success: function() {
      window.location = redirect;
    },
    error: function() {
      window.location.reload();
    }
  })
}

var deleteBookBtn = document.getElementById("deleteBookBtn");
if(deleteBookBtn) {
    deleteBookBtn.addEventListener("click", sendDeleteRequest);
}

Après avoir défini le nom de l'en-tête sur 'Csrf-Token', l'appel ajax fonctionne parfaitement!

2
inieto

De JSP 

<form method="post" id="myForm" action="someURL">
    <input name="csrfToken" value="5965f0d244b7d32b334eff840...etc" type="hidden">    
</form>

C'est la manière la plus simple qui a fonctionné pour moi après 3h de lutte: récupérez simplement le jeton dans le champ masqué d'entrée et, tout en faisant la demande AJAX, il suffit de passer ce jeton dans l'en-tête comme suit:

De JQuery

var token =  $('input[name="csrfToken"]').attr('value'); 

De Javascript simple

var token = document.getElementsByName("csrfToken").value;

Demande finale AJAX 

$.ajax({
      url: route.url,
      data : JSON.stringify(data),
      method : 'POST',
      headers: {
                    'X-CSRF-Token': token 
               },
      success: function (data) { ...      },
      error: function (data) { ...  }

});

Désormais, vous n'avez plus besoin de désactiver la sécurité crsf dans la configuration Web et cela ne vous donnera pas d'erreur 405 (méthode non autorisée) sur la console.

J'espère que cela aidera les gens .. !!

1
MOnkey

Vous pouvez ajouter un en-tête Csrf-Token avec l'option headers.

$.ajax({
    url: '@routes.MyController.myPostAction()',
    method: 'post',
    headers: {
        'Csrf-Token': '@play.filters.csrf.CSRF.getToken.map(_.value)'
    },
    data: {
        name: '@name'
    },
    success: function (data, textStatus, jqXHR) {
        location.reload();
    },
    error: function (jqXHR, textStatus, errorThrown) {
        debugger;
    }
});
0
Aha00a

Comme indiqué dans la documentation Play Framework 2.6 , vous pouvez définir un en-tête 'Csrf-Token' avec le jeton généré par Play:

Si vous faites des demandes avec AJAX, vous pouvez placer le jeton CSRF dans la page HTML, puis l'ajouter à la demande à l'aide de l'en-tête Csrf-Token.

Dans un modèle Scala, vous pouvez obtenir la valeur de jeton à l'aide de @helper.CSRF.getToken.value

Après la documentation jQuerys , vous pouvez soit le définir une fois pour toutes les demandes Ajax en configurant jQuery à l'aide de ajaxSetup

$.ajaxSetup({
  beforeSend: function(xhr) {
    xhr.setRequestHeader('Csrf-Token', '@helper.CSRF.getToken.value');
  }
});

ou bien définissez l'en-tête sur chaque requête en configurant l'objet headers comme ceci:

$.ajax({
  url: route.url,
  ...
  headers: {
    'Csrf-Token': '@helper.CSRF.getToken.value'
  }
});
0
bmenzel