정수원님의 강의 스프링 시큐리티 완전 정복 [6.x 개정판] 보면서 공부한 내용입니다.
💡 Session 이란?
- Session의 배경 : 클라이언트와 서버가 있는 경우, 클라이언트는 서버에 요청하고 서버는 클라이언트의 응답을 받는다. 클라이언트가 접속을 하면 connect이 생기면서 클라이언트의 정보를 저장한다. 하지만 서버는 클라이언트를 식별할 수 있는 아이디나 값이 없다. 그리고 connect이 계속 유지되는 것이 아니라 http프로토콜은 연결을 끊어버려서 disconnect로 만든다.
만약 최초 요청 후 또 한 번 요청을 했을 때 서버는 클라이언트를 식별할 수 있는 값이 없기 때문에 새로운 클라이언트로 인식을 하게 된다. 즉, 서버는 같은 클라이언트가 요청했을 때 가지고 있는 정보를 활용할 수 있는 방법이 없는 것이다.
- Session의 활용 : 클라이언트가 최초 요청 후에 재요청 할 때 클라이언트에 이름을 쿠키형태로 발급한다. (Session ID) 클라이언트마다 고유한 ID가 생기므로 클라이언트가 재요창할 때 해당하는 ID에 담긴 정보를 가지고 활용할 수 있게 된다.
사용자 인증 시도 차단 (maximumSessions : 1)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/invalidSessionUrl","/expiredUrl").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.sessionManagement(session -> session
.invalidSessionUrl("/invalidSessionUrl")
// 이미 만료된 세션으로 요청을 하는 사용자를 특정 엔드포인트로 리다이렉션 할 Url 을 지정
// => maxSessionsPreventsLogin(false)인 경우에만 사용가능
// => expiredUrl 보다 우선 적용된다
.maximumSessions(1) // 동시 세션 허용 최대 개수 (필수)
.maxSessionsPreventsLogin(true)
// false(기본값)인 경우 : 인증하는 사용자에게 접근을 허용하고 기존 사용자의 세션은 만료됨
// => 사용자 세션 강제 만료 방법
// true인 경우 : 최대 세션 수에 도달했을 때 사용자의 인증을 방지한다
// => 사용자 인증 시도 차단 방법
.expiredUrl("/expiredUrl")
// 세션을 만료하고 나서 리다이렉션 할 URL 을 지정한다
// => maxSessionsPreventsLogin(false)인 경우에만 사용가능
);
return http.build();
}
💡 세션 고정 공격이란?
공격자가 악의적으로 사이트에 접근하여 세션을 생성한 후 다른 사용자가 같은 세션으로 로그인하도록 유도해서 사용자의 모든 권한 및 정보를 공격자가 볼 수 있도록 하는 것
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.sessionManagement((session) -> session
.sessionFixation(sessionFixation -> sessionFixation.newSession())
// sessionFixation에 들어간 4가지 보호 전략
// 1. changeSessionId()
// => 기존 세션을 유지하면서 세션 ID(공격자의 쿠키인 세션ID)만 변경하여 인증 과정에서 세션 고정 공격을 방지하는 방식 (기본값)
// 2. newSession()
// => 새로운 세션을 생성하고 기존 세션 데이터를 날려버리는 방식
// 단, SPRING_SECURITY_로 시작하는 속성은 복사하여 사용한다
// 3. migrateSession()
// => 새로운 세션을 생성하고 모든 기존 세션 속성을 새 세션으로 복사하는 방식
// 4. none()
// => 기존 세션을 그대로 사용하는 방식
// => 세션 고정 공격을 방어하지 않는 방식 (절대 사용 X)
);
return http.build();
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// STATELESS 설정에서도 세션이 생성될 수 있다
// => CSRF 기능이 활성화되어있고 수행된다면 사용자의 세션을 생성하여 CSRF 토큰을 저장하게 된다
// => SecurityContext 영속성에 영향을 미치지 않는다
);
return http.build();
}
즉, ConcurrentSessionFilter는 세션을 만료할 사용자를 찾아서 동시 세션 제어를 유지시키기 위해 만료 설정이 true인 사용자를 만료 처리한다
마지막 업데이트 날짜와 시간을 가지도록 한다