Cross-Origin Resource Sharing(CORS)

Bindung·2020년 5월 6일
1

http

목록 보기
1/1
post-thumbnail

CORS는 Cross Origin Resource Sharing의 약자입니다.
최근 API를 가지고 데이터를 불러오는 경우가 생겼다.
도메인을 넣어주고 데이터타입을 넣어주고 DOM에 뿌리면 되겠지 싶었는데.
데이터가 나타나지 않는게 아닌가? 무엇이 문제인가 싶어서 개발자도구에서 확인해보니

Access to XMLHttpRequest at '(api url로 가립니다.)' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

위와 같은 내용이 있는게 아닌가?
CORS policy 라는 걸 보고 찾아보기 시작했다.

CORS Policy

과거에도 분명 이 부분에 대해서 모든 개발자분들이 겪었을 것이다.
API가 많아지고 다른 도메인에서 데이터를 요청하여 가지고 오는 사례가 많아지면서 CORS에 대해서 사람들이 많이 찾아보는 것 같다.

웹에서는 기본적으로 서로 다른 도메인에서 데이터를 가지고 오는 것은 보안에 위협이 되어서 권장하지 않았다.
하지만 서로 다른 도메인에서 데이터를 가지고 오게 생기면서 그 루트를 만들어준 것이 CORS이다.

하지만 그 루트를 사용하기 위해서는 조건이 필요하다.

CORS는 암구호

웹에서는 서로 다른 도메인의 접근을 허용하지 않기에 CORS 루트를 통과하기 위해서는 인증 절차라는 것을 거쳐야한다.

그 인증절차를 비유하자면 암구호와 비슷하다.

암구호는 부대내에서 아군과 아군사이에 비밀스럽게 암호화한 내용이다.

밤이 되어서 보이질 않지만 인기척이나 사람의 형태가 발각되었을 때 암구호를 외쳐 피아식별을 한다.

CORS도 그렇다. 피아식별이 되질 않기 때문에 피아식별을 위해서 암구호처럼 몇몇 인증조건이 필요하다. 암구호처럼 그냥 암호만 대는것이 아닌 우리서버와 상대서버 간의 인증을 해야하는 부분이 생긴다.

요청과 응답

우리는 상상해볼 수 있다.
나는 웹사이트를 하나만들고 거기에 위젯을 하나 붙이고 싶다.
네이버 검색어 순위를 알려주는 위젯을 넣고 싶다.
그러면 네이버 API를 먼저 찾아보고 연결할 것이다.

그러면 위처럼 서버에서 API서버로 라우터를 연결하여 데이터를 가져오고 브라우저에서 요청한것을 응답하는 구조이다.
어차피 브라우저로 보낼꺼면 바로 보내면 안돼? 라는 생각을 하게 된다.

위 이미지처럼 생각할 수 있다. 나도 그렇게 해서 CORS 정책 위반이라고 에러창이 떳다.
그러면 암구호를 우리는 설정해주어야한다.
그게 CORS 정책이다.

CORS Request 종류

  1. Simple Request
  2. Preflight Request
  3. Request with Credential
  4. Request without Credential

CORS 요청은 Simple/Preflight, Credential/Non-Credential 의 조합으로 총 4가지가 있다.

Simple Request

CORS를 암구호에 비유했는데. 좀더 자세히 뜯어보면 4가지중에 Simple Request는 낮에 아파트안에 들어오는 자동차와 같다.
등록되지 않는 자동차이면 아파트 앞 자동문이 열리지 않는다. 하지만 등록되어 있는 차번호이면 가까이만 가면 열린다.
이것이 Simple Request 이다.

  • GET, HEAD, POST 중의 한 가지 방식을 사용
  • POST 방식일 경우 Content-type이 아래 셋 중의 하나여야 한다.
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain
  • 커스텀 헤더를 전송하지 말아야 한다.

이렇게 되면 내가 서버에 1번 요청하고 1번 응답 받는 것으로 마무리 된다.

==Request==

GET /resources/public-data/ HTTP/1.1
Host: mydomain.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,\*;q=0.7
Connection: keep-alive
Referer: http://example/examples/access-control/simpleXSInvocation.html
Origin: http://example

==Response==

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

Preflight Request

Preflight Request는 암구호와 비슷하다.
위의 Simple Request 조건이 해당하지 않으면 Preflight Request로 인증을 받아야한다.

  • GET, HEAD, POST 외의 다른 방식으로도 요청을 보낼 수 있다.
  • application/xml 처럼 다른 Content-type으로 요청을 보낼 수 있다.
  • 커스텀 헤더도 사용할 수 있다.

==Preflight==

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,\*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,\*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

Request with Credential

헤더 정보중에 http cookie와 Authentication 정보를 인식할 수 있게 하는 요청이다.
헤더의 Access-Control-Allow-Origin에는 *가 들어가면 안된다.
구체적인 도메인이 들어가야한다.

Request without Credential

기본적으로 CORS 요청은 Non-Credential 요청이다.
withCredentials가 true로 지정하지 않으면 보통은 Non-Credential 상태이다.

뒷이야기

이번에 생긴 이슈는 프로젝트중에 겪은 이슈이며, 사용법이나 설명서는 어디서든 찾으면 나온다.
하지만 이런 지식이나 정보없이 만약 CORS 이슈를 사용법으로만 풀려고한다면 나중에 이런 이슈가 또 발생했을때. 언어가 다르거나 상황이 달랐을 때 다른방법으로 푼다는 지 생각을 할 수 있을까?
이 작은 지식이 나중에 조금이나마 도움이 될 것이라고 본다.

참고

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://velog.io/@leejh3224/CORS-Real-examples-8yjnloovl5

profile
포기하지말고 천천히...

0개의 댓글