Spring/Spring Security

[Spring Security] 세션 관리(SessionManagement)

공대키메라 2022. 7. 23. 17:18

저번 시간에는 Spring Security에서 인증 저장에 대해 알아보았다.

(지난 내용이 궁금하면 여기 클릭! 😍😍 요기 😍😍)

 

이번 시간에는 Spring Security에서 어떻게 session을 관리하는지 알아보도록 하겠다.

 

참고한 사이트는 다음과 같다.

 

출처:

https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html

https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

 

또한, 인프런 정수원 강사님의 강의를 참고도 했습니다 :)

Session Management

 

1. 강력한 세션 생성 강제하기 (Force Eager Session Creation)

여러번 세션을 꼭 만들어 내는 것은 가치있을수 있다. 이것은 ForceEagerSessionCreationFilter를 사용해서 다뤄질 수 있다.

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
/*
	세션 생성 정책

	sessionCreationPolicy.Always : Spring security가 항상 세션 생성
	sessionCreationPolicy.If_Required : Spring security가 필요시 생성(기본 값)
	sessionCreationPolicy.Never : Spring security가 생성하지 않지만 이미 존재하면 사용
	sessionCreationPolicy.Stateless : Spring security가 생성하지 않고 존재해도 사용하지 않음
*/
    http
        .sessionManagement(session -> session
            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
        );
    return http.build();
}

 

2. 타임아웃 감지(Detecting Timeouts)

불확실한 세션 ID를 제출한 것을 Spring Security가 설정하고 적절한 URL로 사용자을 redirect할 수 있다. 

 

이것은 session management element를 통해 다룰 수 있다. 

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .invalidSessionUrl("/invalidSession.htm")
        );
    return http.build();
}

 

session timeout을 감지할 때 이 메커니즘을 사용한다면, 사용자가 로그아웃하고 나서 브라우저를 닫는 것 없이 다시 로그인 한다면 에러를 기록할 수도 있다는 점을 명심해라. (왜...?)

 

이것은 session을 무효화하고 사용자가 로그아웃 했어도 session을 다시 제출했을 때 쿠키가 지워지지 않았기 때문이다. 

(결국 쿠키가 남아있어서 생긴 문제였다는 것이다!)

 

확실히 로그아웃시에 로그아웃 핸들리(logout handler)를 이용해서 JSESSIONID 쿠키를 지울 수 있을지 모른다.

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .logout(logout -> logout
            .deleteCookies("JSESSIONID")
        );
    return http.build();
}

 

3. 동시 세션 제어(Concurrent Session Control)

 

 

한명만 어플리케이션에 로그인 하게 하려 한다면,  Spring Security는 다음 샘플을 통해 창의적으로 이 문제를 도와준다.

 

먼저, session lifecycle 이벤트에 대한 Spring Security 업데이트을 유지하기 위해 listener를 설정한다. 

 

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

 

그리고 다음을 application context에 추가한다.

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .maximumSessions(1)
        );
    return http.build();
}

 

이것은 사용자가 여러번 로그인 하는것을 방지할 것이다. 두번째 로그인은 첫번째 로그인을 무효화할 것이다. 자주 두번째 로그인을 막는것을 선호할 것이다. 그러면 다음을 사용하면 된다. 

 

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -> session
            .maximumSessions(1) // 최대 동시 접속 세션은 1개
            .maxSessionsPreventsLogin(true) //동시 로그인 차단, false인 경우 기존 세션 만료(default)
        );
    return http.build();
}

 

두번째 로그인은 그리고 나서 거절될 것이다. 거절됨에 따라 사용자가 폼 기반 로그인이 사용된다면 authentication-failure-url보내진다. 

 

두번째 인증이 또 다른 non-interactive한 메커니즘으로(remember-me같은 것) 발생한다면, unauthorized에러 가 사용자에게 에러를 전송할 것이다. 대신에 당신이 에러 페이지를 사용하길 원한다면, session-authentication-error-url속성을 session-management에 추가할 수 있다.

 

만약 폼 기반 로그인으로 커스터마이트된 인증 필터를 사용한다면, 면확하게 동시 세션 제어 지원을 설정해야만 한다. 

(session manegement관련 내용은 밑에 나옴)

 

4. Session Fixation Attack Protection

세션 고정 공격 (Session Fixation Attack)은 악의적인 공격자가 사이트 접근으로 세션을 생성해서, 같은 세션으로 다른 사용자가 접속하도록 하는것이 가능한 잠재적인 공격이다.

 

