# CORS
Cross Origin Resource Sharing 정책
# URL
Protocol
https://Host
www.evan-moon.comPath
/usersQuery String
?sort=asc&page=1Fragment
#fooPort
의 생략 → 기본 포트 번호- HTTP 80
- HTTPS 443
# 출처
Protocol, Host, Port
서버위치를 찾기 위한 가장 기본적인 요소
console.log(location.origin); // "https://evan-moon.github.io"
출처를 비교
하는 로직은 서버가 아니라 브라우저
에 구현되어 있다.
- 브라우저를 통하지 않은 서버 간 통신은 SOP 가 적용되지 않는다.
- 출처 비교 시 포트 번호를 완전 무시하는 브라우저는 Internet Explorer 밖에 없다.
# SOP : Same Origin Policy
같은 출처에서만 리소스를 공유할 수 있다
- CORS 정책을 지킨 리소스 요청은 출처가 다르더라도 허용한다.
# 예외 조항
스크립트, 렌더될 이미지, 스타일 시트, script, img, style 태그
<img src="https://evanmoon.tistory.com/rss">
<script src="https://evanmoon.tistory.com/rss"></script>
2
요청 자체는 성공한다
Sec-Fetch-Mode: no-cors
- 헤더의 요청 필드를 설정
- 브라우저가 no-cors 인 경우 다른 출처여도 CORS 정책을 검사하지 않는다.
- 응답을 자바스크립트에게 알려주지 않아 응답 내용을 알 수 없다.
# CORS 배경
출처가 다른 두개의 어플리케이션이 마음대로 소통할 수 있는 것은 위험하다.
웹에서 돌아가는 클라이언트 어플리케이션은 사용자의 공격에 취약하다.
- CSRF (Cross-Site Request Forgery)
- XSS (Cross0Site Scripting)
- 브라우저 개발자도구에서 각종 정보들을 아무런 제제없이 열 수 있다.
- DOM 확인
- 서버 통신 내역
- 리소스 출처
- 자바스크립트 코드 난독화는 암호화가 아니다.
- 난독화가 안되어 있어 소스코드가 그대로 노출되는 사이트들도 많다.
# CORS 와 브라우저
출처는 브라우저가 판별한다. 따라서
CORS를 위반했을 때
- 서버는 정상적으로 응답
- 응답의 파기 여부는 브라우저가 결정
# CORS 의 기본 동작
웹 클라이언트가 다른 출처의 리소스를 요청할 때 기본적인 동작
- 사용자의 첫 요청
- HTTP 프로토콜을 사용하여 요청을 보낸다
- 브라우저는 요청 헤더에
Origin
필드에 요청을 보내는 출처를 함께 담아 보낸다.
Origin: https://evan-moon.github.io
- 서버의 응답
- 응답 헤더
Access-Controll-Allow-Origin
를 보낸다.- 리소스에 접근할 수 있는 허용된 출처
- 응답 헤더
- 응답을 받은 브라우저
- 자신이 보냈던
Origin
과 응답 받은Access-Control-Allow-Origin
을 비교하여 유효한지 결정한다.
- 자신이 보냈던
# Preflight Request
웹 어플리케이션을 개발할 때 일반적으로 마주치는 시나리오
브라우저는 예비요청
(Preflight)과 본 요청
으로 나누어서 서버로 전송한다.
# Preflight
본 요청을 보내기 전, 브라우저가 요청을 보내는 것이 안전한지 확인한다.
- HTTP 의
OPTIONS
메서드가 사용된다.
const headers = new Headers({
'Content-Type': 'text/xml',
});
fetch('https://evanmoon.tistory.com/rss', { headers });
2
3
4
요청
OPTIONS https://evanmoon.tistory.com/rss
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,ko;q=0.8,ja;q=0.7,la;q=0.6
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: GET
Connection: keep-alive
Host: evanmoon.tistory.com
Origin: https://evan-moon.github.io
Referer: https://evan-moon.github.io/2020/05/21/about-cors/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
2
3
4
5
6
7
8
9
10
11
12
13
14
본 요청에 대한 다른 정보들도 미리 알려준다.
Access-Control-Request-Headers
: Content-TypeAccess-Control-Request-Method
: GET
예비 요청에 대한 응답
OPTIONS https://evanmoon.tistory.com/rss 200 OK
Access-Control-Allow-Origin: https://evanmoon.tistory.com
Content-Encoding: gzip
Content-Length: 699
Content-Type: text/xml; charset=utf-8
Date: Sun, 24 May 2020 11:52:33 GMT
P3P: CP='ALL DSP COR MON LAW OUR LEG DEL'
Server: Apache
Vary: Accept-Encoding
X-UA-Compatible: IE=Edge
2
3
4
5
6
7
8
9
10
11
- 응답 헤더의
Access-Control-Allow-Origin: https://evanmoon.tistory.com
는 요청 헤더의Origin: https://evan-moon.github.io
와 출처가 다르므로 CORS 정책 위반 에러를 띄운다.
# Simple Request
예비 요청이 없다.
- 특정조건이 만족해야 Simple Request 를 할 수 있다.
- 사용이 드물다
언제할 까? TODO
# Credentialed Request
인증된 요청을 사용하는 방법
- CORS 에서 좀더 보안을 강화하고 싶을 때 사용한다.
# credential
비동기 요청 API XMLHttpRequest
객체, fetchAPI
는 브라우저의 쿠키정보나 인증과 관련된 헤더를 담지 않는다.
이때 credential
은 요청에 인증과 관련된 정보를 담을 수 있게 해준다.
# credential 옵션의 종류
Same Origin
(크롬 Default) 같은 출처간 요청에만 인증 정보를 담을 수 있다.include
모든 요청에 인증정보를 담을 수 있다.omit
모든 요청에 인증정보를 담지 않는다.
Same Origin
include
옵션이라면 Access-Control-Allow-Origin
이외에 조건이 더 까다로워진다.
Same Origin
:Access-Control-Allow-Origin: *
허용include
응답헤더에Access-Control-Allow-Origin: *
미허용Access-Control-Allow-Credentials: true
필수
# CORS 해결 방법
# 1. Access-Control-Allow-Origin 세팅
- 서버엔진의 설정 nginx, apache
- 소스코드 내에 응답 미들웨어 세팅
- CORS 미들웨어 라이브러리를 찾아보자
# 2. Webpack DevServer 로 리버스 프록싱
프론트엔드 개발환경에서 CORS 를 많이 마주칠 것이다.
벡엔드의 Access-Control-Allow-Origin: http://localhost:3000
같이 범용적인 출처를 넣어주는 경우는 드물다.
그래서 프론트엔드의 webpack-dev-server
의 라이브러리의 프록시 기능은 CORS 정책을 우회하게 해준다.
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.evan.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
}
}
}
2
3
4
5
6
7
8
9
10
11
localhost:8000/api
처럼, path 가/api
로 시작되는 요청을 웹팩이https://api.evan.com
으로 요청을 프록싱 해준다.- 브라우저는 여전히
localhost:8000/api
로 알고있다.