CORS
출처
- 출처(origin)는 접근할 때 사용하는 URL의 스킴(프로토콜, 호스트(도메인), 포트로 정의
- 두 객체의 스킴, 호스트, 포트가 모두 일치하는 경우 같은 출처를 가졌다고 한다.
동일 출처 예시
스킴과 호스트 일치
http://example.com/app1/index.html
http://example.com/app2/index.html
HTTP의 기본 포트는 80
http://Example.com:80
http://example.com
경로만 불일치
http://store.company.com/dir/page.html
http://store.company.com/dir2/other.html
http://store.company.com/dir/inner/another.html
다른 출처 예시
스킴 불일치
http://example.com/app1
https://example.com/app2
호스트 불일치
http://example.com
http://www.example.com
http://myapp.example.com
포트 불일치
http://example.com
http://example.com:8080
SOP
- 동일 출처 정책(Same-Origin Policy, SOP)
- 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식
- 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄일 수 있다.
CSRF
- 사이트 간 요청 위조(Cross-Site Request Forgery, CSRF 또는 XSRF)
- SOP가 등장하기 전에는 악성 웹사이트에서 사용자의 브라우저에 저장된 쿠키를 이용해 공격자가 원하는 요청을 보낼 수 있었다.
- 이를 막기 위해 SOP를 따르는 브라우저는 다른 웹사이트로 요청을 보내는 경우를 감지해 요청을 막았다.
- 이미지나
script
태그, CSS 등은 막지 않기에 추가 보안을 위하여 CSRF 토큰이나 transaction tickets을 이용한다.
- 이미지나
CORS
- 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)
- 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
기능적 개요
- 웹 브라우저에서 해당 정보를 읽는 것이 허용된 출처를 서버에서 설명할 수 있는 새로운 HTTP 헤더를 추가
- HTTP 헤더: 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송
- 대소문자를 구분하지 않는 이름과 콜론
:
다음에 오는 값으로 구성
- 대소문자를 구분하지 않는 이름과 콜론
- HTTP 헤더: 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송
- HTTP 요청 메서드에 대해, 브라우저가 요청을 OPTIONS 메서드로 프리플라이트하여 지원하는 메서드를 요청하고, 서버의 허가가 떨어지면 실제 요청을 보내도록 요구
- HTTP 요청 메서드: GET을 제외한 HTTP 메서드
- 프리플라이트: preflight, 사전 전달 또는 사전 요청
단순 요청
- 가장 간단한 접근 제어 프로토콜은 Origin 헤더와 Access-Control-Allow-Origin을 사용하는 것이다.
https://foo.example
의 웹 컨텐츠가 https://bar.other
도메인의 컨텐츠를 호출하는 상황을 다음 코드로 가정하자.
1
2
3
4
5
6
const xhr = new XMLHttpRequest();
const url = 'https://bar.other/resources/public-data/';
xhr.open('GET', url);
xhr.onreadystatechange = someHandler;
xhr.send();
서버로 보내는 요청은 다음과 같다.
1
2
3
4
5
6
7
8
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.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
Origin: https://foo.example
서버에서 받아온 응답은 다음과 같다.
1
2
3
4
5
6
7
8
9
10
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: *
: 모든 도메인에서 접근할 수 있음을 의미- 만약 응답이
Access-Control-Allow-Origin: https://foo.example
처럼 특정 도메인을 지정했다면 이외의 도메인은 cross-site 방식으로 리소스에 접근할 수 없다.
프리플라이트 요청
- 서버 데이터에 영향을 미칠 수 있는 DELETE나 PUT 요청 또는 커스텀 헤더와 함께 보내지는 GET, HEAD, POST 요청은 프리플라이트 요청을 보낸다.
- 안전하지 않은 요청의 조건
- 프리플라이트 요청은 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보낸 뒤, 실제 요청이 전송하기에 안전한지 먼저 확인한다.
서버에 요청을 보내기 위해 다음과 같은 코드를 가정하자.
1
2
3
4
5
6
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('Ping-Other', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
위 코드는 서버와의 첫 번째 통신으로 다음과 같은 preflight request 요청을 보낸다.
1
2
3
4
5
6
7
8
9
10
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.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
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
요청에는 Origin, Access-Control-Request-Method, Access-Control-Request-Headers 헤더가 포함된다.
- Access-Control-Request-Method: 실제 요청을 전송할 때 POST 메서드로 전송된다는 것을 알림
- Access-Control-Request-Headers: 실제 요청을 전송 할 때 X-PINGOTHER 와 Content-Type 사용자 정의 헤더와 함께 전송된다는 것을 알림
- 콤마로 구분한다.
서버는 다음과 같은 preflight response를 보낸다.
1
2
3
4
5
6
7
8
9
10
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
응답에는 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers가 포함된다.
- Access-Control-Allow-Methods: 허용 가능한 메서드 목록
- Access-Control-Allow-Headers: 허용 가능한 헤더 목록
실제 요청의 정보와 응답 헤더의 정보가 일치하지 않으면 브라우저는 실제 요청을 보내지 않는다.
요약
- CORS는 다른 출처의 자원에 접근할 수 있도록 권한을 부여하도록 브라우저에 알려주는 체제이다.
- 서버에 요청을 보낼때 헤더에는 Origin이 담기며 응답에는 접근 가능한 출처를 Access-Control-Allow-Origin 헤더에 담는다.
- 서버 데이터에 영향을 미칠 수 있는 등 안전하지 않은 요청을 보낼 때에는 프리플라이트 요청을 먼저 보내 허용 가능한 메서드와 헤더 정보 접근 가능한 출처와 함께 받아온다.