Как реализовать защиту CSRF в вызовах Ajax с помощью express.js(ищите полный пример)?
Я пытаюсь реализовать защиту CSRF в приложении, построенном с использованием node.js, с использованием рамки express.js. Приложение использует изобилие почтовых вызовов Ajax на сервере. Я понимаю, что инфраструктура connect обеспечивает промежуточное ПО CSRF, но я не уверен, как ее реализовать в рамках клиентских Ajax-запросов.
В других вопросах, размещенных здесь в stackoverflow, есть бит и куски, но мне еще предстоит найти достаточно полный пример того, как его реализовать как со стороны клиента, так и с сервера.
Есть ли у кого-нибудь рабочий пример, которому они хотят поделиться тем, как это реализовать? Большинство примеров, которые я видел, предполагают, что вы передаете форму на серверной стороне, а затем отправляете ее (вместе со встроенным полем формы csrf_token) на клиентскую сторону. В моем приложении все содержимое отображается на стороне клиента (включая шаблоны) через Backbone.js. Весь сервер предоставляет значения в формате JSON, которые используются на разных моделях в Backbone.js на стороне клиента. По моему мнению, мне нужно будет сначала восстановить csrf_token через ajax, прежде чем его можно будет использовать. Однако я обеспокоен тем, что это может быть проблематичным с точки зрения безопасности. Является ли это серьезной проблемой?
Ответы
Ответ 1
Это можно сделать, добавив тег meta
для токена CSRF, а затем передайте токен CSRF с каждым запросом Ajax
Сервер
Добавить промежуточное ПО CSRF
app.use(express.csrf());
app.use(function (req, res, next) {
res.locals.token = req.session._csrf;
next();
});
Вы можете передать токен CSRF на клиентскую сторону через, скажем, метатег. Например, в Jade
meta(name="csrf-token", content="#{token}")
Client
jQuery имеет функцию ajaxPrefilter, которая позволяет вам вызывать обратный вызов для каждого запроса Ajax. Затем установите заголовок с помощью ajaxPrefilter.
var CSRF_HEADER = 'X-CSRF-Token';
var setCSRFToken = function (securityToken) {
jQuery.ajaxPrefilter(function (options, _, xhr) {
if (!xhr.crossDomain) {
xhr.setRequestHeader(CSRF_HEADER, securityToken);
}
});
};
setCSRFToken($('meta[name="csrf-token"]').attr('content'));
Ответ 2
server.js
...
// All Cookies/Sessions/BodyParser go first
app.use(express.csrf());
...
// Get the request
app.post('/ajax', function(req, res){
res.render('somelayout', {csrf_token: req.session._csrf});
});
В somelayout.jade
input(type='hidden', name='_csrf', value=csrf_token)
Среднее ПО CSRF генерирует токен csrf один раз за сеанс, поэтому он, вероятно, не изменится на время посещения пользователя.
Кроме того, он не проверяет токен на запросах GET и HEAD. Пока токен находится в запросе (заголовок, тело или запрос), вы хороши. Это почти все, что есть.
Ответ 3
Поскольку вы используете Backbone.js
для своего приложения, я предполагаю, что это SPA, и вы сначала загружаете файл index.html
, а затем делаете любые другие запросы с помощью вызовов ajax
. Если это так, вы можете добавить небольшой фрагмент кода JS в ваш файл index.html
для хранения токена crsf для любых будущих вызовов ajax
.
Например:
index.html (с помощью Handlebars для templating...)
<!DOCTYPE html>
<html>
<head>
...
<script type="text/javascript">
$( function() {
window.Backbone.csrf = "{{csrfToken}}";
});
</script>
</head>
<body>
...
</body>
</html>
При рендеринге файла index.html
присвойте ему токен csrf
, который генерирует здесь выражаемая структура: req.session._csrf
Когда вы используете Backbone.js
, он устанавливает глобальную переменную с именем Backbone
. Все, что делает предыдущая функция, - это установить свойство, называемое csrf
глобальному объекту Backbone
. И когда вы вызываете ajax
вызов POST
данных, просто добавьте переменную Backbone.csrf
в данные как _csrf
, которая отправляется по вызову ajax
.
Ответ 4
В сервере:
app.use(function (req, res) {
res.locals._csrf = req.csrfToken();
res.locals.csrf_form_html = '<input type="hidden" name="_csrf" value="' + req.csrfToken() + '" >';
req.next();
});
В клиенте: (шаблон swig)
var csrf = {{ _csrf|json|safe }};
$.ajaxSetup({
headers: {
'X-CSRF-Token': csrf
}
});
$.post("/create", data, function(result) {
console.log(result);
}).fail(function(){
console.log(arguments);
});
Ответ 5
1. Добавить промежуточное ПО для защиты csrf:
app.use(csrf({cookie: true}));
// csrf middleware
app.use(function (req, res, next) {
res.cookie('X-CSRF-Token', req.csrfToken());
// this line below is for using csrfToken value in normal forms (as a hidden input)
res.locals.csrfToken = req.csrfToken();
next();
});
// routing setup goes here
2. Добавьте обратный вызов beforeSend
, используя $.ajaxSetup
: (добавьте это где-нибудь перед всеми вашими вызовами ajax)
$.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-CSRF-Token", getCookie('X-CSRF-Token'));
}
}
});
3. Что это! теперь вы можете отправлять ajax-запросы, и вам не нужно ничего добавлять в заголовках или в качестве параметра запроса для прохождения через csrf.