본문 바로가기
JAVA/Spring Security

SPring Security에서 SecurityFilterChain의 사용법과 CORS, CSRF

by GangDev 2024. 3. 30.

 

WebSecurityConfigurerAdapter는 Spring Security에서 웹 보안 설정을 쉽게 구성하고 커스터마이즈할 수 있도록 도와주는 확장 가능한 기본 클래스이다.
이 클래스를 상속받아서 사용하면 Spring Security의 웹 애플리케이션 보안을 구성하는 과정이 간편해지며, 개발자가 필요한 보안 규칙을 정의하고 맞춤 설정을 할 수 있다.

 

WebSecurityConfigurerAdapter 클래스는 configure(HttpSecurity http) 메소드를 오버라이드하여 웹 보안 설정을 조정할 수 있는데, 이 메소드 내에서 다양한 보안 관련 옵션을 구성할 수 있다.
주로 다음 같은 작업을 수행할 수 있다.

 

URL 패턴별 권한 설정: 특정 URL 패턴에 대한 접근 권한을 설정할 수 있으며, 누가 해당 URL에 접근할 수 있는지 정의한다.
로그인 및 로그아웃 설정: 로그인 페이지, 로그인 성공 또는 실패 후의 리다이렉션, 로그아웃 처리 방법 등을 구성한다.
CSRF(Cross-Site Request Forgery) 보호 설정: CSRF 공격으로부터 애플리케이션을 보호하기 위한 설정을 활성화할 수 있다.
세션 관리: 세션 타임아웃, 세션 고정 공격 보호 등 세션 관련 설정을 구성한다.
사용자 정의 필터 추가: 필요한 경우 사용자 지정 보안 필터를 추가하여 특정 요청에 대한 사용자 정의 로직을 적용한다.
HTTPS 설정: HTTPS를 사용하도록 강제하는 설정을 추가할 수 있다.

 

하지만 Spring Security 5.4 이후부터는 WebSecurityConfigurerAdapter의 사용이 권장되지 않으며, 대신 SecurityFilterChain 빈을 직접 정의하는 방식이 권장된다.
SecurityFilterChain은 더 유연한 방식으로 보안 설정을 정의할 수 있게 해주며, 더 강력한 보안을 제공한다.
이렇게 하면 더욱 정교한 웹 보안 구성이 가능하며, 새로운 기능을 더욱 쉽게 활용할 수 있다.

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorize -> authorize
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout.permitAll());
        return http.build();
    }
}

 

.authorizeRequests() : 이 메소드는 URL 패턴별 권한을 설정하는 메소드를 호출한다. 예제에서는 / 와 /home 경로에 대해 모든 사용자에게 접근 권한을 허용하고, 나머지 모든 요청은 인증된 사용자에게만 허용하도록 설정하고 있다.
.formLogin() : 이 메소드는 로그인 관련 설정을 정의한다. loginPage("/login") 은 로그인 페이지를 /login 경로로 설정하고, .permitAll() 은 로그인 페이지에 대한 모든 사용자의 접근을 허용한다.
.logout() : 이 메소드는 로그아웃 관련 설정을 정의한다. .permitAll()은 로그아웃에 대한 모든 사용자의 접근을 허용한다.
return http.build() : http.build() 메소드는 HttpSecurity 객체를 빌드하고 SecurityFilterChain을 반환한다. 이렇게 정의된 SecurityFilterChain 은 SpringSecurity에서 웹 보안 구성에 사용된다.


CORS (Cross-Origin Resource Sharing)는 웹 애플리케이션의 보안 정책 중 하나로, 웹 페이지가 다른 출처(도메인, 포트, 프로토콜)에 있는 리소스에 접근할 수 있도록 허용하는 메커니즘이다.
웹 브라우저에서 실행 중인 JavaScript코드가 다른 출처의 API나 리소스에 HTTP 요청을 보내는 것을 허용하는 것을 의미한다.

 

CORS가 필요한 이유는 다음과 같다.

 

