W3-1 SOP와 CORS

hyuun01·2022년 9월 28일
0

WebHacking

목록 보기
4/12
post-thumbnail

0. 키워드 정리

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

: 현재 페이지의 출처가 아닌 다른 출처로부터 온 데이터를 읽지 못하게 하는 브라우저의 보안 메커니즘

Same Origin

: 현재 페이지와 동일한 출처

Cross Origin

: 현재 페이지와 다른 출처

CORS(Cross Origin Resource Sharing; 교차 출처 리소스 공유)

: SOP의 제한을 받지 않고 Cross Origin의 데이터를 처리 할 수 있도록 해주는 메커니즘



1. SOP(Same Origin Policy; 동일 출처 정책)

: 현재 페이지의 출처가 아닌 다른 출처로부터 온 데이터를 읽지 못하게 하는 브라우저의 보안 메커니즘

1) 도입 배경

  • 쿠키는 브라우저 내부에 보관됨
  • 쿠키는 웹 서비스 접속(직/간접적 접근) 시 자동으로 HTTP 요청의 헤더에 포함되어 브라우저에 전달됨
  • 악의적으로 클라이언트 권한 이용해 대상 사이트에 HTTP 요청 보내고 HTTP 응답 정보 획득 가능
  • 정보 유출 위협
    -> 가져온 데이터를 악의적인 페이지에서 읽을 수 없도록 하자!

2) Origin

  • 브라우저가 가져온 정보의 출처
  • 프로토콜(Protocol, Scheme), 포트, 호스트로 구성됨
  • 구성요소가 모두 일치해야 동일한 오리진

1> Same Origin

: 현재 페이지와 동일한 출처

2> Cross Origin

: 현재 페이지와 다른 출처


ex) https://same-origin.com/

1) https://same-origin.com/frame.html
: Same Origin(Path만 다름)

2) http://same-origin.com/frame.html
: Cross Origin(Scheme이 다름)

3) https://cross.same-origin.com/frame.html/
: Cross Origin(Host가 다름)

4) https://same-origin.com:1234/
: Cross Origin(Prot가 다름)


3) SOP 실습

window.open
: 새로운 창 띄우는 함수

object.location.href
:객체가 가리키고 있는 URL 주소를 읽어오는 코드

실습1> Same Origin

  • Same Origin일 때는 데이터 읽고 쓰는데 문제 없음

실습2> Cross Origin 데이터 읽기 : 오류 발생

  • 외부 출처에서 불러온 데이터를 읽으려고 할 때 오류 발생(못 읽음)

실습3> Cross Origin 데이터 쓰기

  • 데이터를 쓸 때는 오류 발생하지 않음





2. CORS(Cross Origin Resource Sharing; 교차 출처 리소스 공유)

  • 교차 출처의 자원을 공유
  • 방법1) CORS와 관련된 HTTP 헤더를 추가하여 전송
  • 방법2) JSONP(JSON with Padding)

1) 도입 배경

  • SOP 완화하여 다른 출처의 데이터를 처리해야 하는 경우
  • ex1)
    이미지, 자바스크립트, CSS 등의 리소스는 SOP에 구애 받지 않고 외부 출처에 대해 접근 허용
  • ex2)
    특정 포털 사이트가 카페, 블로그, 메일 서비스를 다음과 같이 운영하는 경우
    https://cafe.sitename.com : 카페
    https://blog.sitename.com : 블로그
    https://mail.sitename.com : 메일
    https://sitename.com : 메인
    -> 사용자가 받은 메일 개수를 메인에 출력하려면
    -> SOP 적용받지 않고 리소스 공유할 방법 필요
    => CORS(Cross Origin Resource Sharing; 교차 출처 리소스 공유)

2) CORS

  • HTTP 헤더 기반으로 Cross Origin 간에 리소스를 공유하는 방법
  • 발신 측에서 CORS 헤더를 설정해 요청
    -> 수신 측에서 헤더를 구분해 정해진 규칙에 맞게 데이터 가져갈 수 있도록 설정

3) CORS preflight

  • CORS 헤더 방식에서 HTTP 메소드 중 OPTIONS를 통해 수신측 웹 리소스의 접근 관련 질의를 하는 과정

  • CORS preflight 이후 :
    1> 브라우저가 수신측의 응답이 발신측의 요청과 상응하는지 확인
    2> POST 요청 보냄 : 수신 측의 웹 리소스를 요청하는 HTTP 요청을 보냄

CORS preflight 예시

웹 리소스 요청 코드 : 발신측

/*
    XMLHttpRequest 객체를 생성합니다. 
    XMLHttpRequest는 웹 브라우저와 웹 서버 간에 데이터 전송을
    도와주는 객체 입니다. 이를 통해 HTTP 요청을 보낼 수 있습니다.
*/
xhr = new XMLHttpRequest();

/* https://theori.io/whoami 페이지에 POST 요청을 보내도록 합니다. */
xhr.open('POST', 'https://theori.io/whoami');

/* HTTP 요청을 보낼 때, 쿠키 정보도 함께 사용하도록 해줍니다. */
xhr.withCredentials = true;

