이 게시글은 https://developer.mozilla.org/ko/docs/Web/HTTP/CORS 의 내용을 토대로 작성되었습니다.
CORS란
교차 출저 리소스 공유 CORS는 추가 HTTP 헤더를 사용하여, 한 출저에서 실행 중인 웹 어플리케이션이 다른 출저의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 정책이다.
아래는 교차 출처 요청의 예시이다. domain-a.com 이라는 사이트가 보내는 Http Request의 Origin은 domain-a.com이라고 볼 수 있다. 이 사이트가 domain-b.com 이라는 Host에게 자원 요청을 하게 된다면, 이 상황을 바로 교차 출저 리소스 공유라고 한다.
보안 상의 이유로, 브라우저는 기본적으로 CORS를 제한한다. 즉 동일 출저 끼리만 자원을 공유할 수 있도록 제한한다. 만약 다른 출저의 리소스가 필요하다면 알맞은 CORS 헤더를 포함한 응답을 반환해야 한다. 이때 서버는 Access-Control-Allow-Origin 헤더를 통해서 자원을 요청할 수 있는 도메인을 명시할 수 있다. 즉 domain-b.com 서버가 domain-a.com 에서만 자원을 요청할 수 있도록 하고 싶다면 Access-Control-Allow-Origin: "http://domain-a.com" 이라고 설정하면 된다. 이때 도메인이 같아도 Http 프로토콜과 포트가 다르면 다른 Origin이므로 주의해야 한다.
CORS의 보안 상의 문제는 무엇인가?
문제 상황은 공격자가 보안이 취약한 특정 웹서버에 악성 스크립트 파일을 업로드하면서 시작된다. 공격자는 해당 악성 스크립트 파일을 링크하는 게시글을 공격 대상 서버에 등록한다. 즉 교차 출저 리소스 공유를 이용하는 것이다.
이제 피해자가 악성코드가 링크된 글을 클릭하면 악성코드가 수신 및 실행되고 악성 스크립트에 의해서 특정 공격이 수행된다. 이는 피해자의 세션을 오용한 접근제어와 관련된 문제가 발생할 수 있으므로 매우 위험한 공격이 될 수 있다. 또한 공격자는 피해자의 세션을 통해 접근제어를 우회하고 내부 네트워크에 접근할 수 있다.
위에서 교차 출저 리소스 공유의 보안 문제를 알게 되었다면 Access-Control-Allow-Origin을 와일드카드(" * ")로 설정하는 일은 자제해야 함을 알 수 있다. 즉 모든 Origin에서 자원을 요청할 수 있게 하는 행위는 자제해야 한다는 것이다. 물론 보안이 중요하지 않은 간단한 토이 프로젝트에서는 상관없겠지만 말이다.
그리고 Access-Control-Allow-Origin을 와일드카드(" * ")로 설정했을 때 적용되는 제약이 있다. 사용자가 Http Request 시 쿠키와 세션 쿠키 또는 Authorization 헤더와 같은 자격 증명 데이터를 전송한다면, 즉 withCredentials()가 true로 설정되어 있다면 HTTP는 쿠키, 세션 쿠키, Authorization 헤더를 전송하지 않는다. 왜냐하면 모든 Origin에서 이 리소스를 요청할 수 있기 때문에 교차 출저 리소스 공유의 취약점에 노출될 수 있다고 판단했기 때문이다.
즉 서비스에서 쿠키, 세션 쿠키, Authorization 헤더와 같은 자격 증명 데이터를 전송해야 한다면 Access-Control-Allow-Origin을 명확하게 명시해줘야 한다.
OPTIONS와 Preflight 요청
만약 클라이언트가 전송한 Http 요청이 서버 데이터에 SideEffect를 일으킬만한 요청이라면(예를들어 POST, DELETE 등) 브라우저는 먼저 OPTIONS 메서드로 Preflight 요청을 보낸다. 서버는 이에 대한 응답으로 서버에서 지원하는 "Http 메서드", "자격 증명이 필요한지"와 같은 정보를 전송한다. 또한 클라이언트의 도메인이 Access-Control-Allow-Origin에 명시되어 있는지 확인한다. 반면에 GET 같은 단순 요청은 Preflight 요청이 필요하지 않다.
Preflight 요청을 보내는 이유는 서버 데이터에 SideEffect를 일으킬만한 요청이기 때문이다. 미리 해당 요청이 서버에서 수락하는 요청인지 확인하는 과정이라고 볼 수 있다.
서버는 다음과 같은 응답으로 CORS를 지원하는 Origin과 요청에 사용할 수 있는 Method와 Header를 알려준다. 그리고 Max-Age는 Preflight 응답을 캐시할 수 있는 시간을 알려준다. 즉 86400초 까지 Preflight 응답을 캐싱하여 사용할 수 있다는 것을 의미한다.
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials
서버에서 Access-Control-Allow-Credentials를 true로 설정하지 않으면 Credentials 정보를 이용한 Http 통신에 문제가 발생할 수 있다. 브라우저가 서버의 응답 중 Credentials 정보를 클라이언트 측에 노출하지 않기 때문이다. 즉 자격 증명 정보를 클라이언트 측에 노출하고 싶다면 서버에서는 Access-Control-Allow-Credentials: true로 설정해야 한다. 또한 Preflight 요청의 응답에서는 자격 증명 정보를 전송할 수 있는지에 대한 여부를 나타내는 용도로 쓰인다.
Access-Control-Allow-Credentials: true로 설정했을 때 Access-Control-Allow-Origin을 와일드카드로 설정할 수 없음을 위에서 설명하였다.
Spring Security에서 CORS 설정하기
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource())
...
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8888"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Spring Boot에서 WebMvcConfigurer를 통해 CORS 설정하기
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8888")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3000);
}
}
" * "는 모두 허용한다는 의미로 사용되지만 허용되는 것들을 세부적으로 설정해주는 것이 보안에 좋을 것이다.
CORS 정책상 allowedOrigin을 " * "로 설정하면 Credentials를 전송할 수 없도록 되어있다. 따라서 쿠키, 세션 쿠키, Authorization 헤더 사용에 제약이 따른다.
Ref.
https://docs.spring.io/spring-security/reference/reactive/integrations/cors.html
'CS' 카테고리의 다른 글
OSI 7계층 정리 (0) | 2022.10.08 |
---|---|
HTTPS에서 대칭키를 사용하는 방법 (0) | 2022.03.27 |
IP(프로토콜) (0) | 2022.03.20 |