Post

Same-origin Policy, CORS

Same-origin Policy와 CORS

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를 잘못 설정하는 경우?

  1. account같이 개인 정보를 포함하고 있는 페이지인데, 서버에서 Access-Control-Allow-Origin: \* 로 설정하는 경우.

이러면 어떤 악의적인 Origin에서 ajax 요청해서 개인정보를 받아갈 수 있음.

  1. 또는 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.