티스토리 뷰

최근 빗썸 Open API를 이용하여 가상화폐에 대한 데이터를 가져오려고 하고 있다.

"https://api.bithumb.com/public/ticker/all_KRW" url을 get으로 요청을 보냈는데 아래와 같은 오류를 만났다.

 CORS정책에 의해 block이 되었다고 하며, request의 credentials mode가 include 일 경우 'Access-Control-Allow-Origin' header는 wildcard('*')가 되면 안된다는 오류 내용이다.

 사실 CORS 문제는 이전에도 부딪친 적이 있다.

개인 프로젝트로 구축한 nest서버와 react application간 통신을 할때 CORS문제를 마주한 적이 있었다.

https://docs.nestjs.com/security/cors

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 nest에서 이러한 cors에 대한 설정 방법을 제공하고 있고, 공식문서의 내용을 따라서 간단한 설정만으로 해결하여 넘어간 적은 있다. 오늘 다시한번 CORS에 대한 문제를 직면했고, CORS에 대하여 명확히 짚고 넘어가기위해 포스팅을 해야겠다고 생각했다. 

 오늘 확인한 오류를 해결하기 위해 CORS가 무엇인지, 'Access-Control-Allow-Origin' header가 무엇인지 알아보고자 한다.

 

SOP(Same Origin Policy, 동일 출처 정책)

 - 어떤 출처(Origin)에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한 방식이다.

 - 잠재적으로 위험한 문서를 분리함으로써 공격받을 수 있는 경로를 줄여준다.

 - 웹 에서의 "출처"란 URL Scheme(프로토콜), host(도메인), port로 정의 됨.

 - 동일 출처의 예제

// 스킴(http)와 호스트(example.com)이 일치하므로 동일 출처이다.
http://example.com/app1/index.html
http://example.com/app2/index.html

// http의 기본 port는 80이므로 동일 출처이다.
http://example.com:80
http://example.com

- 다른 출처의 예제

//다른 스킴(http, https)이므로 다른 출처이다.
http://example.com/app1
https://example.com/app2

//다른 호스트(도메인)이므로 다른 출처이다.
http://example.com
http://www.example.com
http://exam.example.com

//다른 포트이므로 다른 출처이다.
http://example.com
http://example.com:8080

 - SOP로 인하여 Cross Domain이슈가 발생하게 되었다.

 - javascript(XMLHttpRequest)로 다른 웹 페이지에 접근이 되지 않게 되는 것이다.

 - 이러한 문제를 해결하기 위해 CORS가 나왔다.

CORS(Cross Origin Resource Sharing, 교차 출처 자원 공유)

 CORS를 직역하면 교차 출처 리소스 공유이며, 여기서 "교차 출처"란 SOP에서 설명한 "다른 출처"를 의미한다.

이러한 서로 다른 "출처" 간 자원 공유를 위한 기술이 바로 CORS이며, 안전한 교차 출처 request/response를 지원하기 위해 사용된다.

 CORS는 추가 HTTP header를 사용하여 한쪽의 출처에서 실행중인 Web Application이 다른쪽 출처의 차원에 접근할 수 있는 권한을 부여하도록 브라우저에서 알려준다. 브라우저는 2개의 출처가 같은지/다른지에 대해 어떻게 인식할까?

 1. Web browser가 다른 출처의 리소스를 요청할 경우 보통 HTTP Method를 사용하여 요청을 하는데, 이때 브라우저는 Request header의 Origin에 client의 출처를 담아 보낸다.

 2. server가 이 request에 대한 response를 clinet로 보낼 때 header에 Access-Control-Allow-Origin에 접근 허용 출처를 담아서 보낸다.

 3. Web browser는 자신이 request에 보낸 Origin과 response로 받은 Access-Control-Allow-Origin의 값이 같은지 비교하여 유효한 응답인지 아닌지를 결정한다. (Origin === Access-Control-Allow-Origin이면 유효한 응답)

