LogOut 처리, LogoutFilter
RememberMeAuthentiationFilter
위에 필터가 동작하기 위한 조건
1. Security Context에 Authentication 이 null인경우
2. remember-me cookie값을 가지고 접속한 경우
둘다 충족할 떄 필터에 걸리게 된다.
RemberMeService 의 구현체가 2가지 종류가 있다.
TokenBased = 메모리기반
PersistentTokenBased = 디비기반
httpSecurity.rememberme() 를 통해 spring 기동시 해당 필터를 만들어주게되고
remberme cookie를 주는 시점은 첫번째로 인증을 성공하여 successfulAuthentication 메소드에서 제공해준다.
신기하게 ProviderManager가 authenticate하기전에 user 가 있는지 확인하고 기본적으로 MD5 digest를 통해서 token hash값이 같은지 확인하고 만든 RememberMeAuthenticationToken 인증객체는 이미 인증된 상태로 나온다.
헌데 이미 인증된 객체를 다시 signature를 만들떄 쓰인 key 값을 authenticate에서 한번더 비교를 한다.
AnonymousAuhthenticationFilter
Authentication을 null로 처리하지 않게 하기위해 존재도 하지만 단순히 null 방지가 아니라 어떤 시스템에서 인증을 하지 않는 사용자를 어떤 특정한 클래스를 만들어 거기에 보관하여 관리하도록 설계를 하고 구현한 것이라 이해하면 된다.
동시 세션 제어, 세션 고정 보호, 세션 정책
(동일한 사용자) 세션 허용 개수가 초과되는 시점에 이전 사용자의 세션을 만료하거나 , 현재 로그인 하려는 사용자를 허용안한다.
httpSecurity
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
true로 하면 첫번째 방법으로 동작하고 false로 하게 되면 두번쨰 방법으로 동작한다. 두번째 방법이 기본전략(false)이다.
세션 고정 공격
공격자가 먼저 로그인을 해서 자신의 세션쿠키를 사용자에게 넘겨준다. 쿠키는 브라우저에 저장이되고 사용자가 요청을 보낼떄 담겨져서 전달된다.
공격자가 가지고 있는 JESSSIONID(세션쿠키)에 해당하는 세션은 사용자와 1대1 맵핑이 된다.
공격자는 해당 세션쿠키로 사용자 처럼 로그인하여 사용자 정보를 획득가능하다.
방지하는 방법은 매번 로그인을 시도 할때마다 새로운 세션과 세션쿠키를 return 하면 된다.
httpSecurity
.sessionManagement()
.sessionFixation().none()
// .sessionFixation().changeSessionId()
// .sessionFixation().newSession()
changeSessionId를 통해 방지 가능하다. 해당 세션은 그대로 유지한체 sessionId만 바뀌게 되는것이다.
remember me cookie가 있는 경우도 ConcurrentSessionFilter에서 (예를 들면 max1) 걸리면 삭제 당해서 익명 사용자 상태로 되돌아가게 된다.
일반적으로 세션은 WAS가 먼저 생성을 한다. -> request.getSession(false) , 즉 이미 was가 만든 세션을 사용하겠다는 의미이다.
request.getSession(true)로 하는 경우는 spring security에서 직접 세션을 생성한다.
세션 제어 필터: SessionManagementFilter , ConcurrentSessionFilter
SessionMangementFilter
1. 세션관리
2. 동시적 세션 제어 = 동일 계정으로 접속이 허용된느 최대 세션수를 제한
3. 세션 고정 보호 = 인증할 때마다 세션쿠키를 새로 발급하여 공격자의 쿠키 조작을 방지
4. 세션 생성 정책 = Always, Never, Stateless , If_Required
ConcurrentSessionFilter
위 세션과 연계해서 동시적 세션 제어에서 역할을한다.
매 요청마다 현재 사용자의 세션 만료 여부 체크하고 로그아웃 처리를 해버린다. "This session has been expired"
사용자가 2번째 로그인을 시도한다 -> 이전 사용자의 session 을 expire 시킨다.
이전 사용자가 요청을 할떄마다 ConcurrentSessionFilter를 거치기 때문에 세션이 만료되었는지 확인 가능하다.
만료되면 logout 시킨다.
user1 , user2이지만 둘다 동일한 계정으로 로그인을 시도하는 상황이다.
List<SessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
ConcurrentSessionControlAuthenticationStrategy#onAuthentication에서 session list를 꺼내올때 principal 기준으로 꺼내오는것을 보면 같은 계정에 관한 것임을 알 수 있다.
권한설정과 표현식
선언적 방식
1. URL = http.antMatchers("/users/**).hasRole("USER")
2. Method = @PreAuthorized("hasRole("USER")")
동적 방식(db 연동) = URL, Method
예외처리 및 요청 캐시 필터: ExceptionTranslationFilter , RequestCacheAwareFilter
ExceptionTranslationFilter
AuthenticationException을 처리한다. -> FilterSecurityInterceptor가 맨마지막에 위치하게 되는데 사용자 요청을 처리할 떄 인증, 인증가 예외가 발생되면 ExceptionTranslationFilter의 catch 문에서 잡는다.
1. AuthenticationEntryPoint 호출 = 인증 제대로 안되었으니 다시 인증하라고 로그인페이지를 호출하거나 401 오류코드 전달
2. 인증 예외가 발생하기 전의 요청 정보를 저장
RequestCache = 사용자의 이전 요청 정보를 세션에 저장하고 이를 꺼내 오는 캐시 메커니즘
SavedRequest = 사용자가 요청했던 request parameter 값, header값 등을 저장
인증이 완료된 이후에 사용자가 접근하고자 했던 resource 경로로 이동시켜줄때 사용하게 된다.
AccessDeniedException 발생시 AccessDeniedHandler에서 처리하도록함
처음 요청이 들어왔을때(아예 로그인창전에) AnonymousAuthenticationFilter에서 anonymous 로 인증을 처리했기 때문에 인가예외로 빠진다.
하지만 ExceptionTranslationFilter의 catch 문에서 anonymous 면 인증예외로 처리하여 다시 로그인할 수 있게 AuthenticationEntryPoint를 호출해준다.
remeberme token이 변조되었을 경우에도 빠지게 된다.
httpSecurity
.formLogin()
.successHandler((request, response, authentication) -> {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
SavedRequest savedRequest = requestCache.getRequest(request, response);
String redirectUrl = savedRequest.getRedirectUrl();
response.sendRedirect(redirectUrl);
});
RequestCache도 ExceptionTralsationFilter에서 session에 저장시킨 request 정보를 불러오는 것이다.
사이트 간 요청 위조 - CSRF ,CsrfFilter
로또 당첨을 클릭하는 순간 공격자 작성한 요청을 실행하게 되는데 이때 쇼핑몰은 jessionId를 보고 이미 로그인한 사용자로 간주하고 요청을 실행하게 된다.
CsrfFilter
모든 요청에 랜덤하게 생성된 토큰을 HTTP 파라미터로 요구
요청시 전달되는 토큰 값과 서버에 저장된 실제 값과 비교한 후 만약 일치하지 않으면 요청은 실패한다.
client는 변경 요청(post,patch,put,delete) 를 전송할때 csrf 토큰도 함께 전송해야한다.
https://security.stackexchange.com/questions/51188/cookiesession-based-csrf-protection
csrf token은 JSessionId 처럼 쿠키에 저장하면 안된다. 모든 요청에 쿠키가 포함되기 때문에 (특별한 설정을 하지 않으면)
httpSecurity
.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository());
session 이나 기본 lazyrepo를 사용하면 response 값으로 csrf token 을 주지 않는데 client에서 어떻게 받아서 써야할꼬?
'WEB > Security' 카테고리의 다른 글
실전프로젝트 - 인증 프로세스 Ajax 인증 구현 (0) | 2023.01.15 |
---|---|
스프링 시큐리티 주요 아키텍처 이해 (0) | 2023.01.10 |
OAuth 2.0 Client + Resource Server + Authorization Server 연동 (0) | 2022.12.28 |
Spring Authorization Server - 엔드포인트 프로토콜 (0) | 2022.12.26 |
Spring Authorization Server + 주요 도메인 클래스 (3) | 2022.12.25 |