[웹] CORS

SOP

Same Origin Policy(동일 출처 정책)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리해, 공격받을 수 있는 경로를 줄입니다.

❓ 동일 출처(same origin)란 무엇일까 ?
👉 URL의 Protocol, Host, Port를 통해 같은 출처인지 다른 출처인지 판단할 수 있다.

http://localhost 와 동일 출처인 것은 ?
1. https://localhost
2. http://localhost:80
3. http://127.0.0.1
4. http://localhost/api/cors
👉 2번, 4번
1번 : 프로토콜이 다름
2번 : http의 기본포트는 80
3번 : string value가 다르기 때문에 브라우저가 다른 출처로 인식
4번 : /api/cors는 location이므로 동일출처

✔ http의 기본 포트 : 80 , https의 기본 포트 : 443

❓ 다른 출처의 리소스가 필요할때는 어떻게 해야할까 ?
👉 다른 출처의 자원과의 상호 작용을 위해 SOP 예외 조건으로 CORS 정책이 생겼다. CORS 정책을 위반하지 않는 다면 다른 출처의 리소스를 공유할 수 있다.

CORS

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.

❓ 어떻게 CORS 정책을 위반했는지 판단할까?
👉 출처를 비교하는 로직은 서버가 아닌 브라우저에 구현되어 있다.
CORS 정책에 위반하는 리소스 요청을 하면 서버가 같은 출처만 받겠다는 로직이 있는 경우가 아니면 서버는 정상적인 응답을 하고,
이후 브라우저가 응답 헤더를 분석하여 CORS 정책 위반이라고 판단되면 그 응답을 사용하지 않는다.

Request header / Response header

Request header (클라이언트의 요청 헤더)

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

Response header (서버에서의 응답 헤더)

  • Access-Control-Allow-Origin
    요청을 허용하는 출처
  • Access-Control-Allow-Credentials
    클라이언트 요청이 쿠키를 통해 자격증명을 해야 하는 경우 true
  • Access-Control-Allow-Methods
    요청을 허용하는 메서드, 기본값은 GET,POST. 이 헤더가 없으면 GET과 POST 요청만 가능
  • Access-Control-Allow-Headers
    요청을 허용하는 헤더
  • Access-Control-Max-Age
    클라이언트에서 preflight의 요청 결과를 저장할 기간을 지정. 해당 시간동안에는 preflight 요청을 다시 하지 않는다.

CORS 접근제어 시나리오

Preflight Request


preflight는 브라우저가 본 요청을 보내기 전에 보내는 예비 요청을 말하며 HTTP METHOD 중 OPTIONS를 사용한다.
예비 요청은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 확인하는 과정이다.

Simple Request


단순 요청은 서버에 API를 요청하고, 서버는 Access-Control-Allow-Origin 헤더를 포함한 응답을 브라우저에 보낸다. 브라우저는 Access-Control-Allow-Origin 헤더를 확인해서 CORS 동작을 수행할지 판단한다.
예비 요청 없이 바로 본 요청을 보내는 경우로 발생 조건이 까다로워 해당 요청이 발생하는 경우는 드물다.

❓ simple request를 하면 한번 요청하고 끝내는데 왜 Preflight request가 필요한 것일까?
👉 CORS를 모르는 서버를 위해 필요하다.

🤷‍♀️ 예시

👎 CORS설정이 안되어있는 서버와 simple request를 하는 경우
CORS 설정이 없기 때문에 allow-origin이 없음 -> 브라우저는 확인 후 client에게 CORS 에러 발생 -> 만약 클라이언트가 서버에 get같은 요청이 아닌 delete와 같은 요청이었을 경우 서버입장에서 데이터(DB)가 바뀔 수 있음

👍 CORS설정이 안되어있는 서버와 preflight request를 하는 경우
서버에는 allow-origin이 없기 때문에 브라우저는 client에게 CORS 에러를 발생시킴 -> 이것은 사전요청이기 때문에 서버는 어떠한 행동도 따로하지 않는다. -> client는 CORS에러를 받았기 때문에 다음 실제요청을 보내지 않는다. -> 서버는 안전하게 지켜진다.

Credential Request

인증 관련 헤더를 포함할 때 사용하는 요청이다.

클라이언트 측 ➡ credentials : include

fetch(url, {
  credentials: 'include', // 요청에 인증과 관련된 정보를 포함하겠다.
});

❗ 단, credentials: include를 사용하면 브라우저는 Access-Control-Allow-Origin: * 처럼 와일드 문자를 허용하지 않게 된다.

서버 측 ➡ Access-Control-Allow-Credentials: true
❗ Access-Control-Allow-Origin에는 *를 사용할 수 없으며, 명시적인 URL이어야한다.

좋은 웹페이지 즐겨찾기