Cross Origin Resource Sharing

브라우저에서 다른 출처의 리소스를 공유하는 방법

브라우저 정책 중 하나이다

출처란

URL 구성요소 중 Protocol, Host, Port 를 합친 것

ex, https://www.naver.com:8080

만약 두 URL에서 세 구성요소 (Protocol, Host, Port) 중 하나라도 다르다면 다른 출처로 인식한다.

대상 URL : https://ihoo.github.io/index

URL출처이유
https://ihoo.github.io/about일치-
https://ihoo.github.io/about?q=work일치-
https://ihoo.github.io/about#work일치-
http://ihoo.github.io불일치Protocol 불일치
https://ihoo.github.io:81/about불일치Port 불일치
https://ihoo.heroku.com불일치Host 불일치

동일 출처 정책(Same-Origin Policy)

웹 브라우저 보안을 위해 같은 출처의 서버로만 자원을 공유(주고받도록)하도록 상호작용을 제안하는 보안 정책으로, 악의적인 자원을 분리하기위해 사용되는 브라우저 정책 이다

CORS는 이러한 SOP 로 인한 API 서버와의 자원공유의 어려움을 해결하기위해 나왔다.

즉, CORS 는 외부 자원을 사용하기위한 SOP 의 예외 조항 이다.

CORS 동작원리

CORS 는 단순 요청 (SimpleRequest) 방식과 예비 요청(PreflightRequest) 방식이 있다.

둘 모두, Access-Control-Allow-Origin 을 통해 SOP 정책의 예와사항으로 둘 출처를 전달받아 이를 검증한다. 만약 헤더의 value 가 * 이라면, 모든 출처에 대한 허용(예외지정)을 의미한다.

단순요청방식 (SimpleRequest)

서버에게 요청을 보내고, 서버에서 Access-Control-Allow-Origin 이라는 헤더를 포함한 응답을 브라우저에게 보낸다.

이후, 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 정책위반 여부를 판단한다.

서버로 전달하려는 리퀘스트가 단순 요청으로 동작하려면 아래 3가지 조건을 만족해야한다

  • 요청의 Http 메서드는 GET, HEAD, POST 중 하나여야 한다.
  • Content-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나이어야한다.
  • 아래 항목에 존재하는 헤더 이외의 헤더를 사용하면 안된다. Accept Accept-Language
    Content-Language Content-Type
    DPR Downlink Save-Data
    Viewport-Width Width

예비요청방식 (PreflightRequest)

서버에 예비 요청을 보내고, 안전성을 판단한 후, 본 요청(GET /resources) 를 보내는 방식이다.

안정성 판단 방법은 아래와 같다.

OPTION 메서드로 서버에 예비요청을 먼저 보낸 이후, 서버에서 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저 보내면, 브라우저는 SingleRequest 와 동일한 방식으로 해당 해더를 확인하여, CORS 정책 위반 여부를 판단한다.

SpringBoot 에서의 CORS Issue 해결 방법

CORS 정책 위반으로 일어나는 문제상황을 CORS Issue 라고 한다.

SpringBoot 에서는 CORS 를 편하게 설정할 수 있도록 다음과 같은 Configuration 을 제공한다

@EnableWebSecurity
@Configuation
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          //doSomething
            .authorizeRequests()
            .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() 
          //doSomething
            .anyRequest().authenticated().and()
            .cors().and();
    }

    @Bean
   public CorsConfigurationSource corsConfigurationSource() {
       CorsConfiguration configuration = new CorsConfiguration();
       //doSomething
       configuration.addAllowedOrigin("*");
       configuration.addAllowedMethod("*");
       configuration.addAllowedHeader("*");
       configuration.setAllowCredentials(true);
       configuration.setMaxAge(3600L);
       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
       source.registerCorsConfiguration("/**", configuration);
       return source;
   }
}

.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
SpringSecurity 모듈은 기본적으로, 모든 인증되지 않은 요청에 대해 401 Error 를 반환한다
그러나, 이전에 서술하였던 예비 요청 방식의 CORS 정책 검증은 본 요청을 보내기 전, 인증헤더가 없는 예비 요청을 전송한다
따라서, 이러한 예비 요청에 대해 permitAll() 을 통해 인증 헤더 없이도 접근 가능하게 해놓지 않는다면, 예비 요청 방식으로 CORS 를 검증해야할 때 CORS Issue 가 발생할 위험이 높다

.cors().and();
SpringSecurity 에서 cors 를 적용한다는 뜻이다.
Origin 헤더를 포함하는 모든 요청에 대해 인증 성공 여부와는 관계 없이, Access-Control-Allow-Origin 을 포함한 응답을 전송한다

public CorsConfigurationSource corsConfigurationSource()
CORS 정책에 대한 정보를 담고 있는 Bean 을 작성한다.
어떠한 자원을 SOP 예외(Cross-Origin) 로 처리할지 등을 설정한다.

profile
테오의 스프린트 17기 퍼실리테이터

0개의 댓글