AngularJS 인증, 세션 관리 및 REST API WS 보안 문제
나는 각진 웹 앱을 개발하기 시작했다.JS와 저는 모든 것이 제대로 보호되고 있는지 잘 모르겠습니다(클라이언트와 서버 측).보안은 단일 로그인 페이지를 기반으로 합니다.자격정보가 정상으로 확인되면 서버는 사용자 지정 시간 유효성을 가진 고유 토큰을 반환합니다.다른 모든 REST API는 이 토큰을 통해 액세스할 수 있습니다.응용 프로그램(클라이언트)이 내 진입점 ex: https://www.example.com/home.html 사용자 자격 증명 삽입 후 고유 토큰을 받습니다.이 고유 토큰은 AES 또는 기타 안전한 기술로 서버 데이터베이스에 저장되며 클리어 형식으로 저장되지 않습니다.
지금부터 나의 앵글루아JS 앱은 이 토큰을 사용하여 노출된 모든 REST API에 대해 인증합니다.
토큰을 커스텀http cookie에 임시 저장하려고 합니다.기본적으로 서버가 credential을 확인하면 새로운 cookie Ex를 보냅니다.
app-token : AIXOLQRYIlWTXOLQRYI3XOLQXOLQRYIRYIFD0T
쿠키에는 시큐어 플래그와 HTTP 전용 플래그가 설정되어 있습니다.http protocol은 새로운 쿠키를 직접 관리하여 저장합니다.연속적인 요구는 쿠키를 관리하고 javascript와 함께 저장할 필요 없이 새로운 파라미터를 쿠키에 제시합니다.요구가 있을 때마다 서버는 토큰을 무효화하고 새로운 토큰을 생성하여 클라이언트에 전송합니다.-->1개의 토큰으로 재생 공격을 방지합니다.
클라이언트가 REST API로부터 HTTP 상태 401 무허가 응답을 수신하면 각도 컨트롤러는 모든 쿠키를 삭제하고 사용자를 로그인 페이지로 리다이렉트합니다.
다른 측면도 고려해야 하나요?토큰을 새 쿠키에 저장하는 것이 좋습니까, 아니면 localStorage에 저장하는 것이 좋습니까?고유한 강력한 토큰을 생성하는 방법에 대한 팁이 있습니까?
편집(개선):
- 세션 토큰 생성기로 HMAC-SHA256을 사용하기로 결정했으며 유효 시간은 20분입니다.랜덤 32바이트 GUID를 생성하여 타임스탬프를 첨부하고 40바이트 키를 제공하여 해시-SHA256을 계산합니다.토큰 유효성이 매우 낮기 때문에 충돌을 얻는 것은 매우 불가능합니다.
- 쿠키에는 보안을 강화하기 위한 도메인 및 경로 속성이 있습니다.
- 다중 로그인은 허용되지 않습니다.
https를 통해 서버와 대화하면 재생 공격에 문제가 없습니다.
서버의 보안 기술을 활용하는 것이 좋습니다.예를 들어 JavaEE에는 즉시 사용 가능한 로그인 메커니즘, 선언적인 역할 기반 리소스 보호(REST 엔드포인트) 등이 있습니다.이것들은 모두 쿠키 세트로 관리되기 때문에 저장 및 유효기간은 신경 쓰지 않아도 됩니다.서버/프레임워크가 제공하는 기능을 확인해 보십시오.
API를 더 많은 사용자(특별히 서비스하는 브라우저 기반 UI에 국한되지 않음) 또는 다른 유형의 클라이언트(예: 모바일 앱)에 노출할 계획이라면 OAuth의 채택을 고려해 보십시오.
Angular에는 다음과 같은 보안 기능이 있습니다(표시되는 대로 추가).
CSRF/XSRF 공격
Angular는 CSRF 보호를 위해 즉시 사용 가능한 메커니즘을 지원합니다.체크 아웃$http
docs. 서버측 지원이 필요합니다.
콘텐츠 보안 정책
Angular에는 CSP가 네이블일 때 적용되는 보다 엄격한 JavaScript 런타임과 호환되는 표현식 평가 모드가 있습니다.체크 아웃ng-csp
문서를 참조하십시오.
엄밀한 컨텍스트의 이스케이프
Angular의 Angular를 합니다.$sce
기능(1.2+)을 통해 UI를 XSS 공격 등에 대해 강화할 수 있습니다.조금 덜 편리하지만 더 안전합니다.여기 문서를 확인해 보세요.
이는 일반 Angular 버전에서 구현할 수 있는 클라이언트 측 보안입니다.이것을 시험해 보았습니다.(여기서 제 기사를 참조해 주세요:- https://www.intellewings.com/post/authorizationonangularroutes )클라이언트측 루트 보안에 가세해, 서버측에서의 액세스도 보호할 필요가 있습니다.클라이언트측의 시큐러티에 의해, 서버로의 추가의 라운드 트립을 회피할 수 있습니다.다만, 브라우저에 속임수를 쓰는 경우는, 서버측의 시큐러티가 부정 액세스를 거부할 수 있습니다.
이게 도움이 됐으면 좋겠네요!
스텝 1: 앱 모듈에서의 글로벌 변수 정의
- 응용 프로그램에 대한 역할 할당
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]);
},
}
})
});
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>
첫째, 당신이 질문한 것에 대한 대답은 짧거나 하나뿐입니다.이미 답변이 끝난 것 외에 추가해 보겠습니다.엔터프라이즈 레벨에서는 4개의 주요 컴포넌트가 있습니다.
- UI
- 사용자 인증 서버 - 사용자 자격 증명을 검증하고 사용자가 UI로 이동하는 데 필요한 쿠키를 생성합니다.이 스텝이 실패하면 사용자는 바로 정지합니다.API 토큰 생성과는 무관한 서버이며 API 기반 시스템이 아닌 경우에도 필요합니다.Google Authentication이 한 예입니다.
- API 토큰 서버 - 이 서버는 스텝 #2에서 생성된 쿠키를 기반으로 API 토큰을 생성합니다. 즉, 쿠키를 서버로 전송하여 토큰을 가져옵니다.
- API - 3단계에서 생성된 토큰을 사용하여 API 호출을 수행합니다.
확장성이 향상되도록 이들 4개의 컴포넌트를 개별적으로 도입 및 관리하는 것이 좋습니다.예를 들어 이 기사에서는 인증과 토큰이 단일 엔드 포인트로 혼재되어 있어 바람직하지 않습니다.스프링 부트 기능이 있는 마이크로 서비스 -JWT에 의한 인증(파트
작성하신 내용을 보면 컴포넌트 2와 3을 직접 작성하신 것 같습니다.보통 사람들은 CA SiteMinder - CA Siteminder 작동 방식 – Basics와 같은 기성 도구를 사용합니다.
고유한 강력한 토큰을 생성하는 방법에 대한 팁이 있습니까?
유지관리성과 보안을 향상시키기 위해 JWT 형식을 선택하는 등 표준화된 방법을 사용하는 것이 좋습니다.JSON Web 토큰(JWT) 인증 방식
토큰은 서명 및 암호화되므로 암호화 키 서버 및 키를 정기적으로 회전시키는 메커니즘도 필요합니다.
JWT와 AES를 사용하여 일부 json을 수동으로 암호화하는 것의 차이점은 무엇입니까?
CA 담당자는 이 커뮤니티 포털에 자세한 PDF 가이드를 첨부했습니다.이 가이드는 전체적인 흐름을 이해하는 데 도움이 됩니다.
API 코드는 암호화 키를 가져와 토큰을 인증하기 위해 토큰을 복호화 및 디코딩해야 합니다.토큰이 변조되었거나 누락된 경우 토큰에 플래그를 붙여야 합니다.사용할 수 있는 라이브러리가 있습니다.
토큰을 새 쿠키에 저장하는 것이 좋습니까, 아니면 localStorage에 저장하는 것이 좋습니까?
UI 및 API가 다른 도메인에 있는 경우 로컬 스토리지, 같은 도메인에 있는 경우 쿠키.
JWT를 localStorage 또는 cookie에 저장해야 합니까?
애플리케이션의 보안도 배포 모델과 질문에서 지정하지 않은 부분에 따라 달라집니다.개발자는 SQL Injection과 같은 단순한 결함을 코드에 남길 수 있습니다.
언급URL : https://stackoverflow.com/questions/20870386/authentication-with-angularjs-session-management-and-security-issues-with-rest
'programing' 카테고리의 다른 글
RestTemplate PATCH 요청 (0) | 2023.03.28 |
---|---|
Wordpress에서 "An active PHP session was detected" 중요 경고를 받는 중 (0) | 2023.03.28 |
리액트 라우터와 익스프레스 루트의 차이점은 무엇입니까?js (0) | 2023.03.28 |
React Native에서 JSX 구성 요소를 연결하는 방법 (0) | 2023.03.28 |
Rabbit 설정 방법스프링 토끼와의 MQ 연결? (0) | 2023.03.28 |