CORS
CORS는 Cross Origin Resource Sharing의 약자로, 교차 출처 공유라는 의미다.
원래 웹에서는 동일 출처 내에서만 리소스를 공유하게 하는 SOP(Single Origin Policy)에 따라 보안을 유지했다. 그러나 웹 기술이 발전함에 따라 다른 출처로 요청을 주고받을 일이 많아졌고, 서로 다른 출처끼리도 리소르를 공유하도록 허용하는 CORS 정책이 도입됐다.
출처(Origin)은 ① scheme, ② host, ③ port 로 이루어진 도메인을 의미한다. (IE의 경우 port를 비교하지 않음)
<https://www.naver.com/>
① scheme : https
② host: www.naver.com
③ port: null (공개되지 않음)
현재 자신이 속한 출처(Origin)를 기준으로 다른 출처(Origin)에 API를 요청하게 되면 브라우저에서 이 요청으로 넘어오는 경과가 안전한지 판단하게 되는데, 응답을 보내는 출처가 자신이 속한 출처가 아닌, 다른 출처여도 서로 예상되는 출처라면 요청에 대해 허용해주는 응답 헤더를 보내, 브라우저가 응답 결과를 보여준다.
CORS 동작 방식
단순 요청 (Simple Requests)
다음과 같은 조건을 모두 만족하는 요청은 CORS 프리플라이트를 트리거하지 않는 단순 요청으로 처리된다.
- GET, POST, HEAD 메서드를 사용
- Accept, Accept-Language, Content-Language, Content-Type 헤더
- Content-Type 는 application/x-www-form-urlencoded, multipart/form-data, text/plain 이 세가지 값만 허용된다.
프리플라이트 요청 (Preflight Requests)
프리플라이트 요청은 먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인한 뒤 본요청을 보낸다. 다음은 프리플라이트 요청/응답의 사례다.
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: <http://foo.example>
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: <https://foo.example>
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
- 프리플라이트 요청
- Origin 헤더를 포함한다.
- Access-Control-Request-Method 본요청의 메서드를 명시한다.
- Access-Control-Allow-Headers 본요청의 헤더를 명시한다.
- 프리플라이트 응답
- Access-Control-Allow-Methods 리소스를 쿼리하는 데 사용할 수 있는 메서드를 알려준다.
- Access-Control-Allow-Headers 본요청에서 사용할 수 있는 헤더를 알려준다.
- Access-Control-Max-Age 프리플라이트 요청에 대한 응답을 캐시할 수 있는 시간(초)을 알려준다.
이처럼 프리플라이트 요청과 응답이 완료되면 본요청이 전송된다.
인증 정보를 포함한 요청 (Credentialed Requests)
쿠키, 토큰과 같이 사용자 식별 정보가 담긴 요청에 대해서는 조금 더 엄격하게 처리한다. 클라이언트는 요청을 보낼 때 credentials 옵션을 별도로 설정해줘야한다.
- same-origin : 같은 출처 간 요청에만 인증 정보를 담을 수 있다.
- include : 모든 요청에 인증 정보를 담을 수 있다.
- omit : 모든 요청에 인증 정보를 담지 않는다.
서버는 응답할 때 Access-Control-Allow-Credentials 라는 헤더를 true 로 설정해줘야한다. 이때, Access-Control-Allow-Origin 은 와일드카드가 될 수 없으며, 명확한 출처를 명시해줘야한다.
CORS 오류 해결법
Access-Control-Allow-Origin 응답 헤더 세팅
- 서버측 응답에서 접근 권한을 주는 헤더를 추가하여 해결
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*"); // 모든 도메인
res.header("Access-Control-Allow-Origin", "<https://example.com>"); // 특정 도메인
});
cors 모듈 사용
const cors = require("cors");
const app = express();
app.use(cors());
아무 옵션없이 설정하면 모든 cross-origin 요청에 대해 응답이 이뤄지므로, 특정 도메인이나 특정 요청에만 응답하게 옵션을 설정하는 것이 좋다.
webpack-dev-server proxy 기능
- 리액트 개발환경에서, 서버쪽 코드를 수정하지 않고 해결할 수도 있다. 아래와 같이 프록시 속성을 설정하면 CORS 설정을 우회할 수 있다.
module.exports = {
devServer: {
proxy: {
"/api": {
target: "domain.com",
changeOrigin: true,
},
},
},
};
참고 자료
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
https://hudi.blog/sop-and-cors/
'CS' 카테고리의 다른 글
[OS] 메모리 공간, 스택 영역, 힙 영역 (0) | 2023.09.02 |
---|---|
[OS] 프로세스, 스레드 (0) | 2023.08.28 |
[Network] HTTP, HTTPS (0) | 2023.08.16 |
네트워크 기초 이론 무지성 정리 2 (4) | 2023.04.20 |
네트워크 기초 이론 무지성 정리 (2) | 2023.03.28 |