보안: 웹 보안을 강화하기 위해 브라우저는 동일 출처 정책(Same-Origin Policy)을 따른다. 이로 인해 웹 페이지에서는 원래 출처와 다른 출처의 리소스에 접근할 수 없다. 하지만 일부 상황에서는 다른 출처의 리소스에 접근이 필요할 수 있다. 이때 CORS를 사용하여 보안적으로 안전하게 접근을 제어한다.
웹 애플리케이션 통합: 웹 애플리케이션은 여러 다른 출처에서 제공되는 서비스 및 데이터를 통합할 수 있어야 한다. 예를 들어, 웹 애플리케이션이 다른 도메인의 API를 호출하거나, 다른 출처의 이미지를 로드하는 경우가 있다.

 

HttpSecurity 설정을 통해 CORS를 구성할 수 있다.
cors() 메소드를 사용하여 CORS 구성을 활성화하고, .and() 메소드를 사용하여 다른 보안 설정과 연결한다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .cors() // CORS 설정 활성화
        .and()
        .authorizeRequests()
        .antMatchers("/public/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login")
        .permitAll()
        .and()
        .logout()
        .permitAll();
}

 

CORS를 활성화한 후, 실제 CORS 정책을 정의해야 한다.
이는 CorsConfigurer 를 사용하여 구성할 수 있다.

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*")); // 모든 출처 허용
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); // 허용할 HTTP 메소드
    configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); // 허용할 헤더

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

 

위의 설정은 모든 출처에서의 요청을 허용하고, GET, POST, PUT, DELETE 메소드를 허용하며, Authorization 및 Content-Type 헤더를 허용한다.


CSRF(Cross-Site Request Forgery)는 웹 보안 공격 중 하나로, 공격자가 사용자의 의지와는 무관하게 웹 애플리케이션에서 특정 동작을 수행하도록 유도하는 공격 기법이다.
이러한 공격은 사용자가 인증된 상태로 웹 애플리케이션에 접근하고 있을 때, 공격자가 웹 애플리케이션에서 특정 작업을 수행하도록 만들어 사용자의 계정 또는 데이터를 악의적으로 조작하는 데 사용된다.

CSRF 공격의 동작 원리는 다음과 같다.

 

공격자는 악성 웹 사이트나 이메일을 통해 피해자에게 특정 웹 애플리케이션에 대한 요청을 포함한 링크나 이미지를 제공한다.
피해자가 악성 링크를 클릭하거나 악성 이미지를 열면, 브라우저는 공격자의 의도에 따라 웹 애플리케이션에 요청을 보낸다.
피해자가 웹 애플리케이션에 로그인되어 있고, 그 요청이 인증된 사용자로서 처리되면 공격이 성공하게 된다. 웹 애플리케이션은 사용자의 의지와 무관하게 요청을 처리하게 되며, 이로써 공격자가 원하는 액션을 수행하게 된다.

 

CSRF 공격을 방지하기 위한 주요 방법 중 하나는 CSRF 토큰을 사용하는 것이다.
CSRF 토큰은 웹 애플리케이션에서 발생하는 모든 중요한 요청에 포함되는 무작위 문자열로, 사용자의 세션에 바인딩되어 있다.
이 토큰을 공격자가 알아내거나 조작하는 것은 매우 어렵기 때문에 공격을 예방할 수 있다.

 

Spring Security 는 CSRF 공격을 방지하기 위해 기본적으로 CSRF 보호 기능을 활성화하고, CSRF 토큰을 사용한다.
이로써 웹 애플리케이션이 사용자의 세션과 토큰을 일치시켜 요청의 유효성을 검증하고, 악성 요청을 거부할 수 있게 된다.
CSRF 보호를 활성화하면 Spring Security는 요청 헤더에 CSRF 토큰을 추가하고, 요청이 수신될 때 이 토큰을 검증하여 CSRF 공격을 방지한다.

http.csrf().disable(); // CSRF 보호를 비활성화하는 경우

 

일반적으로 CSRF 보호는 활성화 상태를 유지하는 것이 좋지만, API 서버와 같이 쿠키를 사용하지 않는 인증 메커니즘을 사용하는 경우에는 CSRF 보호를 비활성화할 수 있다.

 

---

24년 2월 5일 작성된 글입니다