CORS

서로 다른 도메인으로 자원을 호출하는 것을 의미한다. 여기서 서로 다른 도메인이란 쉽게 말해서, ip, port가 다른 것을 말한다.

브라우저는 보안 상의 이유로 XMLHttpRequest는 same-origin 정책을 따르기 때문에 다른 도메인으로 자원을 요청하게 되는 경우 cors 이슈가 발생하는 것이다.

CORS 요청의 종류

Simple Request (간단한 요청)

간단한 cors 요청은 다음의 조건에 해당한다.

  • 메서드
    • GET
    • HEAD
    • POST
  • 수동 설정이 허용되는 헤더
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
  • Content-Type 헤더에 대해 허용되는 값
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Preflight (사전 요청)

preflight는 실제 요청을 전달하기 전에 유효여부를 판단하기 위해, 다른 도메인의 자원에 OPTIONS 메소드로 http 요청을 전송하는 것을 말한다. preflight의 응닶에 따라 실요청 여부를 판단한다.

preflight가 실행되는 경우는 아래의 조건에 해당한다.

  • GET, HEAD, POST 외의 메소드
  • POST 요청이 Content-Type이 Simple Request 수행 조건 외의 값을 가진 경우
  • 커스텀 헤더가 정의된 경우

Request with Credential (인증을 이용한 요청)

HTTP Cookie와 HTTP Authentication 정보를 인식할 수 있게 해주는 요청이다. 기본적으로 브라우저는 자격 증명을 위한 정보를 전송하지 않으므로, withCredentialstrue로 설정을 해주어야 한다.

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

CORS 이슈 발생 시

클라이언트에서 해결하는 방법

SOP 설정 끄기

웹 브라우저 실행 시 외부 요청을 허용하기 위해 SOP(Same Origin Policy) 설정을 끄고 실행한다. 예를 들어, 크롬의 경우에는 커맨드 라인 옵션을 이용하여 아래와 같이 브라우저를 실행한다.

// OSX
open -a Google\ Chrome --args --disable-web-security
 
// Windows, 크롬이 설치 된 폴더에서
chrome.exe --disable-web-security

CORS 관련 확장 플러그인 설치

외부 요청을 허용하게 해주는 플러그인을 설치한다.

위의 1, 2의 경우, 개발자의 경우 회피가 가능한 방법이지만, open api를 제공하거나 일반 사용자가 사용하는 웹 어플리케이션이라면 적용이 불가능한 방법이라고 할 수 있다.

JSONP 방식으로 요청

웹 브라우저에서 리소스(css, js) 파일들은 SOP 영향을 받지 않고 로딩이 가능하다. 이러한 부분을 이용하여 서버에서 읽어온 응답 파일을 json으로 바꾸어 로딩하는 방법이다. 하지만, GET 방식의 API에서만 적용이 가능하다.

서버에서 해결하는 방법

모든 요청을 허용한다

가장 쉬운 처리 방법이다. 모든 요청의 응답에 아래의 헤더를 추가한다.

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization
  • Access-Control-Allow-Origin : 어떠한 도메인에 대해 허용할 것인지
  • Access-Control-Allow-Methods : 허용할 요청 메소드
  • Access-Control-Max-Age : Preflight request를 브라우저에 캐시할 시간을 설정. 초단위

선별적으로 허용하는 경우

클라이언트의 헤더값을 보고 서버에서 응답헤더에 해당 출처를 허용하는 경우에, 담아서 전달한다.

서버의 Cross Domain 이슈가 있는 지 테스트 하기

서버에서 Cross Domain을 허용하고 있는 지에 대해 테스트하는 방법

curl

curl을 이용한 테스트 방법은 cURL 명령어로 하는 초간단 CORS 테스트에 작성된 글을 참고했다.

curl \
--verbose \
--request OPTIONS \
http://localhost:8088/vas/od/ondemand/image/upload-image-file \
--header 'Origin: http://localhost:8000' \
--header 'Access-Control-Request-Headers: Origin, Accept, Content-Type' \
--header 'Access-Control-Request-Method: POST'
--header 'api-key: test-vas'

** cros 설정 적용 되지 않은 경우 **

``bash

  • Trying ::1…
  • TCP_NODELAY set
  • Connected to localhost (::1) port 8088 (#0)

    OPTIONS /vas/od/ondemand/image/upload-image-file HTTP/1.1 Host: localhost:8088 User-Agent: curl/7.54.0 Accept: / Origin: http://localhost:8000 Access-Control-Request-Headers: Origin, Accept, Content-Type Access-Control-Request-Method: POST

    < HTTP/1.1 401 < Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH < Content-Length: 0 < Date: Thu, 29 Mar 2018 05:59:41 GMT <

  • Connection #0 to host localhost left intact ```

** cros 설정 적용 되지 않은 경우 **

* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8088 (#0)
> OPTIONS /vas/od/ondemand/image/upload-image-file HTTP/1.1
> Host: localhost:8088
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://localhost:8000
> Access-Control-Request-Headers: Origin, Accept, Content-Type
> Access-Control-Request-Method: POST
>
< HTTP/1.1 200
< Access-Control-Allow-Origin: http://localhost:8000
< Vary: Origin
< Access-Control-Allow-Methods: GET,HEAD,POST
< Access-Control-Allow-Headers: Origin, Accept, Content-Type
< Access-Control-Allow-Credentials: true
< Access-Control-Max-Age: 1800
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
< Content-Length: 0
< Date: Thu, 29 Mar 2018 06:00:48 GMT
<
* Connection #0 to host localhost left intact

cors 테스트 웹페이지

https://www.test-cors.org/

Reference