스프링 시큐리티에 자동 적용되는 CSRF 보호 때문에 HTTP POST를 구현할 땐 CSRF 보호를 비활성화 하는 보조 명령csrf().disable()
을 추가해야 했다. CSRF 보호는 무엇이고, 이를 언제 이용해야 하는지 정리해 봤다.
CSRF는 공격자가 사용자의 브라우저를 속여 사용자의 동의 없이 웹사이트에서 동작을 수행하는 웹 공격 유형이다.
사용자가 로그인 한 상태에서 알 수 없는 링크를 클릭했다고 가정하자. 이때 사용자의 브라우저가 페이지에 접근할 때 위조 스크립트를 받는다. 이제 스크립트는 로그인한 사용자 대신 작업을 행해 애플리케이션에 원치 않는 변경을 적용할 수 있다.
CSRF 토큰
이 사용된다. CSRF 토큰은 서버 측에서 생성되며, 고유한 값을 갖는다.변경 요청
(POST, PUT, DELETE 등)과 함께 전송되며, 서버가 요청을 처리할 때마다 해당 요청에 포함되어야 한다.CSRF 보호의 시작점은 필터 체인의 CsrfFilter다. 이 필터는 요청을 가로채어 변경 요청(POST, PUT, DELETE)일 경우 토큰이 포함된 헤더
가 있는지 확인한다. 토큰을 포함하는 헤더를 추가하지 않거나 잘못된 토큰 값이 포함된 경우 애플리케이션은 요청을 수락하지 않는다.
맞춤형 필터 구현
@Slf4j
public class CsrfTokenLogger implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Object object = request.getAttribute("_csrf"); //_csrf 요청 특성에서 토큰의 값을 얻고 콘솔에 출력함
CsrfToken token = (CsrfToken) object;
log.info("CSRF Token : {}", token.getToken());
chain.doFilter(request, response);
}
}
구성 클래스에 맞춤형 필터 추가
@Configuration
public class ProjectConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.
addFilterAfter(new CsrfTokenLogger(), CsrfFilter.class)+
.authorizeHttpRequests()
.anyRequest().permitAll();
return http.build();
}
}
위 처럼 구성하고 아래 엔드포인트를 호출한다
@RestController
public class HelloController {
@GetMapping("/hello")
public String getHello() {
return "Get Hello!";
}
@PostMapping("/hello")
public String postHello() {
return "Post Hello!";
}
}
이처럼 CSRF 보호가 활성화되어있는 경우엔 변경 요청이 거부된다. POST 요청 시 CSRF 토큰 값을 포함해야 정상적으로 요청된다.
CSRF 보호는 서버 쪽의 세션이나 브라우저 쿠키를 사용하기 때문에 프론트엔드와 백엔드 서버가 동일할 때 사용하는 것이 좋다. REST API의 경우 HTTP와 같이 무상태(stateless)이기 때문에 CSRF 보호를 비활성화 해도 된다.
CORS는 웹 애플리케이션에서 다른 도메인의 리소스에 접근할 수 있는 권한을 부여하는 메커니즘이다.
웹 애플리케이션은 보안 상의 이유로 기본적으로 한 출처(origin)에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호 작용하는 것을 제한한다(= SOP : 동일 출처 정책). 예를 들어, http://example.com의 개발자가 http://example.org 소유자의 REST 엔드포인트를 호출하려고 하면 호출이 거부된다.
CORS 메커니즘으로 이 정책을 완화하고 서로 다른 출처 간의 요청을 허용한다. React나 Vue.js 등의 프레임워크로 개발한 프론트엔드 애플리케이션이 다른 도메인에서 호스팅하는 백엔드의 엔드포인트를 호출할 수 있는 것이다.
CORS를 이용하면 애플리케이션이 서로 다른 도메인 간의 호출을 가능하게 한다.
CORS메커니즘은 HTTP 헤더를 기반으로 작동하며, 가장 중요한 헤더는 다음과 같다
CSRF 보호와 CORS가 함께 언급되는 경우가 많은데, 이는 둘 함께 구현하면 웹 애플리케이션의 보안을 향상시킬 수 있기 때문이다. 둘은 각각 다른 웹 보안 측면을 다룬다.
1. @CrossOrigin 어노테이션으로 CORS 정책 적용
컨트롤러의 메서드 위에 @CrossOrigin 어노테이션을 추가해 적용할 수 있다. 어노테이션의 allowedHeaders 특성과 methods 특성으로 허용되는 헤더와 메서드를 설정할 수 있다.
예시)
@RestController
public class HelloController{
@CrossOrigin(origins = "http://hello.com")
@GetMapping("/api/data")
public String getData() {
// Controller logic
}
}
2. Configuration Class를 생성해 CORS 정책 적용
WebMvcConfigurer
인터페이스를 구현하는 Config 클래스를 생성해 적용할 수 있다.
@CrossOrigin을 사용할 경우 코드가 장황해지고 반복되는 코드가 많아질 것 같아 React와 협업하는 프로젝트에서 나는 이 방법을 사용했다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("OPTIONS", "GET", "POST", "PUT", "DELETE");
}
}
3. Spring Security를 이용해 CORS 정책 적용
아직 프로젝트에 Spring Security를 적용해보지 않아서 추후 추가할 예정
정보 감사합니다.