CORS 등장 배경
CSRF(유저 비의도적 요청) 부분 방지를 위한 웹 브라우저 정책
웹 브라우저는 자바스크립트를 동작시키는 엔진을 갖고있어, 무궁무진한 작업 수행이 가능함과 동시에 해커가 자바스크립트를 통해 위험한 작업을 수행할 수 있는 무한한 가능성을 갖게 된기도 한다.
보안 취약성의 거의 대부분은 웹을 통해 이루어지며, 최전선에 있는 웹 브라우저는 보안에 예민할 수 밖에 없다.
해커는 유저가 웹 브라우저를 정상적으로 사용할때, 악의적인 자바스크립트 수행으로 타 서버를 공격할 수 있다.
웹 브라우저는 그런 요청이 발생하더라도 웹 브라우저 자신이 최대한 방지할 수 있도록 정책들을 마련해놓았다.
- W3C는 가장 먼저 SOP라는 정책 표준을 도입하여 웹 브라우저에서 단단하게 방어를 하도록 했는데
- API호출이 사실상 필수인 프론트엔드 개발자들로부터 온갖 욕을 먹고 추가 정책인 CORS를 도입하여
- 프론트엔드 개발자들이 자바스크립트를 통해 특정 조건하에서는 API 호출을 자유롭게 할수 있게 했다.
CSRF(Cross Site Request Forgery)
: 타 사이트 대한 의도치 않은 요청
A 사이트를 이용하는 유저가 의도하지 않았는데, B 사이트(크로스사이트)에 의도치 않은 요청이 보내지는 것
- 꼭 웹 브라우저에서만 가능한 것뿐만 아니라 네이티브 앱에서도 가능
- 웹 브라우저에서 CSRF 형태: 유저가 의도하지 않은 요청이 자바스크립트 실행을 통해 서버에 전송됨. (AJAX로 서버에 요청을 보낸다)
💡CSRF와 CORS
CS__(Cross Site)와 CO__(Cross Origin)혹은 Same Site, Same-Origin차이는 무엇인가
[Origin과 Site의 차이]
- origin: scheme + host name(domain name) + port
- https://api.aaron.com:8080
- site: domain name 중 SLD(Second Level Domain) + TLD(Top Level Domain)
- api.aaron.com / admin.aaron.com (api 혹은 admin는 sum domain)
- 다른 말로 정의하면 TLD + 1 혹은 eTLD + 1 영역을 가르킨다.
- TLD(Top Level Domain) 에는 eTLD(유효 TLD) 개념이 존재
CORS란
AJAX 통한 타 사이트 웹 서버에 대한 요청 방어를 위한 웹 브라우저의 정책
웹 브라우저에서 자바스크립트 AJAX 를 통한 CSRF방지
- 웹 브라우저: 네이티브 앱에서는 CSRF 방어하기 위해 XSRF Token 사용(임의 난수 + 세션 활용)
- 내가 요청한게 아니라고 token으로 확인
- 자바스크립트 AJAX: 웹 브라우저에서 AJAX가 아닌 FORM을 통한 POST 요청 방어 불가
결과적으로 CORS를 통해 모든 CSRF공격을 방지할 수 있는건 아니다.
CORS는 웹 브라우저에서만의 정책
- 네이티브 어플리케이션의 경우 CORS가 적용되지 않기에 방어되지 않음 -> XSRF 토큰 필요
CORS는 AJAX에 대한 정책
- AJAX가 아닌 FORM을 통한 POST요청에는 CORS 정책이 적용되지 않아 여전히 XSRF 이슈
웹 브라우저 비동기 지원 AJAX
- FORM(Synchronous): 보내고 끝(HTML 페이지 반환 = 페이지 리렌더 존재)
- 회원가입이나 로그인 페이지에 적합(페이지 전환이 필요한 페이지이므로)
- AJAX(Asynchronous JavaScript and XML): XHR (XML 객체 반환 = 페이지 리렌더 하지 않음)로 구현한 비동기 요청
비동기
- XHR 객체 활용 시 서버에 데이터를 요청하거나, 데이터를 전송받을 수 있다
- 즉, 웹 페이지 전체를 다시 로딩하지 않고 일부분만을 갱신할 수 있게 된다.
XHR (XmlHttpRequest): W3C 표준이 아님(브라우저마다 설계 방식 차이)
- 화면이 없는 내장 브라우저로써, HTML문서를 받을 필요가 없고, 데이터를 XML로 주고 받음
- 통신 시 필요한 데이터를 XML(현대엔 JSON) 형식으로 주고 받는 역할로 AJAX 가능하게 함
- 현재 대부분의 주요 웹 브라우저는 서버에 데이터를 요청하기(API) 위해 XHR 객체 활용
웹 브라우저 보안 정책 SOP(Same Origin Policy)
웹 브라우저에서 HTTP Resource를 갖고오기 위한 모든 HTTP요청은 기본적으로 SOP
하지만 웹 브라우저는 이미지, 아이콘처럼 외부에서 정보를 가져오는 경우가 있기 때문에 SOP정책은 'Same-Origin'이름과 달리 부분적으로 Cross-Origin를 허용
- Cross Origin '가져오기'(`<img/>`, `<script/>`): 의도된 조회 = 서버 상태 변경 불가
- Cross Origin '제출하기'(from): 개발자가 설계한 의도된 제출 = 의도된 서버 상태 변경
- Cross-Origin '요청하기' (AJAX-예, POST): 서버 상태 변경 가능 -> 이건 SOP에서도 불허
- Cross-Origin에 대한 AJAX는 Cross-Origin서버 상태를 바꿀 수 있는 보안 위험성 존재
프론트엔드는 외부 API 호출히 필수인데 SOP에서 그걸 막아버림.. 그래서 SOP정책을 보안해서 나온게 CORS
CORS(Cross Origin Resource Sharing)
Cross Origin에 대한 AJAX는 Cross Origin 서버 상태를 바꿀 수 있는 보안 위험성이 존재한다. 그렇다고 API호출이 표준인 AJAX를 막으면 프론트엔드 개발이 불가능 -> AJAX에 대한 예외적 허용을 위한 SOP를 보안하는 추가 보안 정책이 필요 -> CORS
웹 브라우저에서 처리하는 것이기에 서버-서버 통신시에는 CORS이슈는 전혀 발생하지 않는다.
React-Spring일땐 CORS가 발생했었으나 React 에 proxy설정 혹은 Next.js 도입 뒤 CORS 미발생
CORS 서버 측 적용 방법
웹 서버는 웹 브라우저로부터 어떤 요청만 허용하는지 3가지 CORS헤더 설정으로 호출가능 요청을 제약한다.
- 허용된 Origin
- Origin(브라우저) -> (1)브라우저가 요청할 때 보낸다
- Access-Control-Allow-Origin(서버) -> (2)서버가 응답할 때 보낸다.
- 브라우저는 브라우저가 요청할 때 보낸 헤더(1)와 서버가 응답할 때 보낸 헤더(2)를 비교해 유효성검사
- 허용된 method
- Access-Control_Request-Method(브라우저) -> (1)
- Access-Control-Allow-Method(서버) -> (2)
- 브라우저가 (1) 과 (2)을 비교하여 요청의 유효성 검증
- 허용된 Header
- Access-control-Request-Headers(브라우저) -> (1)
- Access-control-Allow-Method(서버) -> (2)
- 브라우저가 (1)과 (2)을 비교하여 요청의 유효성 검증
브라우저는 서버에서 설정한 위 3가지 CORS설정을 알기 위해서 서버에게 요청을 보내고 응답 헤더로 알 수 있다.
웹 브라우저 요청 시 자격증명 Header 전달 허용
- 자격증명: Cookie, Autjorization Headers 또는 TLS client 인증을 위한 정보들
- 자격 증명(인증) 요청 시 allowCredentials = ture 혹은 credentials: "include"(브라우저)
- 요청에 자격증명(인증)과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credectials옵션
- credentials: "include": 최신 자바스크립트 fetch() 활용한 API호출 시
- allowCredentials = ture: 구식 자바스크립트 XMLHttpRequest 활용한 API호출 시
- 요청에 자격증명(인증)과 관련된 정보를 담을 수 있게 해주는 옵션이 바로 credectials옵션
- Access-Control-Allow-Credentials = true (서버)
- 웹 서버 측에서 본 헤더 설정을 통해 자격증명이 담긴 요청을 받을 수 있게 된다.
- Access-Control-Allow-Credentials 사용시 Access-Controll-Allow-Origin 헤더 제약 존재
- Spring Security에서 Access-Controll-Allow-Origin이 *이면 안되는 강력한 제약사항
‼️ 왜 Spring Security에서 allowOrigins(*)를 금지할까?
모든 클라이언트에 대해 자격증명을 받을 수 있다. 토큰이 탈취되면 자격증명을 얻고 악의적인 행동을할 수 있다.
대신 allowOriginPaterns(*)를 사용하도록 Spring Security는 제안하고 있음.
[CORS 서버 측 적용 동작 과정]
- 서버가 응답 헤더에 Allow-Origin을 전달
- 브라우저는 요청할 때 보낸 헤더와 서버 응답으로 받은 Allow-Control-Allow-Origin 헤더에 대해 동등성 비교한다.
CORS 정책에 대한 판단은 브라우저에서 진행
CORS 검증을 위한 요청 1. Simple Request: 서버 상태 조회
서버 상태 조회를 위한 methods: GET, HEAD
[허용된 Origin 체크 절차]
- 브라우저가 서버에게 원요청을 보내면
- 서버는 CORS 헤더가 요청과 부합하지 않으면 -> 반환 결과를 페기한다.
CORS 검증을 위한 요청 2. Preflight Request: 서버 상태 변경
서버 상태 변경을 위한 Methods: POST, PATCH, PUT + GET, HEAD w/ 커스텀 Header
[허용된 Origin 체크 + 허용된 Method 체크 + 허용된 Header 체크 절차]
- 브라우저가 서버에게 임시 요청(Preflight, OPTIONS)을 보내면
- 서버는 CORS헤더만 보내준다
- 브라우저는 CORS헤더가 요청과 부합하지 않으면 원 요청을 보내지 않는다.
- 원요청이 보내지면 그때 서버 상태 변경이 된다
'ASAC 웹 풀스택' 카테고리의 다른 글
자바스크립트 기본, 심화 문법 및 엔진 동작 원리(2) - JS 변수, 함수 정의 및 사용 (0) | 2024.09.10 |
---|---|
자바스크립트 기본, 심화 문법 및 엔진 동작 원리(1) - 함수형 프로그래밍 패러다임(순수함수, 얕은복사, 깊은복사) (1) | 2024.09.10 |
웹 저장소 및 HTTPS, CORS 보안(5) - HTTPS (0) | 2024.09.08 |
웹 저장소 및 HTTPS, CORS 보안(4) - Session (0) | 2024.09.08 |
웹 저장소 및 HTTPS, CORS 보안(3) - Storage (0) | 2024.09.08 |