Authentification avec AngularJS, gestion de session et problèmes de sécurité avec REST Api WS

J’ai commencé à développer une application Web avec angularJS et je ne suis pas sûr que tout soit correctement sécurisé (côté client et serveur). La sécurité repose sur une seule page de connexion, si les informations d’identification sont vérifiées, mon serveur renvoie un jeton unique avec une validité temporelle personnalisée. Toutes les autres API REST sont accessibles via ce jeton. L’application (client) accède à mon point d’entrée: https://www.example.com/home.html, l’ utilisateur insère les informations d’identification et reçoit un jeton unique. Ce jeton unique est stocké dans la firebase database du serveur avec AES ou d’autres techniques sécurisées, il n’est pas stocké dans un format clair.

À partir de maintenant, mon application AngluarJS utilisera ce jeton pour s’authentifier sur tous les Api REST exposés.

Je pense au stockage temporaire du jeton dans un cookie http personnalisé; fondamentalement, lorsque le serveur vérifie les informations d’identification, il renvoie un nouveau cookie Ex.

app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T 

Le cookie contient les indicateurs sécurisés et HTTP uniquement . Le protocole HTTP gère directement le nouveau cookie et le stocke. Les requêtes successives présenteront le cookie avec le nouveau paramètre, sans qu’il soit nécessaire de le gérer et de le stocker avec javascript; à chaque requête, le serveur invalide le jeton et en génère un nouveau et le renvoie au client -> empêcher les attaques par rejeu avec un seul jeton.

Lorsque le client reçoit une réponse HTTP 401 non autorisée de la part de n’importe quel Api REST, le contrôleur angular nettoie tous les cookies et redirige l’utilisateur vers la page de connexion.

Devrais-je envisager d’autres aspects? Est-il préférable de stocker le jeton dans un nouveau cookie ou dans localStorage? Des astuces pour générer un jeton unique?

Modifier (améliorations):

  • J’ai décidé d’utiliser HMAC-SHA256 comme générateur de jetons de session, avec une validité de 20 minutes. Je génère un GUID aléatoire de 32 octets, attache un horodatage et calcule le HASH-SHA256 en fournissant une clé de 40 octets. Il est tout à fait impossible d’obtenir des collisions car la validité des jetons est assez minime.
  • Les cookies auront des atsortingbuts de domaine et de chemin d’access pour augmenter la sécurité.
  • Aucune connexion multiple n’est autorisée.

Si vous parlez au serveur via https, vous n’avez pas de problème avec les attaques par relecture.

Ma suggestion serait de tirer parti de la technologie de sécurité de votre serveur. Par exemple, JavaEE a un mécanisme de connexion prêt à l’emploi, une protection déclarative des ressources basée sur les rôles (vos points de terminaison REST), etc. Ils sont tous gérés avec un ensemble de cookies et vous n’avez pas à vous soucier du stockage. expiration. Découvrez ce que votre serveur / framework vous offre déjà.

Si vous prévoyez d’exposer votre API à un public plus large (et non spécifiquement à l’interface utilisateur basée sur un navigateur que vous hébergez) ou à d’autres types de clients (application mobile, par exemple), envisagez d’adopter OAuth.

Au sumt de ma tête, Angular a les fonctionnalités de sécurité suivantes (appenda plus quand ils apparaissent):

Attaques CSRF / XSRF

Angular prend en charge un mécanisme prêt à l’ emploi pour la protection CSRF . Découvrez $http docs . Le support côté serveur est nécessaire.

Politique de sécurité du contenu

Angular dispose d’un mode d’évaluation de l’expression compatible avec les environnements d’exécution JavaScript plus ssortingcts appliqués lorsque le CSP est activé. Découvrez ng-csp docs .

Échappement contextuel ssortingct

Utilisez la nouvelle fonctionnalité $sce d’ $sce (1.2+) pour renforcer votre interface utilisateur contre les attaques XSS, etc. C’est un peu moins pratique mais plus sécurisé. Découvrez les documents ici .

C’est la sécurité côté client que vous pouvez implémenter dans les versions Angular standard. J’ai essayé et testé cela. (Veuillez trouver mon article ici: – http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security ) Outre la sécurité des routes côté client, vous devez également sécuriser l’access côté serveur. La sécurité côté client permet d’éviter un aller-retour supplémentaire au serveur. Cependant, si quelqu’un trompe le navigateur, la sécurité côté serveur doit pouvoir refuser les access non autorisés.

J’espère que cela t’aides!

Étape 1: Définir les variables globales dans le module d’application

-définir les rôles pour l’application

  var roles = { superUser: 0, admin: 1, user: 2 }; 

-Définir l’itinéraire pour l’access non autorisé pour l’application

  var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess'; 

Étape 2: Définir le service pour autorisation

 appModule.factory('authorizationService', function ($resource, $q, $rootScope, $location) { return { // We would cache the permission for the session, to avoid roundsortingp 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(); } } }; }); 

Étape 3: Utiliser la sécurité dans le routage: Permet d’utiliser tous nos mots-clés réalisés jusqu’à présent pour sécuriser les itinéraires

 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 ssortingct'; // 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 ssortingct'; 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 ssortingct'; app.directive('loginDirective',function(){ return{ templateUrl:'partials/tpl/login.tpl.html' } }); app/js/services/sessionService.js --------------------------------- 'use ssortingct'; 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 ssortingct'; 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 ----------     My AngularJS App