Same-origin Policy, CORS
Same-origin Policy와 CORS
https://en.wikipedia.org/wiki/Same-origin_policy
- 영문 위키가 젤잘나와있음. 시나리오까지.
Same-origin Policy란?
- 스크립트에 적용되는 정책이며, 스크립트 내에서 다른 리소스, 데이터를 요청할 때 요청지와 데이터 출처가 Same-origin이 아니면 막는 정책
- 현재 스크립트를 실행하고 있는 주소와 (프로토콜, 호스트, 포트)가 동일한 주소를 Same-origin으로 판단한다.
- 이는 브라우저가
document.domain
을 보고 판단함.
- 이는 브라우저가
좀 더 정확히는, Cross-origin read를 막는다.
- https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy#교차_출처_네트워크_접근
- 교차 출처 쓰기는 보통 허용합니다.
- Cross-origin으로의 링크, 리다이렉트, form submit 등
- 교차 출처 삽입은 보통 허용합니다.
- HTML 태그로 가져오는 항목(
- 교차 출처 읽기는 보통 불허 (ajax 등)
- 하지만, 종종 교차 출처 삽입 과정에서 읽기 권한이 누출됩니다.
- 예를 들면 삽입한 이미지의 크기나 삽입한
<script>
의 행동 등을 읽을 수 있습니다.- (*그래서 JSONP로 우회가 가능한 것)
- 교차 출처 쓰기는 보통 허용합니다.
왜 script에서 Cross-origin read를 막는가?
- 단순히 Cross-origin으로 데이터 보내는걸 막고자 했다면 form submit도 막았어야 한다.
- 마찬가지로 Cross-origin에서 데이터 가져오는 것 자체를 막고자 했다면, submit이나 삽입도 막았어야 한다.
그렇다면 왜 읽기만 불허할까?
- 읽기를 불허한다는건, script 내에서 ajax 등을 이용해서 페이지 이동 없이 Cross-origin에서 데이터 받는 것을 막겠다 . 라는 의미임.
- 사실은 브라우저가 도메인 별로 쿠키를 관리하고, 해당 도메인에 접근 시 맞는 쿠키를 알아서 꺼내준다는 점과 관련이 있다.
생각해보면 다음과 같은 시나리오가 가능함.
- 상황
- 내가 1탭에는 구글을, 2탭에는 악성사이트를 띄워 둔 상태.
- 2탭의 악성사이트 js에서는 접속자의 구글 계정 정보를 알아내고 싶어 한다.
- 구글 계정 정보는 www.google.com/account로 요청을 보내야 얻을 수 있음.
- 2탭의 js에서 form submit 요청을 보낸다면? www.google.com/account로 요청을 보낼 수는 있겠지만, 해당 정보를 악성사이트 개발자가 가져오지는 못한다. submit 하면서 페이지 이동이 발생하니 단순히 브라우저 상에서 유저가 클릭해서 들어간 것과 별 차이가 없으니까. 공격으로서 아무런 의미가 없다.
- 반면 Same-origin Policy가 없어서 www.google.com/account로 ajax 요청이 가능하다고 가정해보자.
- 2탭의 js에서 ajax로 www.google.com/account로 요청 보내면, 해당 도메인의 세션(쿠키)가 요청에 포함되어 전달되기 때문에 1탭에서 접근한 것과 동일하게 2탭에서 구글 계정 정보를 가져올 수 있다. 가져온 정보를 악성사이트로 submit하거나 또 다시 ajax로 보내는 것이 가능하다.
즉, 악성사이트가 정상사이트의 유저 세션(쿠키)을 이용해 정상사이트로부터 중요 정보를 획득하는 것을 막기 위해서
js 내에서 Cross-origin으로 read 요청을 보내는 것을 브라우저단에서 막아주는 것이 Same-origin policy.
라고 정리할 수 있다.
또는 이런 시나리오가 가능
- 악성사이트에서 www.google.com/account에 대한 팝업을 생성
- 구글의 window, location, document에 마음대로 접근 가능하다면, DOM에 접근해서 민감한 정보를 알아낼 수 있음.
- 그래서 parent에서 child_popup.location 같은 변수에 접근하는 것은 불가능하다. (Cross-origin Read Block)
그럼 Cross-origin read가 필요한 경우 어떻게 해결할 수 있는가?
Cross-Origin Resource Sharing ( CORS ) 설정
- CORS(Cross-Origin Resource Sharing)는 서로 다른 Origin 에서 발생한 리소스 요청이라도, HTTP 응답 헤더 중 Access-Control-Allow-Origin 값으로 내려주는 도메인과 일치하는 경우 브라우저에서 해당 도메인에서 리소스를 이용할 수 있도록 허용한다.
- 따라서 서버가 “특정 리소스에 대한 요청은 CORS 헤더를 추가하겠다.” 라고 설정해주어야 함.
- https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
- https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/cors.html
CORS를 잘못 설정하는 경우?
- account같이 개인 정보를 포함하고 있는 페이지인데, 서버에서
Access-Control-Allow-Origin: \*
로 설정하는 경우.
이러면 어떤 악의적인 Origin에서 ajax 요청해서 개인정보를 받아갈 수 있음.
- 또는
Origin = Access-Control-Allow-Origin
단순히 이렇게 설정하는 경우. 역시 위와 같이 악의적인 Origin에서 ajax 요청했을 때, ACAO 헤더에 악의적인 Origin 도메인이 들어가므로 브라우저가 받아들임.
클라이언트 사이드에서 JSONP
- 가져오는 데이터가 JSON이어야만 한다는 제약이 있기는 함
- 흔히 사용하는
<script src=" (.js OR .json)"></script>
가 바로 JSONP다. - jQuery는
$.ajax({dataType: 'jsonp'..
로 XHR 리퀘스트 형식처럼 사용할 수 있도록 지원한다. - front / back 둘 다 개발한다면 CORS를 사용하는 편이 더 낫겠지. 이건 약간 기믹같은 느낌이 들어서
기타
클라이언트가 직접 타 도메인에서 받아오는게 아니라, 서버 사이드에서 대신 타 도메인에 접근해 데이터를 받아온 다음, 이를 클라이언트에게 내려주는 방식도 있음.
기타 등등! https://en.wikipedia.org/wiki/Same-origin_policy#Relaxing_the_same-origin_policy
Cross-origin 접근 막기
https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy#교차_출처_접근_막기
- 교차 출처 쓰기를 방지하려면 추측 불가능한 토큰을 요청에 포함하세요. 교차 출처 요청 위조(Cross-Site Request Forgery, CSRF) 토큰이라고 부릅니다. 토큰을 요구하는 페이지의 교차 출처 읽기도 막아야 합니다.
- 리소스의 교차 출처 읽기를 방지하려면 삽입 불가하도록 설정하세요. 리소스 삽입 과정에서 일부 정보가 새어 나가므로 방지해야 합니다.
- 교차 출처 삽입을 방지하려면, 리소스가 위에 나열한 삽입 가능 형태로 읽히지 않도록 해야 합니다. 브라우저는 Content-Type 헤더를 준수하지 않을 수도 있습니다. 즉,
This post is licensed under CC BY 4.0 by the author.