아래에 몇가지 CORS에 대한 예제가 있다.

CORS 시나리오 예제

1. 단순 요청(Simple request)

 - GET, HEAD, POST 중 하나의 Method를 사용하여야 한다.

 - 유저 에이전트가 자동으로 설정한 header 외, 수동으로 설정할 수 있는 header는 오직 Fetch 명세에서 CORS-safelisted request-header로 정의한 header 뿐이다. 즉, "Accept", "Accept-Language", "Content-Langauge", "Content-Type", "DPR", "Save-Data", "Viewport-Width", "Width"만 허용된다.

 - Content-Type header는 "application/x-www-form-urlencoded", "multipart/form-data", "text/plain" 만 허용된다.

 - WebKit Nightly와 Safari Technology Preview는 "Accept", "Accept-Language", "Content-Langauge" 헤더중 "nonstandard"값이 존재할 경우 더이상 simple request로 간주하지 않는 제약조건이 있다.

 - 간단하게 client와 server간 통신을 하고 위에 작성한 헤더를 사용하여 권한처리를 한다.

 

2. Preflight 요청

 - 위의 "단순 요청" 시나리오와는 달리, OPTIONS method를 통해 다른 도메인의 리소스로 HTTP 요청을 보내 실제 요청이 전송하기 안전한지 확인한다.

 - Cross-site요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와 같이 "미리 전송(preflighted)" 해야 한다.

 - OPTIONS method로 통신(request, response)을 수행한다.

 - OPTIONS method의 통신에서 request의 Origin header와 response의 Access-Control-Allow-Origin header를 비교하여 맞을 경우 실제 통신을 진행 한다. 두 값이 다를 경우에는 CORS Block 처리한다.

 - CORS는 Browser에서 Block하기 때문에 server에서는 200코드를 반환한다.

 

3. 인증을 포함한 요청(Credential Request)

 - 기본적으로 XMLHttpRequest객체나 Fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 header를 함부로 Request에 담지 않는다. 이때 요청에 인증과 관련된 정보를 담을 수 있게 해주는 옵션이 credential 옵션이다.

 - credential옵션에는 3가지 값이 있다.

 - same-origin(기본값) : 같은 출처 간 요청에만 인증정보를 담을 수 있다.

 - include : 모든 요청에 인증 정보를 담을 수 있다.

 - omit : 모든 요청에 인증 정보를 담지 않는다.

 - 만약 credential옵션에 same-origin또는 include를 사용한다면 browser는 검사조건을 더 강화한다.

 - server에서 resposne로 보내는 header에서 Access-Control-Allow-Origin에서는 wildcard를 사용할 수 없으며 명시적인 URL이어야 한다.

 

HTTP Response Header

Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

 - 단일 출처를 지정하여 해당 출처가 리소스에 접근하도록 허용

 - 자격 증명이 없는 경우 wildcard를 사용하여 브라우저의 Origin과 상관없이 모든 리소스에 접근하도록 허용

 - server에서 하나의 origin을 지정할 경우, server는 Vary 응답 헤더에 Origin을 포함하여야 한다.

 

Access-Control-Expose-Headers

Access-Control-Expose-Headers: <header-name>[, <header-name>]*

 - 브라우저가 접근할 수 있는 header를 server의 화이트리스트에 추가할 수 있다.

 

Access-Control-Max-Age

Access-Control-Max-Age: <delta-seconds>

 - Preflight reqeust 결과를 캐시할 수 있는 시간을 나타낸다.

 - delta-seconds는 결과를 캐시할 수 있는시간(초)를 나타낸다.

 

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials: true

 - credentials가 true일 때 요청에 대한 응답을 표시할 수 있는지 나타낸다.

 - Preflight request에 대한 응답의 일부로 사용하는 경우, credentials를 사용하여 실제 요청을 수행할 수 있는지 나타낸다.

 - Simple request일 경우 preflight되지 않으므로 credential이 있는 리소스를 요청하더라도 반환되지 않는다, 브라우저에서 response를 무시하고 웹 컨텐츠로 반환되지 않는다.

 

