일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 인덱스 알고리즘
- crypto 난수생성
- 중복순열중복조합
- 티스토리챌린지
- cors origin
- sop cors
- 순열조합
- 백트래킹자바
- 멀티프로세스 멀티스레드
- javascript 난수생성
- 인덱스 예시
- crypto 0이상 1미만
- 순열조합중복순열중복조합
- 순열java
- 오블완
- cors동작방식
- db 인덱스 개념
- 백트래킹알고리즘
- cors시나리오
- graphql 장단점
- crypto.getrandomvalues()
- crypto.getrandomvalues() 부동소수점
- 조합java
- javascript crypto
- 이해할때까지안잔다
- 대칭키 비대칭키
- 백트래킹java
- graphql 비교
- graphql 예시
- 백트래킹
- Today
- Total
물흐르듯코딩
[WEB] CORS 개념 및 동작 방식 시나리오 본문
개발을 하다보면 Invalid CORS request와 같은 에러가 반환되는 경우가 있다.
이러한 경우 백엔드 로그에도 별다른 오류 로그가 없다. 즉, 브라우저에서 뱉어낸 오류인 것이다.
CORS란 무엇인지, CORS 에러는 왜 발생되는지 차근차근 알아보자.
SOP와 CORS
웹에서 출처의 동일 여부를 따져 리소스 요청을 제한하는 것과 관련된 정책은 SOP, CORS가 있다.
1) SOP(Same-Origin Policy)
“같은 출처에서만 리소스를 공유할 수 있다”라는 규칙을 가진 정책이다. 브라우저는 기본적으로 보안을 위해 SOP 정책을 따르고 있으나 개발을 하다 보면 다른 출처와의 상호작용이 많이 필요한 케이스가 많다.
2) CORS(Cross-Origin Resource Sharing)
“SOP 정책에 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용한다” 규칙을 가진 정책이다. MDN에 따르면 CORS는 아래와 같은 정의를 가진다.
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.
쉽게 말해 서로 다른 출처에 대한 요청을 안전하게 응답 받을 수 있는 보안 정책인 것이다. 그렇다면 같은 출처, 다른 출처는 어떻게 판단할까?
출처 판단 Origin
📌 출처를 비교하는 로직은 서버에 구현되는 것이 아닌 브라우저에 구현된 스펙이다. 만약 CORS 정책을 위반하는 요청에 서버가 정상적으로 응답을 하더라도 브라우저가 이 응답을 분석해서 CORS 정책에 위반되면 그 응답은 처리하지 않게 된다.
URL은 위 이미지와 같이 구성되어 있다. origin은 Scheme(Protocol), Host, Port까지 포함하며, 동일 출처 판별은 origin의 동일 여부로 판별한다. 포트 번호는 프로토콜의 기본 포트가 정해져 있기 때문에 생략이 가능하다. 만약 특정 포트 번호가 명시되어 있다면, 이 포트 번호까지 모두 일치해야 같은 출처로 인정된다.
그래서 만약 origin이 동일하면 same-origin, 3가지 중 하나라도 다르다면 cross-origin이라고 한다.
CORS 기본 동작 방식
기본적으로 웹 클라이언트에서 다른 출처의 리소스를 요청할 때 HTTP 프로토콜을 사용하여 요청을 보내게 되는데 이때 Request Header에 Origin이라는 필드에 요청을 보내는 출처를 함께 보낸다.
이후 서버가 요청에 대한 응답을 할 때 Response Headers에 Access-Control-Allow-Origin을 비교해본 후 유효한 응답인지 판별한다. CORS의 기본적인 동작방식은 이와 같지만, 분류하자면 세 가지의 시나리오가 있다.
CORS 시나리오
1) Simple Request
단순 요청(Simple Request)의 조건이 갖춰지면 서버에 본요청을 보낸 뒤, 서버는 헤더에 Access-Control-Allow-Origin 값 등을 붙여 보내주면 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다.
📌 단순 요청(Simple Request)의 조건
- 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
- Request Header에는 다음 속성만 허용 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만 허용
- 요청에 사용된 XMLHttpRequestUpload 객체에는 이벤트 리스너가 등록되어 있지 않다. 이들은 XMLHttpRequest.upload 프로퍼티를 사용하여 접근한다.
- 요청에 ReadableStream 객체가 사용되지 않는다.
대부분의 웹 어플리케이션에서 위와 같은 까다로운 조건을 모두 충족시킬 수 없기에 단순 요청 시나리오는 흔히 보기 어렵다. 대부분 text/xml이나 application/json 컨텐츠 타입을 가진다.
2) Preflight Request
브라우저에 요청을 한 번에 보내지 않고 예비 요청과 본 요청으로 나누어 서버로 전송한다. Simple Request와 비교해 보자면, 전반적인 로직은 동일하지만 예비 요청의 존재 유무가 다르다.
같은 출처의 요청이 아닌 경우, 접근할 리소스를 가지고 있는 서버에 예비 요청을 보내고 브라우저 스스로 이 요청을 보내는 것이 안전한지 확인한다. 예비 요청에는 OPTIONS 메소드가 사용되며, OPTIONS 요청을 받은 서버는 Response Header에 서버가 허용할 옵션을 설정하여 브라우저에게 전달한다.
💡 예비 요청은 왜 필요할까?
Request Body에 큰 크기의 배열(e.g. 10,000개의 Array)을 담아 보내는 상황에서 예비 요청이 없이 본 요청을 보냈다가 CORS 정책에 위반된다면 리소스를 낭비하게 된다. 따라서 리소스 낭비 방지 차원에서 예비 요청을 통해 요청이 정책에 위반되지 않는지 확인하는 과정이 필요하다.
3) Credential Request
헤더에 인증과 관련된 정보(쿠키, 토큰 등)를 담아 보내는 방식이다. 다른 출처 간 통신에서 보안을 강화하고 싶을 때 사용하는 방법이다.
기본적으로 출처가 다른 경우에는 쿠키나 인증 관련 헤더를 보낼 수 없으나 credentials 옵션을 설정하면 요청에 인증과 관련된 정보를 담을 수 있다. 옵션은 프론트에서 설정할 수 있으며, 총 3가지 값을 사용할 수 있다.
- same-origin (기본값) : 같은 출처 간 요청에만 인증 정보를 담는다.
- include : 모든 요청에 인증 정보를 담는다.
- omit : 모든 요청에 인증 정보를 담지 않는다.
만약 위 옵션 중 same-origin 또는 include를 사용하여 요청에 인증 정보를 담게 되면, 리소스를 요청할 때 Access-Control-Allow-Origin 뿐 아니라 다른 검사 조건을 추가하게 된다. 서버 쪽에선 아래와 같은 조건에 맞춰 CORS 설정이 필요하다.
1. Access-Control-Allow-Origin에는 *(와일드 카드)를 사용할 수 없으며, 명시적인 URL이어야 한다.
2. 응답 헤더에는 반드시 Access-Control-Allow-Credentials: true가 존재해야 한다.
🏷 참고자료
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
https://velog.io/@effirin/CORS란-무엇인가#cors-정책은-언제-검사할까
'FE' 카테고리의 다른 글
[Javascript] crypto.getRandomValues() 난수 생성 - 0 이상 1 미만 난수 생성법 (0) | 2024.05.09 |
---|---|
[javascript] trim() 사용법 - 문자열 양 끝 공백 제거 (0) | 2024.04.23 |
[javascript] Object.assign() 사용법 - 객체 병합과 복제 (2) | 2024.04.20 |
[javascript] 배열 slice() & splice() 사용법 및 예시 (0) | 2024.04.18 |
[javascript] 비밀번호 조건 체크 정규식(regex) 표현 (0) | 2024.04.15 |