✨ 들어가기
웹 프론트엔드 개발자라면 한 번씩 겪어보았을 에러가 바로 CORS 에러이다.
그만큼 정확하게 원리를 이해하고 있어야 문제 상황을 빠르게 해결할 수 있기에 해당 개념을 살펴보고자 한다.
✅ SOP 은 무엇인가?
Same Origin Poclicy 로 같은 출처에서만 리소스 공유를 허용하는 정책이다.
🤔 같은 출처는 무엇인데?
URL의 Protocol, Host, Port 가 모두 동일한 출처이다.
출처라는 것은 Protocol + Host + Port 까지 모두 합친 URL로 이해할 수 있다.
예를 들면,
https://with-picme.com:8080/home?nickname=happhee#sop
- Protocol 👉 https
- Host 👉 with-picme.com
- Port 👉 8080
- Path 👉 home
- Query String 👉 ?nickname=happhee
- Fragment 👉 #sop
해당되며, 프로토콜 또는 호스트 또는 포트 중 하나라도 다르면 다른 출처로 인식한다.
즉, 동일 출처 서버에 있는 리소스는 자유롭게 가져올수 있지만, 다른 출처(Cross-Origin) 서버에 있는 리소스에는 상호작용이 불가능하다.
🤔 근데 왜 SOP 이 존재하는 걸까?
만약, 출처가 다른 두 서비스가 자유롭게 상호작용 할 수 있다고 한다면...?
CSRF(Cross-Site Request Forgery) 또는 XSS(Cross-Site Scripting)이 발생하는 보안 문제가 발생하게 될 것이다.
이러한 악성 공격으로부터의 방지를 위해 SOP 정책이 등장하게 된 것이다.
우리가 혼동하지 않아야 할 부분은 바로 이것이다.
출처를 비교하는 로직을 수행하는 것은 서버가 아닌 브라우저가 진행한다.
우리가 CORS 에러를 마주했을 때, 해당 로직이 서버에서 진행된 것이라고 오해할 수 있다.
하지만, 서버는 클라이언트에 대한 리소스 응답은 잘 진행해준다. 다만, 브라우저 단에서 받을 수 없다고 차단을 해버려 에러를 마주하게 되는 것이다.
이는 브라우저를 통하지 않고 서버 간에서 통신을 진행할 때는 해당 정책이 적용되지 않는다는 말과 동일하며 이러한 원리를 사용해 CORS에러를 해결하는 방법이 Proxy 서버 구축이다.
🤔 그럼 악성 공격으로부터 보호하기 위해 모두 다 차단하면 될까?
막상 또 그렇지도 않는다. 왜냐하면 인터넷 자체가 다수의 사람들에게 열려있는 환경이고, 웹페이지에서 다른 출처에 존재하는 리소스와 상호작용하는 일은 매우 흔하기 때문이다.
그래서 몇 가지 예외 조항을 가지면서, 다른 출처의 리소스 요청이라도 허용하는 정책인 CORS 정책이 등장하게 된것이다.
✅ CORS :: Cross-Origin Resource Sharing
다른 출처의 리소스 공유에 대한 허용/비허용 정책이다.
앞서 말한대로 보안 문제가 있어도 어쩔 수 없이 다른 출처 간의 상호작용이 필요한 경우가 발생한다.
생각해보면 CORS 는 다른 출처의 리소스를 얻기 위한 해결방법이었다.
📍 동작 과정
1. Client에서 HTTP 요청의 헤더에 Origin을 담아 전달
2. Server는 응답헤더에 Access-Control-Allow-Origin을 담아 Client로 전달
3. Client에서 Origin과 Access-Control-Allow-Origin을 비교
해당 비교 결과로 유효하다면 문제없이 다른 출처의 리소스와 상호작용이 가능하지만, 그렇지 않다면 해당 응답을 사용하지 못하고 버리게 되는 CORS 에러를 발생시킨다.
⚡️ CORS 를 위해서는 서버의 허용이 필요
결론적으로 CORS 정책에 맞게 다른 출처에 대한 리소스와 상호작용하기 위해서는 서버에서 Access-Control-Allow-Origin 헤더에 원하는 url을 기재해주어야 하는 것이다.
✅ CORS 의 3가지 작동방식
CORS의 작동방식은 1가지가 아니라 3가지라는 사실...!!
여기서, 쿠키나 토큰을 관리하는 요청을 진행하고 싶다면 해당 과정을 이해할 필요가 있다.
1️⃣ Preflight Request
브라우저가 본 요청을 보내기 전에 보내는 예비 요청으로 OPTIONS 메소드를 사용
예비 요청의 목적은 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다.
- 브라우저는 서버로 HTTP OPTIONS 메소드로 예비 요청(Preflight) 전송
- 서버는 이 예비 요청에 대한 응답으로 어떤 것을 허용하고 어떤것을 금지하고 있는지에 대한 헤더 정보를 담아서 브라우저로 전송
- 이후 브라우저는 보낸 요청과 서버가 응답해준 정책을 비교하여, 해당 요청이 안전한지 확인하고 본 요청
하지만, 예비 요청을 통한 실 요청에 대한 대기 시간이 극단적으로 증가하면 성능이 하락된다. 그래서 브라우저 캐시나 Access-Control-Max-Age를 통해 캐시 시간을 명시하여 Preflight Request에 대한 최적화를 진행할 수 있다.
2️⃣ Simple
예비 요청을 생략하고 바로 서버로 직행하는 방법이다.
단, 조건이 3가지가 있다.
- 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
- Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
- 만약 Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.
조금은 까다로운 조건들을 가지고 있기에, 해당 단순 요청은 거의 드물게 발생한다.
왜냐하면, 대부분의 HTTP API 요청이 text/html 이나 application/json으로 통신하기 때문이다.
3️⃣ Credentialed Request
Client가 Server에게 자격 인증 정보(Credential)인 쿠키 또는 토큰 등을 담은 헤더를 포함할 때 사용되는 방법이다.
📍 과정
1. 클라이언트에서 인증정보를 보내도록 설정해야 한다. 해당 옵션은 credentials 이다.
same-origin(기본값) | 같은 출처 간 요청에만 인증 정보를 담을 수 있다. |
include | 모든 요청에 인증 정보를 담을 수 있다 |
omit | 모든 요청에 인증 정보를 담지 않는다. |
2. 서버에서 인증된 요청에 대한 헤더를 설정해야 한다. 단, CORS 요청과는 다르게 대응하는 것이 필수이다.
- Access-Control-Allow-Credentials 는 true
- Access-Control-Allow-Origin 헤더가 와일드카드(*)가 아닌 분명한 Origin으로 설정
- Access-Control-Allow-Methods 헤더가 와일드카드(*)가 아닌 분명한 메소드로 지정
- Access-Control-Allow-Headers 헤더가 와일드카드(*) 금지
이러한 조건을 만족해야 인증된 요청을 가지고 리소스를 공유할 수 있다.
'WEB > Insight' 카테고리의 다른 글
[ 쏙쏙 들어오는 함수형 코딩 ] Function 액션/계산/데이터 (0) | 2023.12.06 |
---|---|
[ 좋은 코드, 나쁜 코드 ] 가독성 높은 코드를 작성하라 (0) | 2023.11.02 |
PWA (Progressive Web App) (0) | 2023.06.29 |
모바일 어플리케이션의 종류 (0) | 2023.06.29 |
Yarn berry (0) | 2023.01.11 |