웹 개발 시, 다른 도메인 서버에 있는 자원을 가져다 쓰거나 제공해주는 경우가 많음
웹 브라우저는 HTTP 요청에 대해 각기 다른 특징을 가지기 때문에 CORS ERROR가 생김
CORS란?
Cross-Origin Resource Sharing
교차 출처 리소스 공유 (교차 출처 = 다른 출처)
다른 출처의 리소스 공유에 대한 허용/비허용 정책
• 출처(Origin)?
Origin = scheme + host + port
- scheme(protocol) : http, https
- host : 사이트 도메인
- port : 포트 번호
https://www.google.com/maps
라는 주소에서 protocol = https, host = www.google.com, port = :443
• 동일출처(Same-Origin)?
동일 출처란 protocol, host, port가 모두 같을 때
• SOP(Same-Origin Policy)
같은 출처 에서만 리소스를 공유할 수 있다는 규칙
웹 브라우저에서 동작하는 프로그램은 로딩된 위치에 있는 자원에만 접근할 수 있음
동일 출처 정책이 왜 필요할까..?
출처가 다른 두 어플리케이션이 소통할 수 있는 환경은 꽤 위험한 환경
제약이 없다면
CSRF(Cross-Site Request Forgery)나 XSS(Cross-Site Scripting) 등의 방법을 이용해서 우리가 만든 어플리케이션에서 해커가 심어놓은 코드가 개인 정보 가로챌 수 있음..
• 다른출처(Cross-Origin)?
URL 구성 요 중 scheme, host, port 중 하나라도 다른 것
https://www.heedonguri.com 출처에 대한 비교
URL | 동일 출처인가? | 이유 |
https://www.heedonguri.com:3000/about | O | 프로토콜, 호스트, 포트 번호 동일 |
https://www.heedonguri.com:3000/about?username=hi | O | 프로토콜, 호스트, 포트 번호 동일 |
http://www.heedonguri.com:3000 | X | 프로토콜 다름 |
https://www.heedonguri.co.kr:3000 | X | 호스트 다름 |
https://www.heedonguri.com:8888 | X | 포트 번호 다름 |
https://www.heedonguri.com | X | 포트 번호 다름(443) |
다른 요소는 다르더라도 같은 출처로 인정됨
다른 출처에 있는 리소스를 가져오는건 우리에게 흔한 일이다!
어떻게 가능한걸까?
몇가지 예외 조항을 두고, 이 예외 조항에 해당하면 허용하기 때문이다
그 중 하나가 바로 CORS 정책을 지킨 리소스 요청!
따라서 CORS는 다른 출처의 리소스를 얻기 위한 해결방안임
-> SOP를 위반해도 CORS에 해당하면 다른 출처의 리소스를 얻을 수 있기 때문!
CORS 동작 방식
- 웹은 HTTP 프로토콜을 이용해서 서버에 리소스 요청
이때 브라우저는 요청 헤더의 Origin이라는 필드에 요청을 보내는 출처를 담아서 보냄 - 서버가 응답할 때, 응답 헤더에 Access-Control-Allow Origin이라는 값에 "이 리소스를 접근하는 것이 허용된 출처"를 내려줌
- 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해서 차단할지 말지 결정
(서버에서 응답의 Access-Control-Allow-Origin 헤더에 와일드카드(*)를 설정하는 것은 모든 Origin을 허용한다는 것)
결국 서버에서 Access-Control-Allow-Origin 헤더에 허용할 출처를 기재해서 클라이언트에 응답하는 것
-> 백엔드 개발자가 해라~
주의할 점!!
출처를 비교하는 로직은 서버에 구현된 스펙이 아니라 브라우저에 구현되어 있는 스펙
CORS 정책을 위반하는 리소스 요청 시, 서버는 정상적으로 응답을 하고
이후 브라우저가 응답을 분석해서 CORS 정책 위반이라고 판단되면 그냥 버림
CORS는 브라우저의 구현 스펙에 포함되는 정책 -> 브라우저 통하지 않고 서버 간 통신을 하면 정책 적용X
기본적인 흐름은 이렇고, 세 가지 시나리오가 있다
Preflight Request, Simple Request, Credentialed Request인데
요청이 어떤 시나리오에 해당하는지 파악할 수 있다면 CORS 정책 위반으로 인한 에러 고치기 쉬워짐
하나씩 알아봅시당
Preflight Request(예비 요청)
브라우저는 요청을 한 번에 보내지 않고 예비 요청과 본 요청으로 나누어서 서버로 전송
- 브라우저는 서버로 HTTP OPTIONS 메소드를 사용해서 예비 요청 보냄
- 서버는 예비 요청에 대한 응답으로 어떤 것을 허용하고 어떤 것을 금지하는지 헤더 정보에 담아 브라우저로 보냄
- 브라우저가 보낸 요청과 서버가 응답해준 정책을 비교해서, 해당 요청이 안전한지 확인하고 본 요청 보냄
- 서버가 본 요청에 대한 응답을 하면, 응답 데이터를 자바스크립트로 넘겨줌
여기서 주의할 점은
브라우저가 CORS 정책 위반 여부를 판단하는 시점이 예비 요청에 대한 응답을 받은 이후라는 것!
Simple Request(단순 요청)
- 서버에게 본 요청을 보내고, 서버가 이에 대한 응답 헤더에 Access-Control-Allow-Origin을 보냄
- 브라우저가 CORS 정책 위반 여부를 검사
예비 요청을 생략할 수 있는 경우
- 요청 메서드가 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/xml 이나 application/json으로 통신(3번째 경우 위반)
이렇게 조건들이 까다롭기 때문에 Simple Request가 일어나는 경우는 드물다고 보면 됨
=> 대부분 Preflight Request임
Credentialed Request(인증정보 포함 요청)
인증정보란 쿠키 혹은 Authorization 헤더에 설정하는 토큰 값을 말함
CORS 기본적인 방식보다는 다른 출처 간 통신에서 보안 강화하고 싶을 때 사용하는 방법
- 클라이언트에서 인증 정보 옵션 설정
기본적으로 브라우저가 제공하는 요청 API들은 별도의 옵션 없이 브라우저의 쿠키와 같은 인증과 관련된 데이터를 함부로 요청 데이터에 담지 않도록 되어있음
요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 credentials 옵션
옵션 값 | 설명 |
same-origin(기본 값) | 같은 출처 간 요청에만 인증 정보 담을 수 있음 |
include | 모든 요청에 인증 정보 담을 수 있음 |
omit | 모든 요청에 인증 정보 담을 수 없음 |
이런 설정 없이는 인증 정보는 절대로 서버에게 전송되지 않음
- 서버에서 인증된 요청에 대한 헤더 설정
- Access-Control-Allow-Origin에는 *를 사용할 수 없으며, 명시적인 URL이어야 함
- 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true 가 존재해야 함
CORS 해결 방법
너무너무너무 어려운 CORS,, 해결 방법도 알아보..자..
• Access-Control-Allow-Origin 세팅
직접 서버에서 HTTP 헤더 설정을 하는게 정석적인 해결책
서버 종류가 다양하므로, 각 서버의 문법에 맞게 HTTP 헤더를 추가해주면 됨
CORS와 연관된 HTTP 헤더 값 종류들(모두 설정할 필요는 없음)
- Access-Control-Allow-Origin
접근 허용된 브라우저 - Access-Control-Request-Methods
리소스 접근을 허용하는 HTTP 메서드 지정 - Access-Control-Allow-Headers
요청을 허용하는 헤더 - Access-Control-Max-Age
preflight의 요청 결과를 저장할 기간 저장 - Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
브라우저 측에서 접근할 수 있게 허용해주는 헤더
• 프록시 사이트 이용
프록시 = 클라이언트와 서버 사이의 중계 대리점
서버에서 따로 설정을 안해줘서 CORS 에러가 뜬다면?
모든 출처를 허용한 서버 대리점을 통해 요청을 하면 됨
원래 SOP 정책으로 막혔어야할 외부 리소스들이 CORS 정책으로 뚫렸으니 공격에 노출되는건 당연하다..
CORS 정책에 대해 너무 유연하게 리소스를 허용하면, 개인정보 해킹에 대한 위험성이 생김..
무슨 말이냐면
공부해야될게 왕창 남았다는 말~
'WEB' 카테고리의 다른 글
JWT Token(+로그인) (5) | 2024.05.07 |
---|---|
PWA란? (0) | 2023.05.14 |
Node.js vs 브라우저 환경 (0) | 2023.05.08 |
Rest API, RESTFUL API (0) | 2023.05.06 |
쿠키 vs 세션 vs 웹 스토리지 (0) | 2023.05.05 |