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 côté serveur). La sécurité est basée 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 REST api sont accessibles via ce jeton. L'application (client) navigue jusqu'à mon point d'entrée ex: https://www.example.com/home .html L'utilisateur insère ses informations d'identification et reçoit un jeton unique, stocké dans la base de données du serveur avec AES ou d'autres techniques sécurisées, il n'est pas stocké en format clair.
Mon application AngluarJS utilisera désormais ce jeton pour s’authentifier auprès de tous REST Api exposée.
Je pense à stocker temporairement le jeton dans un cookie http personnalisé; En gros, lorsque le serveur vérifie les informations d'identification, il renvoie un nouveau cookie.
app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T
Le cookie a les indicateurs secure et HTTP Only activés. 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 en javascript; à chaque demande, le serveur invalide le jeton, en génère un nouveau et le renvoie au client -> empêche les attaques par rejeu avec un seul jeton.
Lorsque le client reçoit un statut HTTP 401 non autorisé réponse de la part de REST Api, le contrôleur angular nettoie tous les cookies et rediriger l'utilisateur vers la page de connexion.
Devrais-je avoir à considérer d'autres aspects? Est-il préférable de stocker le jeton dans un nouveau cookie ou dans le stockage local? Des conseils sur la façon de générer un jeton fort unique?
Modifier (améliorations):
Si vous parlez au serveur via https, vous n'avez pas de problème avec les attaques par rejeu.
Ma suggestion serait d'utiliser la technologie de sécurité de votre serveur. Par exemple, JavaEE dispose d’un mécanisme de connexion immédiat, d’une protection déclarative des ressources basée sur un rôle (vos points de terminaison REST), etc. Ces opérations sont toutes gérées avec un ensemble de cookies et se soucient du stockage et de l'expiration. Découvrez ce que votre serveur/framework vous donne déjà.
Si vous envisagez d'exposer votre API à un public plus large (et non spécifiquement à l'interface utilisateur basée sur un navigateur que vous servez) ou à d'autres types de clients (par exemple, une application mobile), envisagez d'adopter OAuth.
Angular a les caractéristiques de sécurité suivantes (en ajoutera d'autres au fur et à mesure de leur apparition):
attaques CSRF/XSRF
Angular prend en charge un mécanisme prêt à l'emploi pour une protection CSRF . Découvrez $http
docs . Un support côté serveur est nécessaire.
Politique de sécurité du contenu
Angular a un mode d'évaluation d'expression compatible avec les exécutions JavaScript plus strictes qui sont appliquées lorsque CSP est activé. Découvrez ng-csp
docs .
Échappement contextuel strict
Utilisez la nouvelle fonctionnalité $sce
(1.2+) d'Angular pour renforcer votre interface utilisateur contre les attaques XSS, etc. C'est un peu moins pratique mais plus sécurisé. Découvrez la documentation ici .
C’est la sécurité côté client que vous pouvez implémenter dans des versions Angular régulières). J’ai essayé et testé ceci. (Veuillez trouver mon article ici: - https: //www.intellewings. com/post/autorisationonangularroutes ) En plus de la sécurité de la route côté client, vous devez également sécuriser l'accès côté serveur. La sécurité côté client permet d'éviter des allers-retours supplémentaires vers le serveur. Toutefois, si quelqu'un trompe le navigateur, il est serveur La sécurité latérale doit pouvoir refuser les accès 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éfinition de la route pour l'accès non autorisé à l'application
var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';
Étape 2: Définir le service pour l'autorisation
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();
}
}
};
});
Étape 3: Utiliser la sécurité dans le routage: Permet d’utiliser tous nos mots durs jusqu’à présent, pour sécuriser les routes
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>
Premièrement, il n’ya pas de réponse brève ou unique à ce que vous avez demandé. En plus de ce qui a déjà été répondu, laissez-moi essayer d'ajouter quelque chose de plus. Au niveau de l'entreprise, il y a quatre composants principaux,
Extension: Authentification Siteminder
Les cookies SiteMinder, leur utilisation, leur contenu et leur sécurité
Construction d’un Java pour Chatkit
Il est préférable de déployer et de gérer ces quatre composants indépendamment pour une meilleure évolutivité. par exemple. dans cet article, ils ont mélangé l'authentification et la génération de jetons en un seul point d'extrémité et ce n'est pas bon - Microservices avec Spring Boot - Authentification avec JWT (Partie 3)
Votre rédaction semble indiquer que vous avez écrit les composants deux et trois vous-même - généralement, les utilisateurs utilisent des outils prêts à l'emploi, tels que CA SiteMinder - Fonctionnement de CA Siteminder - Principes de base
Des conseils sur la façon de générer un jeton fort unique?
Je suggérerais que vous utilisiez une méthode normalisée pour une meilleure maintenabilité et sécurité, c’est-à-dire que vous choisissez le format JWT. Schéma d'authentification du jeton Web JSON (JWT)
Votre jeton sera signé et chiffré. Vous aurez également besoin d'un serveur de clés de chiffrement et d'un mécanisme permettant de faire pivoter ces clés à intervalles réguliers.
Jetons Web JSON - Comment stocker la clé en toute sécurité?
Quelle est la différence entre JWT et le cryptage manuel de json avec AES?
La personne CA a joint un guide pdf détaillé sur ce portail communautaire - qui vous aidera à comprendre le flux global.
Exemple de code/application à utiliser de REST JWT)
Votre code d'API devra extraire la clé de cryptage et décrypter et décoder le jeton pour authentifier le jeton. Si le jeton est falsifié ou manquant, vous devez le signaler comme tel. Il y a des bibliothèques disponibles pour cela.
Est-il préférable de stocker le jeton dans un nouveau cookie ou dans le stockage local?
stockage local si UI & API sont sur des domaines différents et Cookies sur le même domaine.
Le JWT doit-il être stocké dans un stockage local ou un cookie?
La sécurité d'une application dépend également du modèle de déploiement et de la partie que vous n'avez pas spécifiée dans votre question. Parfois, les développeurs peuvent laisser dans leur code des défauts aussi simples que SQL Injection :)