Аутентификация с помощью AngularJS, управление сеансами и вопросы безопасности с помощью REST Api WS
Я начал разрабатывать веб-приложение с angularJS, и я не уверен, что все правильно защищено (на стороне клиента и сервера). Безопасность основана на одной странице входа, если учетные данные проверяются ОК, мой сервер отправляет обратно уникальный токен с пользовательским сроком действия. Все остальные REST api доступны через этот маркер. Приложение (клиент) перейдите к моей точке входа ex:https://www.example.com/home.html пользователь вставляет учетные данные и получает обратно уникальный токен. Этот уникальный токен хранится в базе данных сервера с помощью AES или других безопасных методов, он не хранится в открытом формате.
С этого момента мое приложение AngluarJS будет использовать этот маркер для аутентификации во всех REST Api.
Я думаю о временном хранении токена в пользовательском файле cookie http; в основном, когда сервер проверяет учетные данные, он отправляет обратно новый файл cookie Ex.
app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T
печенье имеет безопасное и только HTTP флагов на. Протокол Http непосредственно управляет новым файлом cookie и сохраняет его. Последовательные запросы будут представлять файл cookie с новым параметром, без необходимости управлять им и хранить его с помощью javascript; при каждом запросе сервер аннулирует токен и генерирует новый и отправляет его обратно клиенту --> предотвратить replay-атаки с помощью одного токена.
когда клиент получает статус HTTP 401 несанкционированный ответ от любого REST Api, угловой контроллер очистить все куки и перенаправить пользователя на страницу входа в систему.
должен ли я рассмотреть другие аспекты? Лучше ли хранить маркер внутри нового файла cookie или в localStorage? Любые советы о том, как создать уникальный сильный знак?
изменить (улучшения):
- я решил использовать HMAC-SHA256 в качестве генератора токенов сеанса с 20-минутной действительностью. Я сгенерировать случайный идентификатор GUID 32byte, прикрепить метку и вычислить хэш-значение SHA256, предоставляя Ключ 40 байт. Совершенно невозможно получить коллизии, так как действительность токена довольно минимальна.
- печенье будет домен и путь атрибуты для повышения безопасности.
- мульти-логины не допускаются.
3 ответа:
Если вы говорите с сервером через https, у вас нет проблем с повторными атаками.
мое предложение было бы использовать технологии безопасности вашего сервера. Например, JavaEE имеет механизм входа из коробки, декларативную защиту ресурсов на основе ролей (ваши конечные точки REST) и т. д. Все они управляются с помощью набора файлов cookie, и вам не нужно заботиться о хранении и истечении срока действия. Проверьте, что ваш сервер/фреймворк уже дает вам.
Если вы планируете чтобы предоставить свой API более широкой аудитории (не специально для пользовательского интерфейса на основе браузера, который вы обслуживаете) или других типов клиентов (например, мобильных приложений), рассмотрите возможность принятия OAuth.
С верхней части моей головы, угловой имеет следующие функции безопасности (добавит больше, как они выскакивают):
CSRF/XSRF атаки
угловые поддержки из механизма коробки для CSRF защита. Проверьте
$http
docs. Серверный необходима поддержка.Политика Безопасности Контента
Angular имеет режим оценки выражений, совместимый с более строгими средами выполнения JavaScript, которые применяются, когда CSP это. Проверьте
ng-csp
docs.Строгий Контекстной Побега
использовать новый угловой
$sce
функции (1.2+), чтобы укрепиться вам пользовательского интерфейса от XSS-атак и т. д. Это немного менее удобно, но более безопасный. Проверьте документы здесь.
это безопасность на стороне клиента, которую вы можете реализовать в обычных угловых версиях. Я пробовал и проверял это. (Пожалуйста, найдите мою статью здесь:- http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security ) В дополнение к безопасности маршрута на стороне клиента, вам также необходимо обеспечить доступ на стороне сервера. Безопасность на стороне клиента помогает избежать дополнительной поездки туда и обратно на сервер. Однако, если кто-то обманывает браузер , то сервер безопасности на стороне сервера должен быть в состоянии отклонить несанкционированный доступ.
надеюсь, что это помогает!
Шаг 1: Определите глобальные переменные в app-module
-определение ролей для приложения
var roles = { superUser: 0, admin: 1, user: 2 };
- определить маршрут несанкционированного доступа для приложения
var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';
Шаг 2: Определите сервис для авторизации
appModule.factory('authorizationService', function ($resource, $q, $rootScope, $location) { return { // We would cache the permission for the session, to avoid roundtrip to server for subsequent requests permissionModel: { permission: {}, isPermissionLoaded: false }, permissionCheck: function (roleCollection) { // we will return a promise . var deferred = $q.defer(); //this is just to keep a pointer to parent scope from within promise scope. var parentPointer = this; //Checking if permisison object(list of roles for logged in user) is already filled from service if (this.permissionModel.isPermissionLoaded) { //Check if the current user has required role to access the route this.getPermission(this.permissionModel, roleCollection, deferred); } else { //if permission is not obtained yet, we will get it from server. // 'api/permissionService' is the path of server web service , used for this example. $resource('/api/permissionService').get().$promise.then(function (response) { //when server service responds then we will fill the permission object parentPointer.permissionModel.permission = response; //Indicator is set to true that permission object is filled and can be re-used for subsequent route request for the session of the user parentPointer.permissionModel.isPermissionLoaded = true; //Check if the current user has required role to access the route parentPointer.getPermission(parentPointer.permissionModel, roleCollection, deferred); } ); } return deferred.promise; }, //Method to check if the current user has required role to access the route //'permissionModel' has permission information obtained from server for current user //'roleCollection' is the list of roles which are authorized to access route //'deferred' is the object through which we shall resolve promise getPermission: function (permissionModel, roleCollection, deferred) { var ifPermissionPassed = false; angular.forEach(roleCollection, function (role) { switch (role) { case roles.superUser: if (permissionModel.permission.isSuperUser) { ifPermissionPassed = true; } break; case roles.admin: if (permissionModel.permission.isAdministrator) { ifPermissionPassed = true; } break; case roles.user: if (permissionModel.permission.isUser) { ifPermissionPassed = true; } break; default: ifPermissionPassed = false; } }); if (!ifPermissionPassed) { //If user does not have required access, we will route the user to unauthorized access page $location.path(routeForUnauthorizedAccess); //As there could be some delay when location change event happens, we will keep a watch on $locationChangeSuccess event // and would resolve promise when this event occurs. $rootScope.$on('$locationChangeSuccess', function (next, current) { deferred.resolve(); }); } else { deferred.resolve(); } } }; });
Шаг 3: Использование безопасности в маршрутизации: позволяет использовать использовать все наши hardword сделано до сих пор, чтобы обеспечить маршруты
var appModule = angular.module("appModule", ['ngRoute', 'ngResource']) .config(function ($routeProvider, $locationProvider) { $routeProvider .when('/superUserSpecificRoute', { templateUrl: '/templates/superUser.html',//path of the view/template of route caseInsensitiveMatch: true, controller: 'superUserController',//angular controller which would be used for the route resolve: {//Here we would use all the hardwork we have done above and make call to the authorization Service //resolve is a great feature in angular, which ensures that a route controller(in this case superUserController ) is invoked for a route only after the promises mentioned under it are resolved. permission: function(authorizationService, $route) { return authorizationService.permissionCheck([roles.superUser]); }, } }) .when('/userSpecificRoute', { templateUrl: '/templates/user.html', caseInsensitiveMatch: true, controller: 'userController', resolve: { permission: function (authorizationService, $route) { return authorizationService.permissionCheck([roles.user]); }, } }) .when('/adminSpecificRoute', { templateUrl: '/templates/admin.html', caseInsensitiveMatch: true, controller: 'adminController', resolve: { permission: function(authorizationService, $route) { return authorizationService.permissionCheck([roles.admin]); }, } }) .when('/adminSuperUserSpecificRoute', { templateUrl: '/templates/adminSuperUser.html', caseInsensitiveMatch: true, controller: 'adminSuperUserController', resolve: { permission: function(authorizationService, $route) { return authorizationService.permissionCheck([roles.admin,roles.superUser]); }, } }) });
app/js/app.js ------------- 'use strict'; // Declare app level module which depends on filters, and services var app= angular.module('myApp', ['ngRoute']); app.config(['$routeProvider', function($routeProvider) { $routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'loginCtrl'}); $routeProvider.when('/home', {templateUrl: 'partials/home.html', controller: 'homeCtrl'}); $routeProvider.otherwise({redirectTo: '/login'}); }]); app.run(function($rootScope, $location, loginService){ var routespermission=['/home']; //route that require login $rootScope.$on('$routeChangeStart', function(){ if( routespermission.indexOf($location.path()) !=-1) { var connected=loginService.islogged(); connected.then(function(msg){ if(!msg.data) $location.path('/login'); }); } }); }); app/js/controller/loginCtrl.js ------------------------------- 'use strict'; app.controller('loginCtrl', ['$scope','loginService', function ($scope,loginService) { $scope.msgtxt=''; $scope.login=function(data){ loginService.login(data,$scope); //call login service }; }]); app/js/directives/loginDrc.js ----------------------------- 'use strict'; app.directive('loginDirective',function(){ return{ templateUrl:'partials/tpl/login.tpl.html' } }); app/js/services/sessionService.js --------------------------------- 'use strict'; app.factory('sessionService', ['$http', function($http){ return{ set:function(key,value){ return sessionStorage.setItem(key,value); }, get:function(key){ return sessionStorage.getItem(key); }, destroy:function(key){ $http.post('data/destroy_session.php'); return sessionStorage.removeItem(key); } }; }]) app/js/services/loginService ---------------------------- 'use strict'; app.factory('loginService',function($http, $location, sessionService){ return{ login:function(data,scope){ var $promise=$http.post('data/user.php',data); //send data to user.php $promise.then(function(msg){ var uid=msg.data; if(uid){ //scope.msgtxt='Correct information'; sessionService.set('uid',uid); $location.path('/home'); } else { scope.msgtxt='incorrect information'; $location.path('/login'); } }); }, logout:function(){ sessionService.destroy('uid'); $location.path('/login'); }, islogged:function(){ var $checkSessionServer=$http.post('data/check_session.php'); return $checkSessionServer; /* if(sessionService.get('user')) return true; else return false; */ } } }); index.html ---------- <!doctype html> <html lang="en" ng-app="myApp"> <head> <meta charset="utf-8"> <title>My AngularJS App</title> <link rel="stylesheet" href="css/app.css"/> </head> <body> <div ng-view></div> <!-- In production use: <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script> --> <script src="lib/angular/angular.js"></script> <script src="lib/angular/angular-route.js"></script> <script src="js/app.js"></script> <script src="js/directives/loginDrc.js"></script> <script src="js/controllers/loginCtrl.js"></script> <script src="js/controllers/homeCtrl.js"></script> <script src="js/services/loginService.js"></script> <script src="js/services/sessionService.js"></script> </body> </html>