66 votes

L'authentification avec AngularJS, gestion de session, les questions de sécurité avec l'Api REST WS

J'ai commencé à développer une application web avec angularJS et je ne suis pas sûr que tout est sécurisé (côté client et serveur). La sécurité est basée sur une seule page de connexion, si les informations d'identification sont vérifiées ok, mon serveur renvoie un jeton unique personnalisé avec le temps de validité. Tous les autres api REST sont accessibles par le biais de ce jeton. L'application (client) accédez à mon point d'entrée ex: https://www.example.com/home.html utilisateur d'insérer des informations d'identification et de récupérer un jeton unique. Ce jeton unique sont stockées dans le serveur de base de données avec l'algorithme AES ou d'autres sécurisé techniques, il n'est pas stocké en format clair.

À partir de maintenant, mon AngluarJS application va utiliser ce jeton pour s'authentifier auprès de toutes les Api REST exposés.

Je pense temporaire stocker le jeton personnalisé dans un cookie http; en principe, lorsque le serveur vérifie les informations d'identification, il envoie en retour un nouveau témoin Ex.

app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T

Le cookie sécurisé et Uniquement HTTP drapeaux. Le protocole Http de gérer directement le nouveau cookie et de le stocker. Les demandes successives sera présente le cookie avec le nouveau paramètre, sans la nécessité de gérer et de le stocker avec javascript; à chaque requête, le serveur invalide le jeton et génère un nouveau et l'envoie au client --> prévenir replay-attaque avec un seul jeton.

Lorsque le client reçoit un code d'état HTTP 401 non autorisé de réponse à partir des Api REST, l'angle de contrôleur de nettoyer les cookies et rediriger l'utilisateur vers la page de connexion.

Devrais-je envisager d'autres aspects? Est-il préférable de stocker le jeton à l'intérieur d'un nouveau cookie ou dans localStorage? Des conseils sur la façon de générer un unique fort jeton?

Edit (améliorations):

  • J'ai décidé d'utiliser l'algorithme HMAC-SHA256 comme générateur de jeton de session, avec 20 minutes de validité. Je la génération aléatoire d'32byte GUID, joindre un horodatage et de calculer le HACHAGE SHA256 en fournissant un 40 octets de la clé. Il est tout à fait impossible d'obtenir des collisions depuis le jeton de validité est tout à fait minime.
  • Cookie sera de domaine et le chemin des attributs de sécurité.
  • Pas de multi-connexions sont autorisées.

54voto

Kos Prov Points 1742

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

Ma suggestion serait de tirer parti de votre serveur de sécurité de la technologie. Par exemple, JavaEE a un out-of-the-box mécanisme de connexion, déclaratif basé sur le rôle de la protection des ressources (votre REPOS des points de terminaison) etc. Ce sont tous gérés avec un ensemble de cookies, et vous n'avez pas de soins sur le stockage et la date d'expiration. Découvrez ce que votre serveur/cadre vous donne déjà.

Si vous prévoyez d'exposer votre API à un public plus large (pas spécialement pour le navigateur de base de l'INTERFACE utilisateur que vous servez) ou d'autres types de clients (par exemple, application mobile), d'envisager d'adopter le protocole OAuth.

Sur le dessus de ma tête, Angulaire a les caractéristiques suivantes (va ajouter plus comme ils pop-out):

CSRF/attaques XSRF

Angulaire prend en charge la sortie de la boîte de mécanisme pour CSRF de protection. Découvrez $http docs. Côté serveur, le soutien est nécessaire.

Contenu De La Politique De Sécurité

Angulaire dispose d'un mode d'évaluation de l'expression qui est compatible avec la plus stricte exécution JavaScript qui sont appliquées lors de la DSP est activé. Découvrez ng-csp docs.

Stricte Contextuelle S'Échapper

Utilisation Angulaire de la nouvelle - $sce fonctionnalité (1.2+) pour durcir vous de l'INTERFACE utilisateur contre les attaques XSS etc. C'est un peu moins pratique mais plus sûr. Découvrez les docs ici.

9voto

Pramod Sharma Points 69

C'est côté client de sécurité que vous pouvez mettre en œuvre régulièrement Angulaire versions. J'ai essayé et testé. (S'il vous plaît trouver mon article ici:- http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security) En outre, à côté client voie de la sécurité, vous avez besoin pour sécuriser l'accès au serveur de côté également. Côté Client, de sécurité permet d'éviter les extra aller-retour vers le serveur. Cependant, si quelqu'un à des astuces le navigateur , le serveur de côté de serveur de sécurité doit être en mesure de refuser l'accès non autorisé.

Espérons que cette aide!

Étape 1: Définir des variables Globales dans l'app-module

-définir des rôles pour l'application

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

-Définir l'itinéraire De l'Accès non autorisé à l'application

 var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';

Étape 2: Définir le service d'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é de routage: Permet d'utiliser tous nos hardword fait 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]);
                     },
                 }
             })
    });

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by: