본문 바로가기

frond-end

CORS

 

위의 사진처럼 웹 개발을 하다보면 가끔 cors 정책 위반으로 인해 에러가 발생하는 경험이 있을거에요

cors error 가 프론트에서 발견하지만 보통 백엔드에서 해결해줘야 하는 에러라고 생각해서 그동안 잘모르고 지나갔었는데요

단순 서버에서의 에러인 줄 았았는데 브라우저의 에러라는 것을 알게 돼서 프론트엔드 개발자라면 cors 에 대해 개념정도는 알아야 하지 않나 싶어서 스터디 하게 되었습니다.

보통 cors 에러가 나타날때 " 다른 출처를 막는게 cors 구나" 라고 생각했었는데 알고보니 다른 출처 요청을 허용해주는게 cors 더라구요 .
한번 제대로 알아봅시다.

 

 

 

 

 

 

 

 

 

 

 

자바스크립트에서의 요청은 기본적으로 서로다른 도메인에 대한 요청을 보안상 제한해요.

여기서, 두가지 정책이 있는데 same origin 정책과 cross origin 정책이 있습니다.

이러한 정책들이 뭐길래 웹 브라우저가 외부 리소스를 가려서 받을까요?

 

 

 

웹 생태계에는 다른 출처로의 리소스 요청을 제한하는 것과 관련된 두 가지 정책이 존재한다.

SOP : Same-Origin Policy

CORS : Cross-Origin Resource Sharing

 

 

 

SOP 란?

“같은 출처 끼리만 요청 보낼 수 있다”

Same-Origin Policy = 동일 출처 정책

 

 

 

먼저 SOP 에 대해 설명을 해보자면,

“같은 출처 끼리만 요청 보낼 수 있는 보안 방식" 인데요

여기서 출처가 뭔가 라고 보면 https://google.com 과 같은 url 을 의미하는데

url 은 아래 사진과 같이 protocol, host, path, queary string, fragment 로 구성되어 있어요

출처는 protocol 과 host, path 를 합쳐놓은 것이라고 합니다. 이 3개를 합쳐놓은 걸로 같은 출처인지 다른 출처인지 판단할 수 있는 거라고 보면 됩니다.

Https 가 protocol 이 되고, google.com 도메인이 host 되고, 뒤에 8000 같이 붙는게 포트번호가 됩니다

인터넷익스플로러 같은 경우는 뒤에 포트가 출처를 판단을 안해요 그래서 보안에 취약하다고 하네요 

 

 

📌 출처(Origin) 란?

서버의 위치를 위미하는 https://google.com 과 같은 URL

 

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

출처(Origin) =  Protocol +  Host +  Port  합친 것 의미

서버의 위치를 찾아가기 위해 필요한 가장 기본적인 것

출처 내의 포트 번호 생략 가능

  • HTTP, HTTPS 프로토콜의 기본 포트 번호가 정해져 있기 때문
console.log(location.origin);

브라우저 개발자 도구의 콘솔에서 Location 객체가 가지고 있는 origin 프로퍼티에 접근함으로써 어플리케이션이 실행되고 있는 출처 확인 가능

 

 

🪝  http://localhost과 같은 url 은?

  1. https://localhost
  2. http://localhost:80
  3. http://127.0.0.1
  4. http://localhost/api/cors
더보기

정답은 2번, 4번 입니다!

 

1번은 https 죠 보기와 프로토콜이 다릅니다

2번은 http 기본 port 가 80port 이기 때문에 보기에서는 생략된거고 둘은 동일 출처입니다

3번은 사실 127.0.0.1 의 ip는 localhost 가 맞긴 맞는데 브라우저 입장에서는 이거를 string value 로 비교를 한다고 해요

3번도 정답인 줄 알았는데 아니였네요 🙊

-> chat gpt의 답변입니다

 

4번은 api/cors 는 추가적으로 붙는 로케이션 이라서 api 앞에까지만 비교해서 이것은 동일 출처라고 볼 수 있습니다.

 

 

정리해서, sop 는 "다른 출처의 리소스를 사용하는 것에 제한 하는 보안 방식" 이라고 볼 수 있습니다.

