본문 바로가기

항해99_10기/105일의 TIL & WIL

[6주차] [20221219] Cross Origin Resource Sharing (CORS)

CORS란?

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

CORS는 HTTP-header 기반의 매커니즘으로, 현재 브라우저가 접속한 origin과 다른 origin (도메인이나 서브도메인, 프로토콜, 포트가 다른 곳)에 요청을 보내는 것
크로스 오리진 요청을 보내려면 리모트 오리진에서 전송받은 특별한 헤더가 필요하다.

CORS가 만들어진 배경

과거 수 년 동안, 한 사이트의 스크립트에서 다른 사이트에 있는 콘텐츠에 접근할 수 없다는 제약이 있었다.

 

당시 자바스크립트는 네트워크 요청을 보낼 수 있는 메서드를 지원하지 않았고, 단순히 웹페이지를 꾸미는 용도로 사용하는 토이 랭귀지 수준이었다.

 

그래서! 개발자들은 <form>과 <iframe>, <script> 태그의 src 등 다양한 트릭을 사용해 다른 remote 서버와 양방향 소통을 가능하게 해 네트워크 통신을 하였다고 한다. (이런 꼼수들은 양쪽의 동의만 있다면, 네트워크 보안 규칙을 어기지 않고 통신을 제공하므로, 해킹도 아니다.)

 

그러던 와중, 드디어 JS에도 네트워크 관련 메서드가 추가되었고, 이 후로도 긴 논의 끝에 cross-origin 요청을 가능하도록 추가 하였다. 대신, CORS 요청은 서버에서 명시적으로 이를 허가 했다는 것을 알려주는 특별한 헤더를 전송 받았을 때만 가능하도록 제약을 걸었다.

 

안전한 요청

  • 안전한 Method : GET, HEAD, POST
    • DELETE, PUT, PATCH 등의 HTTP 메서드는 과거 존재하지 않았고, 아주 오래된 브라우저에서는 지원하지 않는다. 
    • 특별한 방법을 사용하지 않고도 <form>이나 <script> 태그를 이용해 요청을 만들 수 있어야 한다. 
  • 안전한 Header 
    • Accept
    • Accept-Language
    • Content-Language
    • 값이 application/x-www-form-urlencoded이나 multipart/form-data, text/plain인 Content-Type

위의 두 조건을 모두 충족해야만 안전한 요청으로 취급한다. 

 

처음 CORS가 허용됐을 때는, 브라우저에서 안전한 요청만을 보낼 수 있었다. 하지만, 시간이 지나고 브라우저에서 DELETE 와 PUT 등 새로운 HTTP 메서드를 사용할 수 있게 되자, 브라우저에서는 안전하지 않은 요청을 보낼 수 있게 되었다. 그래서, 개발자들은 검증 장치를 하나 더 추가했다. 바로 브라우저에서 요청을 서버로 전송하기 전에 "preflight"요청을 먼저 전송해서 서버가 CORS 요청을 받을 수 있는지 확인하는 단계를 추가했다.

 

CORS를 이용한 요청과 응답

  • 스크립트 : CORS 요청
  • 브라우저 : 스크립트와 서버 사이에서 중재 역할 수행
    1. 스크립트 요청에 대하여 Origin 값이 잘 설정/전송되었는지 확인
    2. 서버 응답에 대하여 브라우저로 값을 넘겨주기 전, Access-Control-Allow-Origin이 있는지 확인
      • 있으면 JS로 응답값에 접근할 수 있도록 허용
      • 없으면 에러를 던짐
  • 서버 : request header에 있는 Origin 검사 -> 요청을 처리하기로 동의한 상태라면, Access-Control-Allow-Origin이라는 특별한 헤더를 응답에 포함
    • Access-Control-Allow-Origin에 오리진 정보나, *이 있으면, 응답 성공 / 없으면 실패
    • 서버에서 보내는 "안전한 응답" 헤더가 있으며, 이외의 헤더로 접근하면 에러가 발생
      • Cache-Control
      • Content-Language
      • Content-Type
      • Expires
      • Last-Modified
      • Pragma
// 스크립트에서 CORS 요청
GET /request
Host: anywhere.com
Origin: https://javascript.info
...

// 서버에서 CORS 요청을 허용한 경우, 브라우저 preflight 요청에 대한 응답은 아래와 같음
200 OK
Content-Type:text/html; charset=UTF-8
Access-Control-Allow-Origin: https://javascript.info

// 서버에서 보내는 안전한 응답
200 OK
Content-Type:text/html; charset=UTF-8
Content-Length: 12345
API-Key: 2c9de507f2c54aa1
Access-Control-Allow-Origin: https://javascript.info
Access-Control-Expose-Headers: Content-Length,API-Key

"안전하지 않은" CORS 요청을 보내는 방법

preflight를 통해 안전하지 않은 요청을 보내고 응답을 받을 수 있다.

 

0단계 - 안전하지 않은 요청

  • PATCH 메서드를 사용하고 있습니다.
  • Content-Type이 application/x-www-form-urlencoded나 multipart/form-data, text/plain가 아닙니다.
  • 비표준 헤더 API-Key를 사용합니다.
// 스크립트에서 안전하지 않은 요청을 보냄 (PATCH 메서드)
let response = await fetch('https://site.com/service.json', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'API-Key': 'secret'
  }
});

 

1단계 - preflight 요청

  • 메서드 – OPTIONS
  • 경로 – 본 요청과 동일한 경로(/service.json)
  • 크로스 오리진 특수 헤더:
    • Origin – 본 요청의 오리진
    • Access-Control-Request-Method – 본 요청에서 사용하는 메서드
    • Access-Control-Request-Headers – 본 요청에서 사용하는 안전하지 않은 헤더 목록(콤마로 구분)
OPTIONS /service.json
Host: site.com
Origin: https://javascript.info
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: Content-Type,API-Key

 

2단계 - preflight 응답

  • Access-Control-Allow-Methods: PATCH
  • Access-Control-Allow-Headers: Content-Type,API-Key
200 OK
Access-Control-Allow-Origin: https://javascript.info
Access-Control-Allow-Methods: PUT,PATCH,DELETE
Access-Control-Allow-Headers: API-Key,Content-Type,If-Modified-Since,Cache-Control
Access-Control-Max-Age: 86400

이렇게 응답을 받은 브라우저는 이제 실제 요청을 서버로 전송해 실제 요청에 대한 응답을 받아 다시 스크립트에서 자원에 대한 access가 가능하도록 한다.

 

3단계 - 실제 요청

  • 요청은 크로스 오리진 요청이기 때문에 Origin 헤더가 붙는다.
PATCH /service.json
Host: site.com
Content-Type: application/json
API-Key: secret
Origin: https://javascript.info

 

4단계 - 실제 응답

Access-Control-Allow-Origin: https://javascript.info

 

CORS

 

ko.javascript.info

 

express에서는 cors 미들웨어를 제공하고, npm i cors로 설치해서 사용한다.

 

Express cors middleware

cors CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options. Follow me (@troygoode) on Twitter! Installation This is a Node.js module available through the npm registry. Installation is don

expressjs.com