我开始使用angularJS开发一个web应用程序,我不确定一切都是正确的安全(客户端和服务器端).安全性基于单个登录页面,如果检查完凭证,我的服务器会发回一个具有自定义时间有效性的唯一令牌.所有其他REST api都可以通过此令牌访问.应用程序(客户端)浏览到我的入口点ex:https://www.example.com/home.html用户插入凭据并接收一个唯一令牌.此唯一令牌使用AES或其他安全技术存储在服务器数据库中,不以明文格式存储.
从现在开始,我的AngluarJS应用程序将使用此令牌对所有暴露的REST Api进行身份验证.
我正在考虑将令牌临时存储在自定义的http cookie中; 基本上,当服务器验证凭据时,它会发回一个新的Cookie Ex.
app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T
该cookie具有安全和HTTP Only标志设置.Http协议直接管理新cookie并存储它.连续请求将使用新参数呈现cookie,而无需管理它并使用javascript存储它; 在每次请求时,服务器使令牌无效并生成一个新令牌并将其发送回客户端 - >使用单个令牌防止重放攻击.
当客户端从任何REST Api 收到HTTP状态401未授权响应时,角度控制器清除所有cookie并将用户重定向到登录页面.
我应该考虑其他方面吗?将令牌存储在新cookie或localStorage中更好吗?有关如何生成唯一强令牌的任何提示?
编辑(改进):
我决定使用HMAC-SHA256作为会话令牌生成器,有效期为20分钟.我生成一个随机的32字节GUID,附加一个时间戳并通过提供一个40字节的密钥来计算HASH-SHA256.由于令牌有效性非常小,因此很难获得冲突.
Cookie将具有域和路径属性以提高安全性.
不允许多次登录.
Kos Prov.. 54
如果您通过https与服务器通信,则重放攻击没有问题.
我的建议是利用您服务器的安全技术.例如,JavaEE具有开箱即用的登录机制,基于角色的声明性资源保护(您的REST端点)等.这些都是使用一组cookie管理的,您不必关心存储和过期.看看你的服务器/框架已经为你提供了什么.
如果您计划将API公开给更广泛的受众(不是专门针对您所服务的基于浏览器的UI)或其他类型的客户(例如移动应用),请考虑采用OAuth.
在我的头脑中,Angular具有以下安全功能(在弹出时会添加更多功能):
CSRF/XSRF攻击
Angular支持开箱即用的CSRF保护机制.查看$http
文档.需要服务器端支持.
内容安全政策
Angular具有一种表达式评估模式,该模式与启用CSP时强制执行的更严格的JavaScript运行时兼容.查看ng-csp
文档.
严格的上下文转义
使用Angular的新$sce
功能(1.2+)来强化你的UI以抵御XSS攻击等.它不太方便但更安全.在这里查看文档.
如果您通过https与服务器通信,则重放攻击没有问题.
我的建议是利用您服务器的安全技术.例如,JavaEE具有开箱即用的登录机制,基于角色的声明性资源保护(您的REST端点)等.这些都是使用一组cookie管理的,您不必关心存储和过期.看看你的服务器/框架已经为你提供了什么.
如果您计划将API公开给更广泛的受众(不是专门针对您所服务的基于浏览器的UI)或其他类型的客户(例如移动应用),请考虑采用OAuth.
在我的头脑中,Angular具有以下安全功能(在弹出时会添加更多功能):
CSRF/XSRF攻击
Angular支持开箱即用的CSRF保护机制.查看$http
文档.需要服务器端支持.
内容安全政策
Angular具有一种表达式评估模式,该模式与启用CSP时强制执行的更严格的JavaScript运行时兼容.查看ng-csp
文档.
严格的上下文转义
使用Angular的新$sce
功能(1.2+)来强化你的UI以抵御XSS攻击等.它不太方便但更安全.在这里查看文档.
这是客户端安全性,您可以在常规Angular版本中实现.我试过并试过这个.(请在此处找到我的文章: - http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security)除了客户端路由安全性之外,您还需要在服务器端安全访问.客户端安全性有助于避免额外的服务器往返.但是,如果某人欺骗浏览器,那么服务器端安全性应该能够拒绝未经授权的访问.
希望这可以帮助!
第1步:在app-module中定义全局变量
- 定义应用程序的角色
var roles = { superUser: 0, admin: 1, user: 2 };
- 针对应用程序的未授权访问的定义路由
var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess';
第2步:定义授权服务
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(); } } }; });
步骤3:在路由中使用安全性:让我们使用目前为止所做的所有硬编码来保护路由
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]); }, } }) });