/* HTTP Body를 JSON 형태로 보낼 것이라고 수신측에 알려줍니다. */
xhr.setRequestHeader('Content-Type', 'application/json');

/* xhr 객체를 통해 HTTP 요청을 실행합니다. */
xhr.send("{'data':'WhoAmI'}");

1> 발신측의 HTTP 요청

OPTIONS /whoami HTTP/1.1
Host: theori.io
Connection: keep-alive
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: https://dreamhack.io
Accept: */*
Referer: https://dreamhack.io/
  • Access-Control-Request-Method : 메소드를 추가적으로 사용할 수 있는지 질의
  • Access-Control-Request-Headers : 헤더를 추가적으로 사용할 수 있는지 질의

2> 서버의 응답

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://dreamhack.io
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type
Header 설명
Access-Control-Allow-Origin 헤더 값에 해당하는 Origin에서 들어오는 요청만 처리
Access-Control-Allow-Methods 헤더 값에 해당하는 메소드의 요청만 처리
Access-Control-Allow-Credentials 쿠키 사용 여부를 판단
: 예시의 경우 쿠키의 사용을 허용
Access-Control-Allow-Headers 헤더 값에 해당하는 헤더의 사용 가능 여부를 나타냄


3. CORS 기법

1) post message

: 메시지를 주고받기 위한 이벤트 핸들러를 이용해 리소스를 공유

ex)

  • https://dreamhack.io
var testWindow = window.open('http://test.dreamhack.io');
window.addEventListener('message', function (e) {
  if (e.origin === 'http://test.dreamhack.io') {
    console.log(e.data); // 'bye'
  }
});
testWindow.postMessage('hello', 'http://test.dreamhack.io');
  • http://test.dreamhack.io
window.addEventListener('message', function (e) {
  if (e.origin === 'https://dreamhack.io') {
    console.log(e.data); // 'hello'
    e.source.postMessage('bye', 'https://dreamhack.io');
  }
});

2) CORS 헤더 사용

: 다른 오리진이 허용하는 설정 등을 HTTP 헤더를 통해 확인한 후 허용하는 요청을 보내 리소스를 공유

CORS preflight 예시

https://dreamhack.io에서 https://test.dreamhack.io/me 에 대해 요청
-> HTTP Request에서 OPTIONS헤더를 통해 메소드, 헤더 등이 허용하는지 확인

  • https://dreamhack.io
xhr = new XMLHttpRequest();
xhr.open('POST', 'https://test.dreamhack.io/me');
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send("{'data':'hello world!'}");
  • HTTP Request
OPTIONS /me HTTP/1.1
Host: test.dreamhack.io
Connection: keep-alive
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: https://dreamhack.io
Accept: */*
Referer: https://dreamhack.io/
  • HTTP Response
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://dreamhack.io
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type

3) JSONP(JSON with Padding)

: 스크립트 태그를 통해 다른 오리진의 리소스를 요청하고, 응답 데이터를 현재 오리진의 Callback 함수에서 다루는 방식으로 리소스를 공유
: 스크립트 태그를 통해 외부 자바스크립트 코드를 호출하면 현재 오리진에서 해당 코드가 실행된다는 점을 이용한 방식

  • CORS 생기기 전 사용하던 방법
  • 현재는 거의 사용 안 함

이미지, 자바스크립트, CSS 등의 리소스는 SOP에 구애 받지 않고 외부 출처에 대해 접근을 허용
-> <script> 태그로 Cross Origin의 데이터를 불러옴

<script> 태그 내에서는 데이터를 자바스크립트 코드로 인식
-> Callback 함수! 활용해야

<script> 태그와 callback함수를 이용

  • Cross Origin에 요청시,
    callback 인자에 어떤 함수로 받아오는 데이터를 핸들링할지 넘김

  • 대상 서버는 전달된 Callback으로 데이터 감싸 응답


예시 1)
  • https://dreamhack.io
<script>
function myCallback(data){ 
	console.log(data.id)  // 'dreamhack'
}
</script>
<script src='http://test.dreamhack.io/me?callback=myCallback'></script>
  • http://test.dreamhack.io/me?callback=myCallback
myCallback({'uid':1, 'id':'dreamhack', 'pw':'********'});
예시2)

웹 리소스 요청코드

<script>
/* myCallback이라는 콜백 함수를 지정합니다. */
function myCallback(data){
    /* 전달받은 인자에서 id를 콘솔에 출력합니다.*/
	console.log(data.id)
}
</script>
<!--
https://theori.io의 스크립트를 로드하는 HTML 코드입니다., callback이라는 이름의 파라미터를 myCallback으로 지정함으로써
수신측에게 myCallback 함수를 사용해 수신받겠다고 알립니다.
-->
<script src='http://theori.io/whoami?callback=myCallback'></script>

웹 리소스 요청에 따른 응답코드

/*
수신측은 myCallback 이라는 함수를 통해 요청측에 데이터를 전달합니다.
전달할 데이터는 현재 theori.io에서 클라이언트가 사용 중인 계정 정보인
{'id': 'dreamhack'} 입니다. 
*/
myCallback({'id':'dreamhack'});

0개의 댓글