Autenticação com AngularJS, gestão de sessões e questões de segurança com a API REST

comecei a desenvolver uma aplicação web com angularJS e não tenho a certeza se está tudo bem seguro (lado cliente e servidor). A segurança é baseada em uma única página de login, se as credenciais são verificadas ok, Meu servidor envia de volta um token único com validade de tempo personalizado. Todas as outras api de descanso são acessíveis através deste token. A aplicação (cliente) navega até ao meu ponto de entrada ex: https://www.example.com/home.html o utilizador insere credenciais e recebe de volta um símbolo único. Este único token é armazenado no banco de dados do servidor com AES ou outras técnicas seguras, ele não é armazenado em formato claro.

A partir de agora, o meu aplicativo AngluarJS vai usar este token para autenticar todas as Api restantes expostas.

estou a pensar em guardar temporariamente o item num cookie http personalizado; basicamente, quando o servidor verifica as credenciais, envia de volta um novo cookie ex.

app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T

o cookie tem as opções {[[11]}seguras e apenas em HTTP . Protocolo Http gerir directamente o novo Biscoito e guardá-lo. Sucessivos pedidos apresenta o cookie com o novo parâmetro, sem a necessidade de gerenciá-lo e armazená-lo com javascript; em cada pedido, o servidor invalida o token e gera um novo e envia-lo de volta para o cliente --> evitar a repetição de ataques com um único token.

Quando o cliente recebe uma resposta HTTP 401 não autorizada de qualquer Api de descanso, O controlador angular limpa todos os cookies e redirecciona o utilizador para a autenticação pagina.

Tenho de considerar outros aspectos? É melhor guardar o token dentro de um cookie novo ou em localStorage? Alguma dica sobre como gerar um símbolo único e forte?

editar (melhorias):

    Decidi usar o HMAC-SHA256 como gerador de token de sessão, com validade de 20 minutos. Eu gerar um GUID 32byte Aleatório, anexar um timestamp e calcular o HASH-SHA256 fornecendo uma chave de 40 bytes. É completamente impossível obter colisões desde o token. a validade é mínima.
  • o Cookie terá domínio e localização Atributos para aumentar a segurança.
  • não são permitidas multi-loginas.
Author: StarsSky, 2014-01-01

3 answers

Se você falar com o servidor através de https, você não tem um problema com ataques de repetição.

A minha sugestão seria usar a tecnologia de segurança do seu servidor. Por exemplo, o JavaEE tem um mecanismo de login fora da caixa, uma proteção declarativa baseada no papel dos Recursos (seus pontos finais de descanso), etc. Estes são todos gerenciados com um conjunto de cookies e você não tem que se importar com o armazenamento e expiração. Confira o que o seu servidor / framework já lhe dá.

Se planeia expor sua API para um público mais amplo (NÃO especificamente para a IU baseada no navegador que você serve) ou outros tipos de clientes (por exemplo, app móvel), considere a adoção de OAuth.

No topo da minha cabeça, o Angular tem os seguintes elementos de segurança (irá adicionar mais à medida que vão surgindo):

ataques CSRF/XSRF

O Angular suporta um mecanismo fora da caixa para protecção da CSRF. Check out $http docs . O suporte do lado do servidor é necessário.

conteúdo Política De Segurança

O Angular tem um modo de avaliação de expressão que é compatível com as execuções JavaScript mais rigorosas que são aplicadas quando o CSP está activo. Check out ng-csp docs .

Escapar Contextual Estrito

Usar a nova funcionalidade do Angular $sce (1, 2+) para endurecer a sua IU contra ataques do XSS, etc. É um pouco menos conveniente, mas mais seguro. Vejam os documentos.

 52
Author: Kos Prov, 2014-10-24 09:02:45

Esta é a segurança do lado do cliente que você pode implementar em versões angulares regulares. Eu tentei e testei isto. (Please find my article here: - [[6]} http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security Além da segurança da rota do cliente, você precisa garantir o acesso também no lado do servidor. A segurança do lado do cliente ajuda a evitar uma viagem extra ao servidor. No entanto, se alguém engana o navegador, então a segurança do servidor do servidor deve ser capaz de rejeitar acesso.

Espero que isto ajude!

Passo 1: definir variáveis globais no módulo app

- definir funções para a aplicação

  var roles = {
        superUser: 0,
        admin: 1,
        user: 2
    };
Defina a rota de acesso não autorizado para a aplicação.
 var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';

Passo 2: Definir o serviço de autorização

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();
        }
    }

};
});

Passo 3: usar a segurança no encaminhamento: vamos usar todas as nossas palavras duras feitas até agora, para proteger as rotas

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
Author: Pramod Sharma, 2014-08-27 00:55:32
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>
 1
Author: Jaydeep Gondaliya, 2016-06-22 04:46:14