CORS

남자김용준·2021년 9월 1일
0

ajax 요청을 보내게 될 사이트가 현재 접속 사이트와 다르면 요청이 실패할 수 있다. 도메인이나 서브도메인, 프로토콜, 포트가 다른 곳에 요청을 보내는 것을 Cross-Origin-Request라고 한다. 크로스 오리진 요청을 보내려면 리모트 오리진에서 전송받은 특별한 헤더가 필요하다. 이런 정책을 CORS(Cross-Origin-Resource Sharing)이라고 한다.

CORS는 해커의 공격으로부터 보호하기 위해 만들어진 정책이다. 해커가 만든 웹 사이트에서 gmail.com에 있는 메일 박스에 접근할 수 없게 하는 것이다. 이런 제약 덕분에 안전하게 사용할 수 있었지만, 크로스 오리진 요청을 보낼 일들이 많아졌다.

안전한 요청

크로스 오리진 요청은 크게 두 가지 종류로 구분된다.
1. 안전한 요청
2. 그 외의 요청

안전한 요청은 아래의 두 조건을 모두 만족시키는 요청이다.
1. 안전한 메서드 - GET이나 POST,HEAD를 사용한 요청
2. 안전한 헤더
Accept
Accept-Language
Content-Language
값이 applicatoin/x-www-form-urlencoded 이나 multipart/form-data, text/plain인 Content-Type

두 조건을 모두 만족하지 않는 요청은 안전하지 않은 요청으로 취급된다. PUT 메서드를 사용하거나 헤더에 API-Key 등의 안전한 헤더가 아닌 값들이 명시된 요청의 경우는 안전하지 않은 요청이다.

크로스 오리진 요청을 보낼 경우, 브라우저는 항상 Origin이라는 헤더를 request에 추가한다.
https://test.com/page 에서 https://test2.com/request에 요청을 보낸다고 했을 때 헤더는 아래와 같다.

GET /request
Host: test2.com
Origin: https://test.com
...

Origin 헤더엔 요청이 이뤄지는 페이지 경로(test.com/page)가 아니라 오리진 정보(도메인, 프로토콜, 포트)가 담기게 된다. 서버는 요청 헤더에 있는 Origin을 검사하고 요청을 받아들이기로 동의한 상태면 특별한 헤더인 Access-Control-Allow-Origin 에 오리진 정보나 * 이 들어있으면 응답은 성공하고 그렇지 않으면 응답은 실패한다.

이 과정에서 브라우저는 중재인의 역할을 한다.
1. 브라우저는 크로스 오리진 요청 시 Origin에 값이 제대로 설정, 전송되었는 지 확인한다.
2. 브라우저는 서버로부터 받은 응답에 Access-Control-Allow-Origin이 있는 지를 확인해서 서버가 크로스 오리진 요청을 허용하는 지 아닌지를 확인한다. 응답 헤더에 Access-Control-Allow-Origin이 있다면 자바스크립트를 사용해 응답에 접근할 수 잇고 아니라면 에러가 발생한다.

응답 헤더

크로스 오리진 요청이 이뤄진 경우, 자바스크립트는 기본적으로 '안전한' 응답 헤더로 분류되는 헤더에만 접속할 수 있다. '안전한' 응답 헤더는 아래와 같다.

  1. Cache-Control
  2. Content-Language
  3. Content-Type
  4. Expires
  5. Last-Modified
  6. Pragma

이 외의 응답 헤더에 접근하면 에러가 발생한다.

자바스크립트를 사용해서 안전하지 않은 응답 헤더에 접근하려면 서버에서 Access-Control-Expose-Headers 라는 헤더를 보내줘야 한다. Access-Control-Expose-Headers엔 자바스크립트 접근을 허용하는 안전하지 않은 헤더 목록이 담겨있다.

Access-Control-Expose-Headers: Content-Length,API-Key

이렇게 Access-Control-Expose-Headers 헤더가 응답 헤더에 있으면 자바스크립트로 응답 헤더의 Content-Length, API-Key를 읽을 수 있다.

안전하지 않은 요청

과거엔 웹페이지에서 GET,POST 이외의 HTTP 메서드를 사용해 요청을 보내는 경우를 생각하지 않았다. 이런 서버들은 GET,POST 이외의 메서드를 사용한 요청이 오면 브라우저에서 보낸 요청이 아니라고 판단하고 접근 권한을 확인한다. 이런 상황을 피하고자 브라우저는 '안전하지 않은 요청'이 이뤄지는 경우, 서버에 바로 요청을 보내지 않고 preflight 요청이라는 사전 요청을 서버에 보내 권한이 있는 지를 확인한다.

preflight 요청은 options 메서드를 사용하고 두 헤더가 함께 들어가며, 본문은 비어있다.
Access-Control-Request-Method : 안전하지 않은 요청에서 사용하는 메서드 정보가 담겨있다.
Access-Control-Request-Headers: 안전하지 않은 요청에서 사용하는 헤더 목록이 담겨있다.

안전하지 않은 요청을 허용하기로 협의하였다면 서버는 본문이 비어있고 상태 코드가 200인 응답을 다음과 같은 헤더와 함께 브라우저로 보낸다.
Access-Control-Allow-Origin : * 나 요청을 보낸 오리진이어야 한다.
Access-Control-Allow-Methods : 허용된 메서드 정보가 담겨있다.
Access-Control-Allow-Headers : 허용된 헤더 목록이 담겨있다.
Access-Control-Max-Age : 퍼미션 체크 여부를 몇 초간 캐싱해 놓을지를 명시한다. 캐싱되어 있을 경우 preflight 요청을 생략하고 요청을 보낼 수 있다.

자격 증명

자바스크립트로 크로스 오리진 요청을 보내는 경우, 기본적으로 쿠키나 http 인증같은 자격 증명이 함께 전송되지 않는다. 이럴 땐 credentials: "include" 옵션을 추가하면 자격 증명 정보들도 함께 전송된다. 자격 증명이 함께 전송되는 요청을 보낼 땐 Access-Control-Allow-Origin에 * 을 사용할 수 없다. 정확한 오리진 정보만 명시되어야 서버가 신뢰할 수 있기 때문이다.

출처: javascript blog

profile
frontend-react

0개의 댓글