SOP - CORS

ppou·2020년 4월 9일
1

Web Technologies

목록 보기
3/3
post-thumbnail

CORS(Cross-Origin Resource sharing)?

CORS웹 브라우저에서 외부 도메인 서버와 통신하기 위한 방식을 표준화한 스팩입니다. 서버와 클라이언트가 정해진 헤더를 통해 서로 요청이나 응답에 반응할지 결정하는 방식으로 CORS(cross-origin resource sharing, 교차 출처 자원 공유)라는 이름으로 표준화 되었습니다.
SOP의 문제점을 해결하기 위한 정책인 만큼 CORS 방식을 통해 Cross-Origin에서도 Cross-Site HTTP Request를 허용하도록 설정할 수 있습니다.

CORS Request 종류

CORS Request는 다음과 같이 4가지 종류가 있습니다.

  • Simple Request
  • Preflight Request
  • Credential Request
  • Non-Credential Request

Simple Request

CORS Simple Request는 GET, POST, HEAD method를 사용합니다.
특히 POST method를 사용할 경우 Content-type은 application/x-www-form-urlencoded, multipart/form-data, text/plain를 사용해야합니다.
또한 custom header를 사용해서는 안됩니다.

아래의 예에서는 Origin headerhttps://foo.example 도메인을 작성하여 해당 도메인에서 리소스에 접근 가능여부를 확인합니다.
서버의 응답으로 Access-Control-Allow-Origin header:*을 전달합니다.
*은 해당 서버는 모든 도메인에서 리소스에 접근 가능하다는 것을 의미합니다.

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

다음 예는 서버측에서 Access-Control-Allow-Origin header 에서 특정 도메인만 리소스에 접근할 수 있도록 설정하는 예시입니다. 해당 예는 이후 알아볼 Credential Request 입니다.

서버 측에서 Access-Control-Allow-Origin에 설정된 도메인이 Request Origin Header을 통해 전달되면 Response Access-Control-Allow-Origin Header로 다시 전달됩니다.

GET /accountDetails HTTP/1.1
Host: 9900c2000a.web-security-academy.net
Origin: https://9900c2000a.web-security-academy.net
Connection: close
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Sec-Fetch-Dest: empty
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Referer: https://9900c2000a.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: session=sbtUVKYJx1YFMT2KaPW5zqCu0yZG1Csw


HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://9900c2000a.web-security-academy.net
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-XSS-Protection: 0
Connection: close
Content-Length: 89

{
  "username": "",
  "email": "",
  "apikey": ""
}

만약 Access-Control-Allow-Origin 이 설정되지 않은 도메인이 Request Origin Header을 통해 전달되면 Response Access-Control-Allow-Origin Header가 전달되지 않습니다.

GET /accountDetails HTTP/1.1
Host: 9900c2000a.web-security-academy.net
Origin: https://test.com
Connection: close
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Sec-Fetch-Dest: empty
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Referer: https://9900c2000a.web-security-academy.net/my-account?id=wiener
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: session=sbtUVKYJx1YFMT2KaPW5zqCu0yZG1Csw


HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-XSS-Protection: 0
Connection: close
Content-Length: 89

{
  "username": "",
  "email": "",
  "apikey": ""
}

Preflight Request

CORS Request를 통한 Cross-Site Request는 사용자 데이터에 영향을 미칠 수 있기 때문에 Actual Request(실제 요청)이 안전한지 판단하기 위해서 Preflight Request(OPTIONS method)를 먼저 전달합니다.

Preflighted Request는 Actual Request(실제요청) 시 사용하려는 method, header를 전달하고, 서버는 Response로 허용 가능한 method, header를 전달해줍니다.

만약 Preflighted Request로 전달한 method, header가 Response로 받은 허용 가능한 method, header 목록에 포함되지 않는다면 브라우저는 405 Method Not Allowed를 발생시켜서 Actual Request를 하지 않도록 합니다.

CORS Preflight Request는 먼저 OPTIONS method를 통해 다음 3가지의 HTTP Header를 전달합니다.

  • Access-Control-Request-Method : 실제 요청하려는 메서드
  • Access-Control-Request-Headers : 실제 요청에 포함되어 있는 헤더 이름
  • Origin : 요청을 보내는 페이지의 출처

