web-dev-qa-db-fra.com

Authentification avec AngularJS, problèmes de gestion de session et 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 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):

  • 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é du jeton est assez minime.
  • Cookie aura les attributs de domaine et de chemin pour renforcer la sécurité.
  • Aucune connexion multiple n'est autorisée.
66
StarsSky

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 $httpdocs . 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-cspdocs .

É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 .

54
Kos Prov

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]);
                     },
                 }
             })
    });
9
Pramod Sharma
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>
2
Jaydeep Gondaliya

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,

  1. [~ # ~] ui [~ # ~]
  2. Serveur d'authentification utilisateur - Ici, vous validez les informations d'identification de l'utilisateur et générez les cookies nécessaires pour permettre à l'utilisateur de passer à l'interface utilisateur. Si cette étape échoue, l'utilisateur est arrêté là. Ce serveur n’a rien à voir avec la génération de jetons d’API. Vous en aurez également besoin pour les systèmes non basés sur des API. L’authentification Google en est un exemple.

Extension: Authentification Siteminder

Les cookies SiteMinder, leur utilisation, leur contenu et leur sécurité

Construction d’un Java pour Chatkit

  1. Serveur de jetons d'API - Ce serveur génère des jetons d'API basés sur les cookies générés à l'étape 2, c'est-à-dire que vous envoyez des cookies au serveur et obtenez un jeton.
  2. API - Vous utilisez un jeton généré à l'étape 3 pour effectuer des appels d'API.

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?

Cookies interdomaines

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 :)

Et si JWT est volé?

1
Sabir Khan