Access-Control-Allow-Methods

Access-Control-Allow-Methods: <method>[, <method>]*

 - 자원에 접근할 떄 허용되는 method를 지정한다.

 - Preflight request에 대한 응답으로 사용된다.

 

Access-Control-Allow-Headers

Access-Control-Allow-Headers: <header-name>[, <header-name>]*

 - Preflight request에 대한 응답으로 사용된다.

 - 실제 요청 시 사용할 수 있는 HTTP헤더를 담는다.

 

HTTP Reqeust Header

Origin

Origin: <origin>

 - Cross-site 접근 요청 또는 Preflight request의 출처를 나타낸다.

 - 요청이 시작된 곳의 URI 또는 null을 담는다.

 - 접근 제어 요청에는 항상 Origin header가 포함되어 요청을 보낸다.

 

Access-Control-Request-Method

Access-Control-Request-Method: <method>

 - Preflight request할 경우 사용되며, 실제 요청에서 어떤 HTTP method를 사용할 지 서버에 알려준다.

 

Access-Control-Request-Headers

Access-Control-Request-Headers: <field-name>[, <field-name>]*

 - Preflight request할 경우 사용되며, 실제 요청에서 어떤 HTTP header를 사용할지 서버에 알려준다.

 

 

 

=============================================================

 

 

 

지금까지 CORS에 대한 내용을 살펴보았다.

그리고 명확한 답을 얻을 수 있었다.

실제 MDN에 있는 내용이다.

 

그리고 현재 마주한 문제

 

예외 내용에 wildcard가 포함되어 있으면 안된다고 명시가 되어 있다.

그렇다면 위에서 작성한 CORS 시나리오 중 3번째로 작성한 인증을 포함한 요청(Credential Request)에 해당되며,  예외내용 중 "when the requests credentials mode is 'include'."이 있는 것으로 보아 Request의 header에 credential 옵션이 include로 설정되어 있다는 것을 의미한다.

하지만 따로 header를 설정하여 보낸적은 없었다.

header로 어떠한 값을 넣지는 않았다.

http request/response를 할때 axios라이브러리를 사용하고 "axios에 기본적인 설정을 해놓은게 있지 않았나...?"에 까지 생각이 닿았다.

기본 설정을 해놓은 적이 있었고, local에 구축한 서버와 cookie를 포함하여 통신하기 위해 다음과 같이 설정했었다.

requests credentials mode를 include로 기본 설정

이제 명확한 해결방법이 나왔다.

기본설정을 없애거나, open api에 request를 보낼 때 credential option을 변경하는 내용을 header에 포함시키는 것이다.

나는 후자를 선택하였다.

request보낼 때 header에 withCredentials를 false로 변경해놓았다.

결과는....!!

빗썸 api로 부터 데이터를 받아오는데 성공하였다!

 

이제 발뻗고 잘수 있다.

 

참고 : https://developer.mozilla.org/ko/docs/Web/HTTP/CORS

 

교차 출처 리소스 공유 (CORS) - HTTP | MDN

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy

 

동일 출처 정책 - 웹 보안 | MDN

동일 출처 정책(same-origin policy)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로

developer.mozilla.org

https://evan-moon.github.io/2020/05/21/about-cors/

 

CORS는 왜 이렇게 우리를 힘들게 하는걸까?

이번 포스팅에서는 웹 개발자라면 한번쯤은 얻어맞아 봤을 법한 정책에 대한 이야기를 해보려고 한다. 사실 웹 개발을 하다보면 CORS 정책 위반으로 인해 에러가 발생하는 상황은 굉장히 흔해서

evan-moon.github.io

'보안' 카테고리의 다른 글

[웹보안] XSS(Cross-Site Scripting)공격  (0) 2021.02.13
[웹 보안]SQL Injection  (0) 2021.01.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함