CORS란

웅평·2023년 4월 6일
0

네트워크

목록 보기
18/21

CORS란?

교차 출처 리소스 공유(Cross-Origin Resource , CORS)는 추가 HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처(프로토콜, 도메인, 포트번호)의 리소스에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. - mdn 설명

출처중 하나라도 다를 경우에 출처가 다른 교차 출처(Cross-Origin)라고 판단되며 브라우저에서는 보안 때문에 Cross-Origin HTTP 요청을 제한한다. 권한을 부여 받기 위한 Cross-Origin 요청은 서버의 동의가 필요한는데 HTTP-header를 통해서 받을 수 있다.

출처(프로토콜, 도메인, 포트번호) 세가지만 동일한지 확인하면 되는데 모든 출처가 일치한다면 Same Origin, 하나라도 일치하지 않는다면 Cross Origin이 된다

프로토콜 - http와 https는 프로토콜이 다르다.
도메인 - domain.com과 other-domain.com은 다르다.
포트 번호 - 8080포트와 3000포트는 다르다.

출처란?

출처(Origin) 라는 것은 Protolcol 과 Host 그리고 Port 까지 모두 합친 URL을 의미한다고 보면 된다.

Protocol(Scheme) : http, https
Host : 사이트 도메인
Port : 포트 번호
Path : 사이트 내부 경로
Query string : 요청의 key와 value값
Fragment : 해시 태크

  • Origin : Protocol + Host + Port

CORS는 왜 필요한가?

CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 되면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 된다. 예를 들어서 기존 사이트와 완전히 동일하게 동작하도록 하여 사용자가 로그인을 하도록 만들고, 로그인했던 세션을 탈취하여 악의적으로 정보를 추출하거나 다른 사람의 정보를 입력하는 등 공격을 할 수 있습니다. 이렇나 공격을 할 수 없도록 브라우저에서 보호하고, 필요한 경우 에만 서버와 협의하여 요청할 수 있도록 하기 위해서 필요하다.

동일 출처 정책 (Same-Origin Policy)

SOP(Same Origin Policy) 정책은 동일한 출처에서만 리소스를 공유할 수 있다.
동일 출처(Same-Origin) 서버에 있는 리소스는 자유로이 가져올수 있지만, 다른 출처(Cross-Origin) 서버에 있는 이미지나 유튜브 영상 같은 리소스는 상호작용이 불가능하다는 말이다.

동일 출처와 다른 출처 예시

https://www.domain.com:3000 출처에 대한 여러 URL에 따른 동일 출처 비교 표

URL출처 구분이유
https://www.domain.com:3000/about동일 출처프로토콜, 호스트, 포트 번호 동일
https://www.domain.com:3000/about?username=inpa동일 출처프로토콜, 호스트, 포트 번호 동일
http://www.domain.com:3000다른 출처프로토콜 다름 (http ≠ https)
https://www.another.co.kr:3000다른 출처호스트 다름
https://www.domain.com:8888다른 출처포트 번호 다름
https://www.domain.com다른 출처포트 번호 다름 (443 ≠ 3000)

CORS 동작 원리


기본 동작

  1. 클라이언트에서 HTTP요청의 헤더에 Origin을 담아 전달
  • HTTP 프로토콜을 이용하여 서버에 요청을 보내게 되는데, 이때 브라우저는 요청 헤더에 Origin 이라는 필드에 출처를 함께 담아 보내게 된다.
  1. 서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다
    • 이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더에 Access-Control-Allow-Origin이라는 필드를 추가하고 값으로 '이 리소스를 접근하는 것이 허용된 출처 url'을 내려보낸다.

  1. 클라이언트에서 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.
    • 이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 차단할지 말지를 결정한다
    • 만약 유효하지 않다면 그 응답을 사용하지 않고 버린다. (CORS 에러 !!)
    • 유효하다면 다른 출처의 리소스를 문제없이 가져오게 된다.

CORS 동작 3가지

1. 예비 요청 (Preflight Request)

  • 브라우저는 요청을 보낼때 한번에 바로 보내지않고, 먼저 예비 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보낸다.

    예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다

  • 이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며, 이 예비요청의 HTTP 메소드를 GET이나 POST가 아닌 OPTIONS라는 요청이 사용된다는 것이 특징이다.

    이때 내용물은 없이 헤더만 전송한다.