그럼 왜 동일 출처가 아닌 경우 접근을 차단하는 이유는 뭘까요?

 

 

 

 

동일 출처가 아닌 경우 접근을 차단하는 이유는??

제약 없다면 -> CSRF(Cross-Site Request Forgery) , XSS(Cross-Site Scripting) 등의 방법으로 해킹  당함

출처 비교와 차단은 브라우저가 한다 !

출처를 비교하는 로직은 "브라우저에 구현된 스펙"

결국 CORS Error 는 브라우저의 SOP 정채에 따라 다른 출처의 리소스를 차단하면서 발생된 에러이고, CORS는 다른 출처의 리소스를 얻기위한 해결 방안 이었다.

=>SOP 정책을 위반해도 CORS 정책에 따르면 다른 출처의 리소스라도 혀용 한다는 뜻

 

 

출처가 다른 두 어플리케이션이 자유로이 소통할 수 있는 환경을 꽤 위험한 환경인데요
만약 제약이 없다면 해커가 CSRF 나 XSS 등으로 해커가 심어놓은 코드가 실행하게 해서 개인 정보를 가로챌 수 있습니다.
따라서, 이러한 악의적인 경우를 방지하기 위해, SOP 정책으로 동일하지 않은 다른 출처의 스크립트가 실행되지 않도록 브라우저에서 사전에 방지하는 것입니다.

! 그렇다면 다른 출처의 리소스가 필요하다면 어떻게 할까요? 바로 cors 로 해결합니다

 

 

 

 

 

cors란?

“다른 출처 라도 요청 보낼 수 있다”

Cross-Origin Resource Sharing

교차 출처 자원 공유

 

교차 출처라고 하면 잘 안와닿는데 한국어에 맞게 쉽게 말해서, "다른 출처"의 자원을 공유한다 라고 생각하면 됩니다
제가 이해한거는 cors 는 다른 출처에 접근을 한다는 것이고 이 권한은 브라우저에 알려주는 체제라고 이해했습니다.

cors 접근제어 시나리오는 3가지가 있는데
simple request / preflight request / credentialed request 가 있습니다

 

cors 접근제어 시나리오 (3)

  • simple request
  • preflight request
  • credentialed request

 

1️⃣[단순 요청 Simple request]

1. 클라이언트에서 HTTP 요청의 헤더에 Origin 을 담아 전달

기본적으로 웹은 HTTP 프로토콜을 이용하여 서버에 요청을 보내는데, 브라우저는 요청 헤더에 Origin 이라는 필드에 출처를 담아 보낸다.

 

2. 서버는 응답헤더에 Access-Control-Allow-Origin 을 담아 클라이언트로 전달

이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더에 Access-Control-Allow-Origin 이라는 필드를 추가하고 값으로 '이 리소스를 접근하는 것이 허용된 출처 url' 을 내려보낸다

 

3. 클라이언트에서 Origin 과 서버가 보내준 Access-Control-Allow-Origin 을 비교한다.

만약 유효하지 않으면 그 응답을 버린다 (CORS 에러)

결국 CORS 해결책은 서버의 허용이 필요

서버에서 Access-Control-Allow-Origin 헤더에 허용할 출처를 기재해서 클라이언트에 응답하면 됨 <- 백엔드 개발자가 고쳐야 함

 

 

[ Simple Request 조건] 

HTTP method 가 다음 중 하나이면서

  • GET
  • HEAD
  • POST

자동으로 설정되는 헤더는 제외하고, 설정할 수 있는 다음 헤더들만 변경하면서

  • Accept
  • Accept-Language
  • Content-Language

Content-Type 이 다음과 같은 경우

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Simple request 라고 부른다.

이 요청은 추가적으로 확인하지 않고 바로 본 요청을 보낸다.

 

 

 

 

 

2️⃣[✈️ 예비 요청 Preflight Request ]

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

예비 요청 = 본 요청 보내기 전 브라우저 스스로 안전한 요청인지 미리 checking

