출처(Origin) | SOP | CORS
출처(Origin)
URL에서 protocol, host, port를 묶어서 Origin이라 한다.
localhost != 127.0.0.1
둘은 동일한 사이트로 연결되지만, URL을 문자열 값으로 비교하면 동일하지 않으므로, 다른 출처라고 판단한다.
📌SOP
SOP(Same-Origin Policy, 동일 출처 정책)
: 동일한 출처 간에 API 등의 데이터 접근 및 리소스만 사용이 가능하도록 하는 보안 방식
1️⃣ 사용자가 A라는 동일한 출처의 사이트에 정보를 가지고 요청을 하는 상황
2️⃣ 실수로 다른 출처인 사이트에 접속하게 되어 지니고 있던 정보가 유츨된다.
3️⃣ 이 정보를 가지고 악의적으로 A 사이트에 원치 않았던 요청을 하게 될 수 있다.
XSS : 접속한 사용자의 브라우저에 악성 스크립트가 실행되도록 하여 세션 가로채기, 피싱 공격 등을 의미한다. [Cross Site Scripting]
CSRF : 사용자의 의지와 무관한 행위를 특정 웹 사이트에 요청하는 공격을 의미한다. [Cross-Site Request Forgery]
따라서, 동일한 출처인지를 확인하는 SOP를 통한 보안이 필요하다.
📌CORS
CORS(Cross-Origin Resource Sharing, 교차 출처 자원 공유 방식)
: SOP의 반대 개념으로, 다른 출처간에 리소스를 공유할 수 있도록 하는 것
❓원리
추가 HTTP 헤더를 이용하여, 다른 출처에 접근할 수 있는 권한을 부여할 수 있도록 브라우저에게 알려준다.
1️⃣ Header에 Origin을 담아 요청한다.
2️⃣ 요청을 받은 서버는 응답 Header에 Access-Control-Allow-Origin를 담아 응답한다.
3️⃣ Browser에서는 Origin과 Access-Control-Allow-Origin을 비교하여 제한해야 하는지를 판단한다.
CORS의 3가지 시나리오
- Simple Request
- Preflight Request
- Credentialed Request
❕ Preflight Request
1️⃣ 사전 요청인 Preflight Request를 보낸다.
2️⃣ 요청이 가능하다고 하면, 실제로 보내려 했던 요청(Actual Request)을 한다.
❓ 왜 사전 요청해야 하는가
서버에서 마련해둔 CORS가 마련되지 않은 상황에서 PUT이나 DELETE처럼 데이터에 변화를 주는 요청이 보내진다면,
이미 서버에서는 해당 요청을 처리하여 데이터에 변화가 일어나게 되고 그 후에 브라우저에서 Origin과 Access-Control-Allow-Origin(없음)을 비교히여 CORS 에러가 발생하게 된다.
이렇게 되면, 이미 데이터에 변화가 일어났기 때문에 서버 측에서는 제대로 요청과 응답이 처리된 것 같아 보여도, 브라우저 측에서는 CORS 에러가 발생하여 문제가 된다.
따라서, Preflight Request를 보내어 안전한지 & 요청이 가능한지 미리 확인하도록 한다.
[ Preflight Request ]
- Origin : 요청 출처
- Access-Control-Request-Method : 실제 요청의 메서드 (PUT, DELETE 등)
- Access-Control-Request-Headers : 실제 요청의 추가 Header
HTTP 메서드 중 OPTIONS 메서드를 이용하여 사전 요청을 보낸다.
[ Preflight Response ]
- Access-Control-Allow-Origin : 접근 허용 출처
- Access-Control-Allow-Methods : 허용 메서드
- Access-Control-Allow-Headers : 허용 Header
- Access-Control-Max-Age : Preflight 응답 캐시 시간
- 브라우저에서 해당 시간동안에는 실제 요청을 바로 보낼 수 있도록 함
❕ Simple Request
Simple Request란, Preflight 요청을 보내지 않고 바로 실제 요청(Actual Request)을 보내는 것이다.
[ 조건 ]
1. HTTP 메서드가 GET, POST, HEAD 여야 한다.
2. 허용 Header
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR, Downlink, Save-Data, Viewpoint-Width, Width
3. 허용 Content-Type
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
이 모든 조건이 충족되어야 Preflight가 아닌 Simple Request가 보내질 수 있다.
❕ Credentialed Request
Credentialed Request란, 쿠키나 JWT 토큰을 요청에 담아서 보내고 싶을 때처럼 인증 관련 헤더를 포함할 때 사용하는 요청이다.
[ Client ]
credentials라는 옵션을 include로 설정한다.
same-origin(default) | 같은 출처 간 요청에만 인증 정보를 전달한다. |
include | 모든 요청에 인증 정보를 전달한다. |
omit | 인증 정보를 전달하지 않는다. |
[ Server ]
Response Header에도 마찬가지로 Access-Control-Allow-Credentials가 true로 담겨있어야 한다.
+ Access-Control-Allow-Origin : * 로 모든 것을 허용해주는 것은 불가능하다.
[10분 테코톡] 🌳 나봄의 CORS