Обработка CSRF / XSRF токенов с угловым фронтендом и бэкендом Drupal 7
Я в процессе создания нового интерфейса AngularJS для веб-сайта Drupal 7. Это использование модуля служб с сеансовой аутентификацией, в двух доменах, использующих CORS. Я могу аутентифицироваться с помощью Drupal, извлекать объект пользователя и данные сеанса, а затем получать маркер CSRF из модуля служб. С чем у меня возникли проблемы, так это с настройкой всего этого в заголовке, чтобы последующие запросы были аутентифицированы. Я понимаю общую концепцию, но я новичок в обоих случаях. AngularJS и предотвращение атак CSRF.
Из того, что я собрал, читая об этой настройке с AngularJS и RubyOnRails, могут быть несоответствия между платформами относительно того, как маркер называется и как он обрабатывается. Существует также ряд предложений о том, как установить этот маркер в заголовке. Однако мне трудно найти убедительный пример того, как заставить эти платформы говорить на одном языке.
Единственное, что я делаю с моим $httpProvider в приложении.js-это:
delete $httpProvider.defaults.headers.common['X-Requested-With'];
Контроллер входа в систему, в контроллере.js:
.controller('LoginCtrl', ['$scope', '$http', '$cookies', 'SessionService', function($scope, $http, $cookies, SessionService) {
$scope.login = function(user) {
//set login url and variables
var url = 'http://mywebsite.com/service/default/user/login.json';
var postDataString = 'name=' + encodeURIComponent(user.username) + '&pass=' + encodeURIComponent(user.password);
$http({
method: 'POST',
url: url,
data : postDataString,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (data, status, headers, config) {
var sessId = data.sessid;
var sessName = data.session_name;
$cookies[sessName] = sessId;
var xsrfUrl = 'http://mywebsite.com/services/session/token';
$http({
method: 'GET',
url: xsrfUrl
}).success(function (data, status, headers, config) {
$cookies["XSRF-TOKEN"] = data;
SessionService.setUserAuthenticated(true);
}).error(function (data, status, headers, config) {
console.log('error loading xsrf/csrf');
});
}).error(function (data, status, headers, config) {
if(data) {
console.log(data);
var msgText = data.join("n");
alert(msgText);
} else {
alert('Unable to login');
}
});
};
4 ответа:
Решение связано с тем, как файлы cookie должны быть установлены и затем переданы через последующие запросы. Попытки установить их вручную не увенчались успехом, но решение оказалось проще, чем я ожидал. Каждый вызов $http должен задать следующие параметры:
Еще одно изменение, которое я сделал, заключалось в использовании термина CSRF вместо XSRF, чтобы соответствовать Drupal. Я не использовать какие-либо встроенные функции в AngularJS от CSRF атак.withCredentials: true
addItem: function(data) { return $http.post('api/programs/'+$stateParams.id+'/workouts', {item:data},{ headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-CSRF-Token': $('meta[name="xxtkn"]').attr('content') } }); }
С тех пор как прошел год этой темы! не уверен, что все еще сталкиваюсь с той же проблемой, но для тех, кто приходит искать ответы вот как я справляюсь с этим! Обратите внимание на заголовки{} Часть i определите новый заголовок и назовите его X-CSRF-Token и захватите значение из DOM (serverside) сгенерированного html или php. Это не хорошая практика, чтобы также просить CSRF токен с сервера.Потому что атакующий мог каким-то образом запросить и это. Так как вы сохраняете его как печенье. Злоумышленник может украсть печенье! Нет необходимости сохранять его в печенье! отправьте маркер с заголовком и прочитайте его на стороне сервера, чтобы соответствовать ему!
И для мультитаб одного и того же выпуска страницы. Я использую один и тот же токен на протяжении всей сессии. Регенерируйте только при входе, выходе из системы и изменении основных настроек сайта или пользователя.
Есть отличная библиотека callse ng-drupal-7-services. Если вы используете это в вашем проекте, он решает аутентификацию / повторную аутентификацию и создание файла / узла aut коробки, и вы можете сосредоточиться на важных вещах в вашем проекте.
Итак, аутентификация там решается следующим образом:
function login(loginData) { //UserResource ahndles all requeste of the services 3.x user resource. return UserResource .login(loginData) .success(function (responseData, status, headers, config) { setAuthenticationHeaders(responseData.token); setLastConnectTime(Date.now()); setConnectionState((responseData.user.uid === 0)?false:true) setCookies(responseData.sessid, responseData.session_name); setCurrentUser(responseData.user); AuthenticationChannel.pubLoginConfirmed(responseData); }) .error(function (responseError, status, headers, config) { AuthenticationChannel.pubLoginFailed(responseError); }); }; (function() { 'use strict'; AuthenticationHttpInterceptor.$inject = [ '$injector']; function AuthenticationHttpInterceptor($injector) { var intercepter = { request : doRequestCongiguration, }; return intercepter; function doRequestCongiguration (config) { var tokenHeaders = null; // Need to manually retrieve dependencies with $injector.invoke // because Authentication depends on $http, which doesn't exist during the // configuration phase (when we are setting up interceptors). // Using $injector.invoke ensures that we are provided with the // dependencies after they have been created. $injector.invoke(['AuthenticationService', function (AuthenticationService) { tokenHeaders = AuthenticationService.getAuthenticationHeaders(); }]); //add headers_______________________ //add Authorisation and X-CSRF-TOKEN if given if (tokenHeaders) { angular.extend(config.headers, tokenHeaders); } //add flags_________________________________________________ //add withCredentials to every request //needed because we send cookies in our request headers config.withCredentials = true; return config; };
Существует также своего рода кухонная раковина для этого проекта здесь: Drupal-API-Explorer
Да, каждая платформа имеет свое собственное соглашение в именовании своих токенов.
Вот небольшая библиотека, собранная вместе в надежде сделать ее простой в использовании с различными платформами. Это позволит вам использовать имена наборов и может использоваться во всех запросах. Он также работает для междоменных запросов.