728x90
이 글은 ASAC 06기를 수강하며 강의 자료 참고 및 추가 자료 수집을 통해 작성된 글입니다.
CORS(Cross Origin Resource Sharing)
- 웹 브라우저에서 유저 비의도적 요청(CSRF, 해커에 의한 악의적인 요청)에 대한 '부분적' 방어 정책
- '부분적'의 의미 : 완벽하게 CSRF를 방어하지 못한다는 의미
- W3C는 가장 먼저 SOP(Same Origin Policy)라는 정책 표준을 도입함.
=> Same Origin만 허용 - API 호출이 사실상 필수인 프론트엔드 개발자들로부터 비난을 받은 후, 추가 정책인 CORS를 도입하여
- 프론트엔드 개발자들이 자바스크립트를 통해 특정 조건하에서 API 호출을 자유롭게 할 수있게 했다.
Domain, Origin, Site의 차이점
- Domain : aaron.com
- Origin : 스킴 + 도메인 + 포트 => https:// + aaron.com + :8080
- Site: api.aaron.com or admin.aaron.com
=> Same Site = TLD(Top-Level-Domain)+SLD(Second-Level-Domain) 같은 경우
=> Same Origin = 스킴 + 도메인 + 포트 같은 경우
+) TLD와 eTLD
- .com은 TLD
- .co.kr은 eTLD (TLD를 꾸며줌)
주의 : CORS가 CSRF 공격을 모두 방어하는 것은 아님.
CORS => 1. 웹 브라우저에서 2. 자바스크립트 AJAX(Asynchronous Javascript XML)를 통한 CSRF 방지
- 웹 브라우저 : 네이티브 앱에서는 CSRF 방어를 위해 CSRF 토큰 사용 (임의 난수 + 세션 활용)
- => 네이티브 앱에서는 CORS 아닌 CSRF 토큰 사용 필요
- => CSRF 토큰을 통하여 Same Origin인지 검증한다.
- 자바스크립트 AJAX
- => AJAX 아닌 FORM을 통한 POST 요청은 방어 불가
- FORM => HTML 페이지 반환. 렌더링 O. 동기 요청
- 페이지 새로고침이 발생한다.
- AJAX => XML(현재에는 JSON)을 반환. 리렌더링 X. 비동기 요청
- 페이지 새로고침이 발생하지 않는다.
- 비동기 처리. XHR 객체 활용
- XHR (XmlHttpRequest) : W3C 표준이 아님(브라우저마다 설계 방식의 차이로)
- 화면이 없는 내장 브라우저로 HTML 문서를 받을 필요가 없고 페이지 새로고침이 발생하지 않는다.
- XML 뿐만 아니라 모든 종류의 데이터를 가져올 수 있다.
- 현재 대부분의 주요 웹 브라우저는 서버에 데이터를 요청하기(API) 위해 XHR 객체를 활용한다.
https://developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest
SOP (Same-Origin Policy)
- 웹 브라우저는 이미지, 아이콘처럼 외부에서 정보를 가져오는 경우가 있기 때문에, 부분적으로 Cross-Origin을 허용한다.
- Cross-Origin '가져오기' (<img/>, <script/>) : 의도된 조회 = 서버 상태 변경 불가
- Cross-Origin '제출하기' (FORM) : 개발자가 설계한 의도된 제출 = 의도된 서버 상태 변경
- Cross-Origin '요청하기'(AJAX - 예, POST, DELETE) : SOP에서도 불허.
- Cross-Origin에 대한 AJAX는 Cross-Origin 서버 상태를 바꿀 수 있는 보안 위험성이 존재하기 때문이다.
AJAX는 CORS 정책에 맞을 시에만 조건부 허용
- React - Spring 일 때 CORS 발생. React에 Proxy 설정 혹은 Next.js 도입하면, 프록시는 작은 서버
- 서버 - 서버 간 통신은 CORS 에러가 발생하지 않기 때문이다.
- BFF (Backend - For - Frontend) -> Next.js 쓸 때 생긴 작은 서버. SSR을 위해서.
- 리액트 - 프록시, 혹은 Next.js의 BFF는 Same-Origin 이므로 CORS 문제가 발생하지 않는다.
- 리액트는 모든 요청을 프록시를 통해서 서버로 하게 된다.
CORS 서버 측 적용 방법
CORS는 웹 브라우저를 위한 정책이지만, 쿠키와 마찬가지로 CORS 설정은 웹 서버에서 한다.
3가지 CORS 헤더 설정
허용 정책을 설정하고, 나머지는 다 막는다.
- 허용된 Origin
- 허용된 Method
- 허용된 Header
- CORS는 웹 브라우저의 정책이다.
- 그러나 CORS 정책 설정은 웹 서버에서 한다.
- 헤더에 Allow-Origin 주소를 담아서 응답한다.
- 브라우저는 응답 헤더의 Allow-Origin을 확인하여 응답을 쓸 지 버릴 지 결정한다.
서버가 설정한 것으로 브라우저가 판단한다.
- 하지만 무조건 응답을 받기 때문에 패킷을 볼 수 있다는 문제점이 있다.
- => HTTPS로 패킷 내용을 감춘다.
- GET이 아닌 POST라면 이미 값이 바뀌고 응답까지 받을 수 있다.
- => Pre-flight를 사용하여 Same Origin인지 확인한 후, 원요청(POST, 서버의 상태 변환)을 할 지 말 지 결정한다.
브라우저의 유효성 검사
- 허용된 Origin
- 브라우저는 요청 헤더와 응답 헤더를 비교.
- Origin 헤더 -> 브라우저가 요청할 때 쓰는 헤더
- Access-Control-Allow-Origin 헤더 -> 서버가 응답할 때 쓰는 헤더
- Access-Control-Allow-Origin 헤더는 특정 도메인 1개 혹은 모든 도메인(*)을 값으로 가진다.
- 허용된 Method
- Access-Control-Request-Method -> 브라우저가 요청할 때 쓰는 헤더
- Access-Control-Allow-Method -> 서버가 응답할 때 쓰는 헤더
- 허용된 Header
- Access-Control-Request-Headers -> 브라우저가 요청할 때 쓰는 헤더
- Access-Control-Allow-Headers -> 서버가 응답할 때 쓰는 헤더
- 자격증명 Header 전달 허용
- 자격증명 : Cookie, Authorization Headers 또는 TLS client 인증을 위한 정보들
- allowCredentials = true 혹은 credentials: “include” -> -> 브라우저가 요청할 때 쓰는 헤더
- allowCredentials = true => jQuery 사용 시 쓰는 헤더
- credentials:“include” => fetch(), axios() 사용 시 쓰는 헤더
- => 개발자의 의도된 요청에 대해서만 자격증명을 보낼 수 있도록 한다.
- Access-Control-Allow-Credentials = true -> 서버가 응답할 때 쓰는 헤더
- Access-Control-Allow-Credentials 사용 시, Access-Controll-Allow-Origin 헤더 값이 *일 수 없다.
- 자격 증명을 모두에게 보내면 안되기 때문이다.
- 만약 Access-Controll-Allow-Origin: *를 허용하면, CSRF 공격에 매우 취약해진다.
- 스프링 사용 시, allowedOriginPatterns()를 사용한다.
- Access-Control-Allow-Credentials 사용 시, Access-Controll-Allow-Origin 헤더 값이 *일 수 없다.
Access-Control-Allow-Origin과 allowedOriginPatterns의 차이
- Access-Control-Allow-Origin 헤더는 특정 도메인 1개 혹은 모든 도메인(*)을 값으로 가진다.
- allowedOriginPatterns()는 스프링 시큐리티에서 사용되는 기능으로, 여러 개의 도메인 혹은 와일드카드를 사용한 패턴을 허용한다.
- 스프링에서 allowedOriginPatterns()를 사용하면, 브라우저의 요청으로부터 origin 헤더의 도메인을 읽어서 allowedOriginPatterns에 담긴 값인지 확인한다. 만약 허용하는 도메인에 있다면, Access-Control-Allow-Origin에 해당 도메인을 설정하여 응답한다.
스프링에서는 allowedOriginPatterns()의 유연한 처리로, Access-Control-Allow-Credentials가 true일 때 특정 도메인으로의 자격 증명만 허용할 수 있다.
Simple Request & Preflight Request
- Simple Request
요청 -> 처리 -> 응답 -> 검증
검증의 시기가 처리 후이다.
결과와 함께 CORS 헤더를 전송
- Safe Method = 서버 상태 조회 Methods. GET, HEAD
- Unsafe Method = 서버 상태 변경 Methods. POST, DELETE
Safe Method는 처리 후 검증 해도 상관이 없다.
그러나 Unsafe Method는 처리 후 검증 하면 늦는다.
Unsafe Method에 대해서는 Preflight Request를 사용한다.
- Preflight Request
요청 -> 응답 -> 검증 -> 요청 -> 처리 -> 응답
첫번째 요청은 의미 없는 요청이다. (실제 요청이 아님)
즉, 검증의 목적만 있는 조건부 요청
조건부 요청(임시 요청, OPTIONS라는 METHOD를 사용함)에 대해서는 서버가 CORS 헤더만 보내준다.
브라우저는 CORS 헤더가 요청과 부합하지 않으면 => 원 요청을 보내지 않는다.
CORS 헤더가 요청과 부합한다면, 원 요청을 보내게 되고, 원 요청에서는 Unsafe Method 수행 후 결과를 반환받을 수 있다.
728x90
'ASAC' 카테고리의 다른 글
[ASAC 06] 세션과 JWT, CORS 관련 질문 정리 (0) | 2024.09.12 |
---|---|
[ASAC 06] Shell, Linux 명령어 (0) | 2024.09.12 |
[ASAC 06] HTTPS (1) | 2024.09.11 |
[ASAC 06] 웹 저장소 (쿠키, 세션, 웹 스토리지) (4) | 2024.09.11 |
[ASAC 06] 프록시 사용 목적, 기능과 예시 (1) | 2024.09.01 |