요청을 받은 서버는 Response로 다음 HTTP Header를 전달합니다.

  • Access-Control-Allow-Origin : 요청을 허용하는 출처
  • Access-Control-Allow-Methods : 요청을 허용하는 메서드, 해당 헤더가 없으면 GET과 POST 요청만 가능하다
  • Access-Control-Allow-Headers : 요청을 허용하는 헤더
  • Access-Control-Max-Age : Preflight Request 요청 결과를 저장할 시간을 지정, 해당 시간 동안 클라이언트는 Preflight Request를 하지않는다.

디음 예제에서는 OPTIONS method를 통해 서버가 POST method, X-PINGOTHER header, Content-Type header를 허용하는지 확인합니다.

Response로 받은 허용 가능한 method, header가 포함되어 있고, Access-Control-Allow-Origin 에도 요청하는 도메인 주소가 포함되어 있으므로 정상적으로 CORS Request를 할 수 있습니다.

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
    
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
    
POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache
    
<person><name>Arun</name></person>
    
    
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
    
[Some GZIP'd payload]

Credential Request

표준 CORS는 기본적으로 Request 시 cookie를 전송하지 않습니다.
CORS Request 시 cookie를 통해서 자격증명을 해야하는 경우, 또는 cookie를 Request에 포함하고 싶다면 XMLHttpRequest 객체의 withCredentials 속성 값을 true로 설정해야합니다.

서버는 Access-Control-Allow-Credentials: true 와 Set-Cookie Header에 다음 Request 시 전달해야하는 cookie 값을 Response에 포함시켜 전달합니다.

다음 예제는 Credential Request입니다.
Response 값에 Access-Control-Allow-Credentials: true, Set-Cookie: pageAccess=1을 포함하여 전달합니다.

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: aruner.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Origin: http://arunranga.com
Referer: http://arunranga.com/examples/access-control
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Connection: close

HTTP/1.1 200 OK
Date: Thu, 09 Apr 2020 05:40:53 GMT
Server: Apache
Access-Control-Allow-Origin: http://arunranga.com
Cache-Control: no-cache
Pragma: no-cache
Access-Control-Allow-Credentials: true
Set-Cookie: pageAccess=1; expires=Sat, Max-Age=2592000
Upgrade: h2
Connection: Upgrade, close
Cache-Control: max-age=172800
Expires: Sat, 11 Apr 2020 05:40:53 GMT
Vary: Accept-Encoding,User-Agent
Content-Length: 80
Content-Type: text/plain;charset=UTF-8

I do not know you or anyone like you

Request 시 Cookie: pageAccess=1 을 포함하여 전달합니다.
서버는 Response로 다음 Request 시 전달해야하는 Set-Cookie: pageAccess=2 값을 포함하여 전달합니다.

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: aruner.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Origin: http://arunranga.com
Referer: http://arunranga.com/examples/access-control
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6
Cookie: pageAccess=1
Connection: close

HTTP/1.1 200 OK
Date: Thu, 09 Apr 2020 05:41:38 GMT
Server: Apache
Access-Control-Allow-Origin: http://arunranga.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=2; expires=Sat, Max-Age=2592000
Upgrade: h2
Connection: Upgrade, close
Cache-Control: max-age=172800
Expires: Sat, 11 Apr 2020 05:41:38 GMT
Vary: Accept-Encoding,User-Agent
Content-Length: 104
Content-Type: text/plain;charset=UTF-8

You have been to aruner.net at least 1 time(s) before!

Non-Credential Request

기본적으로 withCredentials 속성 값은 false 입니다. Credential Rquest와 같이 별도로 지정을 하지 않는 모든 요청은 Non-Credential Request 입니다.

따라서 Request 시 cookie를 전달하지 않습니다.

참고

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://java119.tistory.com/67
https://brunch.co.kr/@adrenalinee31/1
https://www.popit.kr/cors-preflight-인증-처리-관련-삽질
http://wiki.gurubee.net/display/SWDEV/CORS+(Cross-Origin+Resource+Sharing)
http://arunranga.com/examples/access-control/

profile
Security

0개의 댓글