정리

Simple requests가 아닌 cross-origin요청은 모두 preflight 요청을 하게 되는데, 실제 요청을 보내는 것이 안전한지 확인하기 위해 먼저 OPTIONS 메서드를 사용하여 cross-origin HTTP 요청을 보냅니다. 이렇게 하는 이유는 사용자 데이터에 영향을 미칠 수 있는 요청이므로 사전에 확인 후 본 요청을 보낸다.

  1. 자바스크립트의 fetch() 메서드를 통해 리소스를 받아오려고 한다.
  2. 브라우저는 서버로 HTTP OPTIONS 메소드로 예비 요청(Preflight)을 먼저 보낸다.
    1. Origin 헤더에 자신의 출처를 넣는다.
    2. Access-Control-Request-Method 헤더에 실제 요청에 사용할 메소드를 설정한다.
    3. Access-Control-Request-Headers 헤더에 실제 요청에 사용할 헤더들을 설정한다.
  3. 서버는 이 예비 요청에 대한 응답으로 어떤 것을 허용하고 어떤것을 금지하고 있는지에 대한 헤더 정보를 담아서 브라우저로 보내준다.
    1. Access-Control-Allow-Origin 헤더에 허용되는 Origin들의 목록을 설정한다.
    2. Access-Control-Allow-Methods 헤더에 허용되는 메소드들의 목록을 설정한다.
    3. Access-Control-Allow-Headers 헤더에 허용되는 헤더들의 목록을 설정한다.
    4. Access-Control-Max-Age 헤더에 해당 예비 요청이 브라우저에 캐시 될 수 있는 시간을 초 단위로 설정한다.
  4. 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여, 해당 요청이 안전한지 확인하고 본 요청을 보내게 된다.
  5. 서버가 본 요청에 대한 응답을 하면 최종적으로 이 응답 데이터를 자바스립트로 넘겨준다.

요청 헤더 목록

  • Origin
  • Access-Control-Request-Method
    • preflight 요청을 할 때 실제 요청에서 어떤 메서드를 사용할 것인지 서버에게 알리기 위해 사용된다.
  • Access-Control-Request-Headers
    • preflight요청을 할 때 실제 요청에서 어떤 header를 사용할 것인지 서버에게 알리기 위해 사용된다.

응답 헤더 목록

  • Access-Control-Allow-Origin
    • 브라우저가 해당 origin이 자원에 접근할 수 있도록 허용한다. 혹은 *은 credentials이 없는 요청에 한해서 모든 origin에서 접근이 가능하도록 허용한다.
  • Access-Control-Expose-Headers
    • 브라우저가 액세스할 수 있는 서버 화이트리스트 헤더를 허용한다.
  • Access-Control-Max-Age
    • 얼마나 오랫동안 preflight요청이 캐싱 될 수 있는지를 나타낸다.
  • Access-Control-Allow-Credentials
    • Credentials가 true 일 때 요청에 대한 응답이 노출될 수 있는지를 나타낸다.
    • preflight요청에 대한 응답의 일부로 사용되는 경우 실제 자격 증명을 사용하여 실제 요청을 수행할 수 있는지를 나타낸다.
    • 간단한 GET 요청은 preflight되지 않으므로 자격 증명이 있는 리소스를 요청하면 헤더가 리소스와 함께 반환되지 않으면 브라우저에서 응답을 무시하고 웹 콘텐츠로 반환하지 않는다.
  • Access-Control-Allow-Methods
    • preflight요청에 대한 대한 응답으로 허용되는 메서드들을 나타낸다.
  • Access-Control-Allow-Headers
    • preflight요청에 대한 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP 헤더를 나타냅니다.

예비 요청의 문제점과 캐싱

OPTIONS 메소드로 예비요청을 보내 보안을 강화하는것은 좋지만 실제 요청에 걸리는 시간이 늘어나게 되어 어플리케이션 성능에 영향을 미치는 단점이 있다.

