CORS

CORS는 Cross Origin Resource Sharing의 약자다

url 구조

cors를 알기 위해서는 url구조를 먼저 알아야한다

https://kgong1013.github.io:443/main?page=1#Origin

  • https://: Protocol
  • kgong1013.github.io: Host
  • :443: Port (생략가능)
  • /main: Path
  • ?page=1: Query String
  • #Origin: Fragment

Origin이란

Protocol, Host, Port를 합친 것을 말한다. 만일 이중 하나라도 다른 것이 있다면 그것은 같은 Origin이라고 할 수 없다.

SOP(Same Origin Policy)

브라우저는 기본적으로 SOP를 지켜서 다른 출처의 리소스 접근을 금지시킨다. 이 정책을 지키면 XSS나 XSRF등의 보안 취약점을 노린 공격을 방어할 수 있다. 외부 리소스를 사용하기 위한 예외를 CORS라고 한다.

CORS 동작 원리

브라우저가 다른 출처의 리소스를 가지고 오려면 출처에서 올바른 CORS헤더를 포함한 응답을 반환해야한다.

Simple Request

img

브라우저가 외부 서버에 요청시 헤더에 Origin: foo.example의 정보를 헤더에 담아서 전송을한다. 서버에서는 이에 대한 응답으로 Access-Control-Allow-Origin: *을 응답 헤더에 담아서 전송을한다. 이것이 가장 간단한 접근 방법이다. Access-Control-Allow-Origin: *은 모든 도메인에서 접근할 수 있다는 것을 의미한다. 만일 도메인 접근을 한곳으로만 제한하려면 다음과 같이 응답이 되어야한다.

Access-Control-Allow-Origin: foo.example

리소스 접근이 허용되려면 Access-Control-Allow-Origin헤더라는 요청에 Origin헤더에 전송된 값이 포함되어야 한다.

Preflight Request

img

preflight는 OPTIONS 메소드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청을 전송하기에 안전한지 확인한다. preflight request부분에서 options는 서버에서 추가 정보를 판별하는데 사용하는 HTTP/1.1 메서드이다. safe메서드 이기 때문에 리소스를 변경하는데 사용할 수 없다.

위 그림에서 OPTIONS에는 두개의 헤더가 전송된 것을 확인할 수 있다.

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method는 실제 요청시 POST로 보낸다는 것을 알려준다.

Access-Control-Request-Headers는 실제 요청을 보낼때 X-PINGOTHER, Content-Type헤더가 함께 전송된다는 것을 알려준다. 이런것을 서버에서 수락할 수 있는지 결정하게된다.

preflight request의 응답 헤더를 보면 다음과 같이 전송된 것을 확인할 수 있다.

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-Origin: http://foo.example도메인에 접근할 수 있다고 알려준다

Access-Control-Allow-Methods: 사용가능한 요청메서드라고 알려준다.

Access-Control-Allow-Headers: 실제 요청에 해당 헤더를 사용할 수 있음을 알려준다.

Access-Control-Max-Age: 이것은 prefilght-request에 대한 응답을 캐시할 수 있는 시간을 말한다.

그후 preflight-request가 완료되면 실제 요청을 보낸다.

Credentialed Request

img

인증정보를 포함한 요청이 필요할때 사용하는 방법이다.

XMLHttpRequstFetch호출에서 자격증명을 보내지는 않고 플래그만을 보낸다. 이때 응답의 Access-Control-Allow-Credentialstrue로 오지 않을 경우 응답을 거부한다. 자격증명 요청에 응답할 때는 Access-Control-Allow-Origin헤더 값이 *와일드 카드보다는 출처를 명확히 증명해야한다.

Http 헤더

응답 헤더

  • Access-Control-Allow-Origin 단일 출처 혹은 와일드 카드를 사용해 리소스에 접근 가능한 출처들을 허용하도록 한다.
  • Access-Control-Expose-Headers 브라우저가 접근할 수 있는 헤더를 서버의 화이트리스트에 추가한다.
  • Access-Control-Max-Age preflight request에 대한 응답의 캐시시간을 말한다.
  • Access-Control-Allow-Credentials 플래그가 true일 때 응답을 표시할 수 있는지 나타낸다. preflight의 응답으로 사용할 경우 실제 응답할 수 있는지 나타낸다. simple request의 경우 이 헤더가 리소스와 함께 반환되지 않는다.
  • Access-Control-Allow-Methods 리소스에 접근할 때 허용되는 메서드를 지정한다. preflight의 응답으로 사용된다.
  • Access-Control-Allow-Headers 실제 사용할 수 있는 HTTP 헤더를 나타낸다.

요청 헤더

  • Origin cross-site 접근 요청 또는 preflight request의 출처를 나타낸다.
  • Access-Control-Request-Method 실제 요청시 어떤 HTTP 메소드를 사용할지 서버에게 알려준다. preflight시에만 사용된다.
  • Access-Control-Request-Header 실제 요청시 어떤 HTTP 헤더를 사용할지 서버에게 아려준다. preflight시에만 사용된다.

스프링에서 CORS 설정

컨트롤러에서 선언

CrossOrigin 어노테이션을 이용하는 방법이다.

@RestController
public class TestController {
    @CrossOrigin
    @GetMapping("/test")
    public String test() {
        return "test";
    }
}

Spring 4.2 부터 지원하는 어노테이션이다. 기본값은 모든 도메인, 모든 요청에 대해서 허용한다는 뜻이고. test() 메소드는 모든 도메인, 모든 요청으로 부터 허용되는 컨트롤러 메소드가 된다. 해당 컨트롤러에 모두 적용하고 싶으면 컨트롤러에 어노테이션을 설정한다.

@CrossOrigin
@RestController
public class TestController {
    @GetMapping("/test")
    public String test() {
        return "test";
    }
}

만일 모든 도메인이 아닌 특정 도메인에 대해서만 CrossOrigin을 허용하고 싶으면 origins를 세팅해주면 된다.

@CrossOrigin(origins = "http://domain1.com, http://domain2.com")
@RestController
public class TestController {
    @GetMapping("/test")
    public String test() {
        return "test";
    }
}

위와 같이 코드를 작성할 경우 TestController에 선언된 url에 접근하는 origin 도메인을 확인후 origins값에 해당하는 도메인일 경우 접근을 허용한다.

글로벌 선언

설정 클래스를 만들어 전역적으로 CrossOrigin을 설정할 수 있다.

@Configuration
public class WebConfig implements WebMvcConfigurer {
	@Override
    public void addCorsMapping(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("http://domain1.com", "http://domain2.com")
            .allowedMethods("GET", "POST")
            .maxAge(3000);
    }
}

WebMvcConfigurer의 구현체를 만들고 cross origin정보를 등록한다.

  • addMapping : CORS를 적용할 url패턴을 정의한다.
  • allowedOrigins : 리소스를 허용할 도메인들을 지정할 수 있다.
  • allowedMethods : 리소스를 허용할 요청 메소드를 지정할 수 있다.
  • maxAge : preflight 캐싱시간을 지정할 수 있다.

출처

[교차 출처 리소스 공유 (CORS) - HTTP MDN (mozilla.org)](https://developer.mozilla.org/ko/docs/Web/HTTP/CORS)
[[Browser] CORS란? Beomy](https://beomy.github.io/tech/browser/cors/#cors-동작원리)

[Spring Boot] CORS 설정하기 (tistory.com)