Spring Security는 새로운 세션을 자동적으로 생성해서 이것을 막던가 session ID를 사용자가 로그인 시 변경하는 것으로 막는다. 이러한 보호가 필요없다면 혹은 다른 요구사항과 충돌할 수 있다면, 4가지 옵션을 가진 <session-management> 속성으로 session-fixation-protection을 사용해서 제어가 가능하다. 

 

  • none -  아무것도 하지 마라! 원래 세션이 얻어질 것이다.
  • newSession - 새로운 clean 세션을 생성한다. 존재하는 세션 데이터를 복사하는 것 없이!
  • migrateSession - 새로운 세션을 생성하고 새로운 세션으로 모든 존재하는 세션 속성을 복사한다. servler3.0이나 더 낮은 container에서 이게 기본이다. 
  • changeSessionId - 새로운 세션을 생성하지 않는다. 대신에, Servelet Container에서 제공되는 세션 고정 보호를 사용한다. 

세션 고정 보호(session fixation protection)이 발생하면, 이것이 application context에서 발행된 SessionFixationProtectionEvent를 초래한다.


 

여기서 session fixation보호는 왜 필요한지 간단히 보고 가자

 

공격자가 일부러 우리 서버에 접속을 해서 JSESSIONID 쿠키를 발급한다.

공격자인지 모르는 우리의 착한 서버는 그냥 주는데 공격자가 그것을 사용자에게 준다. 

 

그러면 사용자는 공격자가 준 쿠키인줄 모르고 그대로 그것을 가지고 로그인을 한다. 

 

진짜로 로그인에 성공하게 된다면 이미 인증이 되어있는 쿠키를 이용해 공격자가 접속을 한다. 

 

하지만 이런 일은 로그인을 할 때 변조되지만 않으면 된다. 

 

5. SessionManagementFilter

SessionManagementFilter는 사용자가 현재 요청동안(remember-me같은 전형적으로 non-interactive 한 인증 메커니즘) 인증되었는지 아닌지 결정하기 위해 현재 SecurityContextHolder에 대해 SecurityContextRepository의 내용을 확인한다. 

 

만약 repository가 security context를 포함하면, filter는 아무것도 안한다. 그렇지 않다면(security context를 포함하지 않는다면), 그리고 thread-local SecurityContext가 인증 객체를 포함한다면, filter는 이전 필터에 의해 인증되었다고 가정한다. 

 

이것은 설정된 SessionAuthenticationStrategy를 호출할 것이다. 

 

만약 사용자가 현재 인증되지 않았다면, 필터가 무효한 세션 아이디가 요청됐는지 아닌지를 확인하고 InvalidSessionStrategy를 호출할 것이다. 

 

가장 흔한 행위는 고정된 URL로 redirect하는 것이고 이 행위는 표준 구현체인 SimpleRedirectInvalidSessionStrategy안에 캡슐화되있다. 후자는(여기서는 SimpleRedirectInvalidSessionStrategy) 또한 무효화된 session URL를 일찍이 묘사된 namespace를 통해 설정할 때  사용된다. 

 


SessionManagementFilter에 대한 설명을 정리하자면, 다음과 같다.

 

역할

  1. 세션관리 : 인증 시 사용자의 세션 정보를 등록, 조회, 삭제 등의 세션이력을 관리
  2. 동시적 세션 제어 : 동일 계정으로 접속이 허용되는 최대 세션수를 제한
  3. 세션 고정 보호 : 인증 할 때 마다 세션 쿠키를 새로 발급하여 공격자의 쿠키 조작을 방지
  4. 세션 생성 정책 : Always, If_Required, Never, Stateless

ConcurrentSessionFilter도 있는데, 이것의 역할은 다음과 같다. 

 

역할

  1. 매 요청 마다 현재 사용자의 세션 만료 여부 체크
  2. 세션이 만료되었을 경우 즉시 만료 처리

 


7. SessionAuthenticationStrategy

SessionAuthenticationStrategy는 SessionManagementFilter와 AbstarctAuthenticationProcessingFilter에서 둘 다 사용된다. 그래서 커스터마이즈된 폼 로그임 class를 사용한다면, 두 경우에 SessionAuthenticationStrategy를 inject해야 한다. 

 

이러한 경우, namespace를 결합하고 설정하는 것과 커스팀 빈은 다음과 같다.

 

<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
	<beans:property name="sessionAuthenticationStrategy" ref="sas" />
	...
</beans:bean>

<beans:bean id="sas" class=
"org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />

 

8. 동시성 제어(Concurrency Control)

Spring Security는 여러번 구체화된 수 이상으로 같은 어플리케이션에서 동시적으로 인증하는 것으로부터 principal을 막아낼 수 있다. (간단히 이야기하면 한 아이디로 여러번 접속을 막음)

 


 

이번에는 세션 관리(SessionManagement)에 대해 알아보았다.