CORS

Jeff·2021년 11월 29일
0
post-thumbnail

CORS 정리

배경

Origin

protocol + host + port 가 동일한 url의 경우 같은 origin(출처)라고 합니다

[다른 출처 #1, 프로토콜이 다름]
https://naver.com
http://naver.com

[다른 출처 #2, 호스트가 다름(실제로는 동일하지만 브라우저에서 string 비교할 때 다르게 인식)]
http://localhost:8081
http://127.0.0.1:8081

[다른 출처 #3, port가 다름]
localhost:8080
localhost:8081


[같은 출처, path만 다름]
http://naver.com
http://naver.com/login

SOP (Same Origin Policy)

동일 출처의 리소스로만 상호작용을 제한하는 브라우저의 보안 정책입니다.
악의적일 수 있는 문서를 분리함으로 공격 경로를 줄여줍니다.

example
1. 페이스북에 로그인 후 인증토큰을 받아옵니다
2. 메일에서 악의적인 페이지(hack.com)에 접속하게 되었습니다
3. 악의적인 페이지에서 인증토큰을 이용해 페이스북에 포스트를 올리는 요청을 보냅니다
4. 이때 SOP 정책이 없는 경우 이슈가 발생합니다

IE의 경우는 SOP에 몇가지 예외사항이 있습니다.
1. 양쪽 도메인 모두 신뢰할 수 있는 사이트(높은 단계의 보안 수준)인 경우 SOP를 적용하지 않습니다
2. SOP 검사에 포트를 포함하지 않습니다

CORS (Cross Origin Resource Sharing)

페이지를 만들다 보면 다른 출처의 자원이 필요한 경우가 생깁니다. 하지만 브라우저에서는 SOP로 인해 다른 출처의 자원과 상호작용이 제한됩니다.
CORS는 HTTP 헤더를 사용해 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 mechanism 입니다.

서버 데이터에 부수 효과(side effect)를 일으킬 수 있는 HTTP 요청 메서드(GET을 제외한 HTTP 메서드)에 대해, CORS 명세는 브라우저가 요청을 OPTIONS 메서드로 "프리플라이트"(preflight, 사전 전달)하여 지원하는 메서드를 요청하고, 서버의 "허가"가 떨어지면 실제 요청을 보내도록 요구하고 있습니다.

접근 제어 시나리오 예제

단순 요청 (simple request)

simple request의 경우는 CORS preflight를 트리거하지 않습니다(ex. 별도의 side effect가 없는 get 요청).
simple request는 다음 조건을 모두 충족해야 합니다

  • Method
    • GET, POST, HEAD 중 하나
  • 아래의 Header만 설정됨
    • user agent가 자동으로 설정한 헤더(Connection, User-Agent, ...)
    • Accept (클라가 이해가능한 컨텐츠 타입 나열)
    • Accept-Language (클라가 이해, 지역 설정 중 선호되는 언어)
    • Content-Language (청중을 위한 언어)
    • Content-Type
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • 실제 서비스에서 위 조건을 만족하기는 어렵습니다 (ex. Authorization 헤더 등 사용)

Simple Request 헤더

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
  • CORS 요청을 보내는 경우 브라우저는 항상 Origin 헤더를 request에 추가합니다
  • Origin 헤더엔 요청이 이뤄지는 페이지 경로(/page)가 아닌 오리진(도메인·프로토콜·포트) 정보가 담기게 됩니다.

Simple Response 헤더

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…]
  • 서버는 요청 헤더에 있는 Origin를 검사하고, 요청을 받아들이기로 동의한 상태라면 특별한 헤더 Access-Control-Allow-Origin를 응답에 추가합니다
  • 크로스 오리진 요청이 이뤄진 경우, 자바스크립트는 기본적으로 ‘안전한’ 응답 헤더로 분류되는 헤더에만 접속할 수 있습니다.
  • 안전한 응답 헤더 리스트
    • Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma
    • 이 외의 응답 헤더에 접근하면 에러가 발생합니다.
    • 자바스크립트를 사용해 안전하지 않은 응답 헤더에 접근하려면 서버에서 Access-Control-Expose-Headers라는 헤더를 보내줘야만 합니다

프리플라이트 요청 (preflight request)

CORS가 허용되는지 먼저 preflight 요청을 통해 체크합니다

  • CORS 요청을 보내는 경우 브라우저는 항상 Origin 헤더를 request에 추가합니다

인증정보를 포함한 요청 (credentialed requests)

자바스크립트로 크로스 오리진 요청을 보내는 경우, 기본적으로 쿠키나 HTTP 인증 같은 자격 증명(credential)이 함께 전송되지 않습니다.
서버에서 이를 허용하고 싶으면, 자격증명이 달린 헤더를 명시적으로 허용하겠다는 세팅이 서버에 필요합니다.
자격증명을 함께 전송하는 방법

  • fetch의 경우 credentials: include
  • axios의 경우 withCredentials: true,

자격 증명 정보가 담긴 요청을 서버에서 받아들이기로 동의했다면 서버는 응답에 Access-Control-Allow-Origin 헤더와 함께 Access-Control-Allow-Credentials: true 헤더를 추가해서 보냅니다.
자격 증명이 함께 전송되는 요청을 보낼 땐 Access-Control-Allow-Origin에 *을 쓸 수 없습니다.

CORS 해결 방법

프론트 프록시 서버 설정

서버 응답 시 직접 헤더에 설정

  • 서버분들에게 요청
  • springboot에서 @CrossOrigin 설정 등 ,,

참고

https://ko.javascript.info/fetch-crossorigin
https://ui.toast.com/weekly-pick/ko_2021110
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

profile
Fast is better than good

0개의 댓글