问题
I'm using the express middleware csurf for CSRF protection. If I'm using it with forms, where I put the token into a hidden field, the action behind the routes works. Now I want to make a simple AJAX call but there csurf says its invalid.
AJAX call:
$('.remove').on('click', function () {
var csrf = $(this).attr('data-csrf');
$.ajax({
type: 'DELETE',
url: '/user/' + $(this).attr('data-id'),
data: {
_csrf: csrf
},
success: function (data) {
//.....
}
});
});
And the part in the view:
<td class="uk-table-middle">
<button data-id="{{ _id }}" data-csrf="{{ csrfToken }}" class="uk-button-link uk-text-large remove">
<i class="uk-icon-remove"></i>
</button>
</td>
And the init from the middleware:
import * as csurf from 'csurf';
// init bodyparse and and and...
app.use(csurf());
回答1:
I don't know in express but usually the CSRF token is inside a cookie, so you will need these two functions:
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;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
And then:
var csrftoken = getCookie('csrftoken');
$.ajax({
url : formURL,
type: "POST",
data : postData,
beforeSend: function(xhr, settings){
if (!csrfSafeMethod(settings.type)) xhr.setRequestHeader("X-CSRFToken", csrftoken);
},
success:function(data, textStatus, jqXHR){
},
error: function(jqXHR, textStatus, errorThrown){
//if fails
}
});
Or if you don't want to use jQuery, you can use XMLHttpRequest to make the AJAX request:
var csrftoken = getCookie('csrftoken');
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader("X-CSRFToken", csrftoken);
xhr.onload = function(){
if(xhr.status === 200){
var response = JSON.parse(xhr.responseText);
console.log(response)
}
};
xhr.send(encodeURI('category=' + cat));
回答2:
The only reason why this method doesn't work is that you're not passing cookies with your ajax request. I was struggling when I figuring it out when I look into the code.
Csurf needs you to pass the secret key which stored on cookies (_csrf
). Cookies have a limitation through the permission based on the domain (except your server allowing CORS)
On my case, I use fetch
to passing the cookies with same domain request (i don't have to allow CORS)
const { _csrf, someData } = jsonData; // _csrf here you got from the form
const response = await fetch("/api/some/endpoint", {
method: "POST",
credentials: "same-origin", // here is the option to include cookies on this request
headers: {
"x-csrf-token": _csrf,
"Content-Type": "application/json"
},
body: JSON.stringify({ someData })
});
Please note the code above I'm using es6 format. You may change same-origin
with include
if you requesting to different domain a.k.a CORS see the doc here
If you using jQuery
as the library, this code could be useful to you. This code was untested and you should find out by yourself based on what library you use.
$.ajax({
url: 'http://your.domain.com/api/some/endpoint',
xhrFields: { withCredentials: true }, // this include cookies
headers: {'x-csrf-token': 'YourCSRFKey'}
})
Please be free to use your own names on the headers, post data or querystring when passing the _csrf
value. On my example above I'm using header with name x-csrf-token
, but you can use other method based on screenshot code below.
来源:https://stackoverflow.com/questions/39264129/csurf-ajax-call-invalid-csrf-token