브라우저 캐시(Cache) 를 이용해 Access-Control-Max-Age 헤더에 캐시될 시간을 명시해 주면, 이 Preflight 요청을 캐싱 시켜 최적화를 시켜줄 수 있다.

예비 요청 캐시는 다른 캐싱 매커니즘과 유사하게 작동한다

  • 브라우저는 예비(Preflight) 요청을 할 때마다, 먼저 Preflight 캐시를 확인하여 해당 요청에 대한 응답이 있는지 확인한다.
  • 만일 응답이 캐싱 되어 있지 않다면, 서버에 예비 요청을 보내 인증 절차를 밟는다.
  • 만일 서버로 부터 Access-Control-Max-Age 응답 헤더를 받는다면 그 기간 동안 브라우저 캐시에 결과를 저장한다.
  • 다시 요청을 보내고 만일 응답이 캐싱 되어 있다면, 예비 요청을 서버로 보내지 않고 대신 캐시된 응답을 사용한다.

2. 단순 요청 (Simple Request)

  • 예비 요청(Prefilght)을 생략하고 바로 서버에 직행으로 본 요청을 보낸 후, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin 헤더를 보내주면 브라우저가 CORS정책 위반 여부를 검사하는 방식

3가지 경우를 만족 할때 만 예비 요청을 생략

  • HTTP method가 GET, HEAD, POST 중 하나이어야 한다.
  • 자동으로 설정되는 헤더는 제외하고, 설정할 수 있는 다음 헤더들만 변경하면서
    • Accept
    • Accept-Language
    • Content-Language
  • Content-Type헤더가 다음과 같을 경우
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

까다로운 조건들이 많기 때문에 대부분의 API 요청은 그냥 예비 요청(preflight)으로 이루어진다 라고 이해하면 된다.

3. 인증된 요청 (Credentialed Request)

인증된 요청은 클라이언트에서 서버에게 자격 인증 정보(Credential)를 실어 요청할때 사용되는 요청이다.

자격 인증 정보란
세션 ID가 저장되어있는 쿠키(Cookie) 혹은 Authorization 헤더에 설정하는 토큰 값 등을 일컫는다.

1. 클라이언트에서 인증 정보를 보내도록 설정하기
- 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credentials 옵션이다. 이 옵션에는 3가지의 값을 사용할 수 있으며, 각 값들이 가지는 의미는 아래와 같다.

옵션 값설명
same-origin(기본값)같은 출처 간 요청에만 인증 정보를 담을 수 있다.
include모든 요청에 인증 정보를 담을 수 있다.
omit모든 요청에 인증 정보를 담지 않는다.
- 만일 이러한 별도의 설정을 해주지 않으면 쿠키 등의 인증 정보는 절대로 자동으로 서버에게 전송되지 않는다.

2. 서버에서 인증된 요청에 대한 헤더 설정하기
서버도 마찬가지로 이러한 인증된 요청에 대해 일반적인 CORS 요청과는 다르게 대응해줘야 한다.

  • 응답 헤더의 Access-Control-Allow-Credentials 항목을 true로 설정해야 한다.
  • 응답 헤더의 Access-Control-Allow-Origin 의 값에 와일드카드 문자("*")는 사용할 수 없다.
  • 응답 헤더의 Access-Control-Allow-Methods 의 값에 와일드카드 문자("*")는 사용할 수 없다.
  • 응답 헤더의 Access-Control-Allow-Headers 의 값에 와일드카드 문자("*")는 사용할 수 없다

응답의 Access-Control-Allow-Origin 헤더가 와일드카드(*)가 아닌 분명한 Origin으로 설정되어야 하고, Access-Control-Allow-Credentials 헤더는 true로 설정되어야 한다는 뜻이다. 그렇지 않으면 브라우저의 CORS 정책에 의해 응답이 거부된다. (인증 정보는 민감한 정보이기 때문에 출처를 정확하게 설정해주어야 한다)

- preflight요청에 대한 대한 응답으로 실제 요청 시 사용할 수 있는 HTTP 헤더를 나타낸다.

참고
https://hannut91.github.io/blogs/infra/cors
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F
https://hymndev.tistory.com/78

0개의 댓글