예비 요청의 HTTP 메소드를 GET 이나 POST 가 아닌 OPTIONS 라는 요청이 사용된다

 

 

Simple requests가 아닌 cross-origin요청은 모두 preflight 요청을 하게 되는데, 실제 요청을 보내는 것이 안전한지 확인하기 위해 먼저 OPTIONS 메서드를 사용하여 cross-origin HTTP 요청을 보냅니다.

이렇게 하는 이유는 사용자 데이터에 영향을 미칠 수 있는 요청이므로 사전에 확인 후 본 요청을 보냅니다.

 

 

[ preflight request 조건]

요청 헤더 목록

  • 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 헤더를 나타냅니다.

 

 

 

왜 preflight request 가 필요한가??

⇒ CORS 를 모르는 서버를 위해서 이다

만약 서버가 cors 설정이 없는 서버라면 , 클라이언트에서 본요청을 바로 보냈을 때 어떤 오류가 날까?

 

 

 

cors 가 교차출처요청 이라고 했잖아요

만약 서버가 cors 에 관해 아무런 설정이 없는 서버라고 해봅시다

클라이언트에서 본요청을 바로 보냈을 때 어떤 오류가 생기냐면

클라이언트 입장에서는 나의 origin 이라고 브라우저에게 보내게 되고

브라우저는 서버에게 그대로 보냅니다

서버는 cors 에 대해서 전혀 모르기 때문에 그냥 일단 해결을 합니다 응답을 내리게 되는데

서버는 cors 설정이 없기 때문에 allow-orgin 같은 경우가 없는데

브라우저는 그것을 확인하고 그제서야 서버한테 allow-origin 이 없다고 이거는 cors 에러야 라고 이야기 해줍니다

여기서 서버는 일단 다 해결했어요 응답을 했는데 브라우저에서 CORS 에러를 내뱉는 겁니다

이 요청이 심플한 get 요청이 아니라 delete 같은 요청이라면

서버입장에서는 db 를 다 지우고 응답을 하게 되는데 브라우저에서는 뒤늦게 cors error 라고 터지는데 늦었다는거죠

그래서 preflight 가 필요하다는 겁니다

 

 

 

preflight 는

클라이언트가 요청을 브라우저에 보내고 브라우저는 그대로 서버에 보내게되죠

서버입장에서는 cors 설정이 없으니 당연히 allow-origin 이 없죠 그래서 브라우저는 cors error 를 터트립니다

이거는 사전요청이기 때문에 서버는 행동을 하지 않아요

그래서 클라이언트에서는 이거는 cors error 이니까 다음 실제요청을 보내지 않게 되죠

그렇기 때문에 서버는 안전하게 잘 지켜집니다

다시정리하면 preflight 는 cors 를 모르는 서버를 위해서 필요한 작업이다 ! 라고 이해하시면 될 것 같아요

 

 

 

 

 

 

3️⃣ [🔐인증 정보 포함 요청 Credentialed Request]

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

 

쿠키나 jwt 토큰 을 클라이언트에서 자동으로 담아서 보내고 싶을 때 즉 , 다른 출처 간 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법이에요.

 

[credentialed request 조건]

클라이언트 측

  • credentials: include

서버 측

  • Accelss-Control-Allow-Credentials : true
  • (Access-Control-Allow-Origin: * 안된다)

해결법?

서버에서 Access-Control-Allow-Origin 헤더 세팅하기

 

 

 

 

정리

origin 즉 출처를 비교하는 로직은 서버에 구현된 스펙이 아니라 브라우저에 구현된 스펙이다.

CORS(Cross-Origin Resource Sharing)는 웹 애플리케이션에서 다른 출처(origin)로부터 온 리소스에 접근할 때 발생하는 보안 정책 이고,

CORS Error(Cross-Origin Resource Sharing Error)란, 웹 브라우저에서 보안상의 이유로 다른 출처(origin)로부터의 리소스 요청을 거부하는 오류를 의미한다.

"SOP 정책때문에 막혔던 다른 출처로의 요청을 CORS 설정을 거치게 되면 정상적으로 요청을 보낼 수 있게 된다 ! ”

728x90
반응형