웹 프로그래밍/[ Spring Security ]

[ Spring Security ] 03. 동시 세션제어와 세션 고정보호, 세션 정책 관리

kim.svadoz 2021. 12. 8. 16:21
반응형
인프런에서 진행하는 정수원 강사님의 "스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security" 강의를 보고 학습을 위해 개인적으로 추가/정리한 글임을 알립니다.

[ 동시 세션 제어, 세션 고정 보호, 세션 정책 ]

> 동시 세션 제어

동일한 계정으로 인증을 받을 때 생성되는 세션에 허용 개수가 초과 되었을 경우 어떻게 세션을 유지하는 지에 대한 제어를 말한다.

 

Spring-Security에서는 두 가지 전략으로 동시세션제어를 하게 된다.

image-20211120173859245

default 최대 세션 허용 개수 : 1개

동일한 계정으로 들어온 사용자에 대해서 세션 허용 개수

  1. 이전 사용자 세션 만료
    • 사용자 1의 세션은 만료 되고 사용자 2의 세션은 사용가능 하다.
  2. 현재 사용자 인증 실패
    • 뒤늦게 들어온 사용자 2의 세션을 차단해서 사용자 1의 세션은 사용가능 하다.
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()                    
            .maximumSession(1)                            // 최대 허옹 가능 세션 수(default 1)
            .maxSessionsPreventsLogin(true)                 // 동시 로그인 차단한다 -> true:현재사용자인증실패 // (default)false:기존세션만료
            .invalidSessionUrl("/invalid")                 // 세션이 유효하지 않을 때 이동 할 페이지
            .expiredUrl("/expired")                        // 세션이 만료된 경우 이동 할 페이지
    ;
}

이 때 invalidSessionUrl()expiredUrl()을 모두 설정했을 땐 invalidSessionUrl()이 우선적용 된다.

 

false(기존 사용자 세션 만료)로 설정하면 사용자 1과 사용자 2 모두 세션이 실행이 되긴한다.

그러나 현재 사용자의 세션이 만료되었는지 안되었는지 ConcurrentSessionFilter가 체크를 해서 이전 사용자의 세션을 만료시키게 된다.

This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).

 

> 세션 고정 보호

image-20211120175513131

공격자가 임의로 JSESSIONID 를 웹에 심어놓고 사용자도 공격자의 세션 쿠키를 사용하게 되면,

사용자와 공격자 모두 동일한 세션을 공유하게 되는 참사가 발생한다.

이런걸 세션 고정 공격이라고 한다.

 

그래서 Spring-Security는 세션 고정 보호를 제공한다.

사용자가 비록 공격자가 심어놓은 쿠키로 접속을 해서 인증을 시도한다 하더라도, 인증할 때 마다 새로운 세션을 생성하면 새로운 쿠키가 만들어질 거고 공격자가 심어놓은 JSESSIONID는 달라지기 때문에 사용자의 정보를 공유할 수 없다.

 

즉, 인증에 성공할 때마다 새로운 세션을 생성되고 새로운 JSESSIONID를 발급하도록 하는 것이 세션 고정 보호이다.

protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
            .sessionFixation().changeSessionid()        // 기본값, (none, migrateSession, newSession)
    ;
}

여기서 changeSessionid() 는 서블릿 3.1 이상에서의 기본값이고 그 이하 버전에서의 기본값은 migrateSession()이다.

changeSessionid과 migrateSession 이 두 개의 옵션은 이전 세션의 설정한 여러 속성값들을 그대로 사용할 수 있도록 지원하며

newSession은 이전 세션에서 설정한 여러 속성값들을 사용하지 못하고 새롭게 속성값들을 설정해야 한다는 특징이 있다.

none 옵션은 새롭게 세션을 생성하지 않고, 공격에 취약하다.

 

> 세션 정책

세션 관리 기능이 작동하는 정책

protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.If_Required)
    ;
}
  • SessionCreationPolicy.Always : 스프링 시큐리티가 항상 세션을 생성한다.
  • SessionCreationPolicy.If_Required : 스프링 시큐리티가 필요 시에 생성한다.(기본 값)
  • SessionCreationPolicy.Never : 스프링 시큐리티가 생성하지 않지만 이미 존재하면 사용한다.
  • SessionCreationPolicy.Stateless : 스프링 시큐리티가 생성하지 않고 존재해도 사용하지 않는다.
    • JWT이라는 웹 토큰을 이용해 토큰에 사용자의 정보나 추가적인 사항들을 저장하고 인증을 하는 방식이다.
    • 이 때 는 Stateless 옵션을 사용한다.

 

< SessionManagementFilter, ConcurrentSessionFilter >

추가로 세션 관리에 사용되는 필터들에 대해서 알아보겠다!

  • SessionManagementFilter
    1. 세션 관리
      • 인증 시 사용자의 세션정보를 등록, 조회, 삭제 등의 세션 이력을 관리
    2. 동시적 세션 제어
      • 동일 계정으로 접속이 허용 되는 최대 세션 수를 제한
    3. 세션 고정 보호
      • 인증할 때마다 세션쿠키를 새로 발급해서 공격장의 쿠키 조작을 방지
    4. 세션 생성 정책
      • Always, If_Required, Never, Stateless
  • ConcurrentSessionFilter
    • 매 요청마다 현재 사용자의 세션 만료 여부를 체크해서
    • 세션이 만료되었을 경우에는 즉시 만료 처리를 한다.
    • session.isExpired() == true
      • 로그아웃 처리
      • 즉시 오류 페이지 응답
    • 이 필터는 동시적 세션제어를 하기 위해 SessionManagementFilter와 항상 연계되어 있다.

image-20211121204044680

사용자1과 2의 로그인 과정에서 동시적 세션 접근제어를 하는 과정

image-20211121204406623

  1. 사용자1이 UsernamePassworddAuthenticationFilter는 가장 먼저 ConcurrentSessionControlAuthenticationStrategy 클래스를 가장 먼저 호출한다. 이 계정으로 생성된 세션의 개수가 얼마인지 확인한다. 이 때는 session count가 0이므로 문제없이 통과한다.
  2. 그 이후 ChangeSessionIdAuthenticationStrategy 클래스를 호출해서 세션 고정 보호를 처리한다. 새롭게 세션을 생성하고 새로운 세션 쿠키를 발급한다.
  3. 세 번째로 RegisterSessionAuthenticationStrategy 클래스를 호출해서 사용자의 세션을 등록하고 저장하는 역할을 한다. 이 때 session count가 1이 된다.
  4. 이 상태에서 사용자2가 사용자1가 동일한 계정으로 인증을 시도한다.
  5. 해당 계정으로 생성된 세션이 한개가 있고, 최대 허용 세션 개수도 1로 똑같으므로 두 가지 전략을 타게 된다.
    1. 인증 실패 전략일 경우
      • SessionAuthenticationException이 발생하고 인증이 실패가 된다.
      • 더이상 다른 클래스를 호출하지 않고 인증이 끝나버린다!
    2. 이전 사용자의 세션만료 전략일 경우
      • 사용자 1의 세션을 만료 시킨다 : session.expireNow()
      • 사용자 2의 세션을 인증한다 : 똑같이 1,2,3의 과정을 거친다.
      • 이 때 사용자 1이 다시금 자원에 접근을 하게 되면 ConcurrentSessionFilter가 사용자의 세션 만료여부를 체크한다 : session.isExpired() == true
반응형