Mitigation: Same Origin Policy

강혜인·2024년 4월 5일
0

Security

목록 보기
3/12

Same Origin Policy(SOP)


브라우저는 인증 정보로 사용될 수 있는 쿠키를 브라우저 내부에서 보관함

이용자가 웹 서비스에 접속할 때, 브라우저는 해당 웹 서비스에서 사용하는 인증 정보인 쿠키를 HTTP 요청에 포함시켜 전달함

브라우저는 타 사이트에 접근할 때도 인증 정보인 쿠키를 함께 전송하는 특징을 가짐

→ 악의적인 페이지가 클라이언트의 권한을 이용해 대상 사이트에 HTTP 요청을 보내고, HTTP 응답 정보를 획득 하는 코드를 실행할 수 있음

→ 클라이언트 입장에서는 가져온 데이터를 악의적인 페이지에서 읽을 수 없도록 해야함 ⇒ 브라우저의 보안 메커니즘인 동일 출처 정책(Same Origin Policy, SOP)

Same Origin Policy의 Origin 구별 방법


오리진은 프로토콜(Protocol, Scheme), 포트(Port), 호스트(Host)로 구성됨

구성 요소가 모두 일치해야 동일한 오리진임

ex) https://same-origin.com/ 라는 오리진과 아래 URL 비교

URL결과이유
https://same-origin.com/frame.htmlSame OrigignPath만 다름
http://same-origin.com/frame.htmlCross OriginScheme이 다름
https://cross.same-origin.com/frame.htmlCross OriginHost가 다름
https://same-origin.com:1234/Cross OriginPort가 다름

Same Origin Policy 실습


SOP는 Cross Origin이 아닌 Same Origin일 때만 정보를 읽을 수 있도록 해줌

[window.open](http://window.open) → 새로운 창을 띄우는 함수

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

Same Origin Policy(SOP)데모


<-!-- iframe 객체 생성 -->
// 현재 웹 페이지 안에 또 다른 하나의 웹 페이지를 삽입하는 HTML 태그
// src 요소를 설정함으로써 삽입할 웹 페이지의 주소가 결정됨
<iframe src=""
id="my-frame">
</iframe>

<-!-- Javascript 시작 -->
<script>
/* 2번째 줄의 iframe 객체를 myFrame 변수에 가져옴 */
let myFrame = document.getElementById('my-frame')

/* iframe 객체에 주소가 로드되는 경우 아래와 같은 코드를 실행함. */
// onload는 이벤트 핸들러로써, 해당 객체가 성공적으로 로드되었을 때 동작함
myFrame.onload = () => {
	/* try... catch는 에러를 처리하는 로직 */
	try {
		/* 로드가 완료되면, secret-element 객체의 내용을 콘솔에 출력 */

// 로드가 완료되면 iframe내에 삽입된 주소에서 secret-element 객체의 값인 treasure를 읽어와 콘솔에 출력하는 동작을 수행함
			let
secretValue = myFrame.contentWindow.document.getElementByID('secret-element').innerText;

console.log({secretValue});
	}
catch(error){
	/* 오류 발생 시 콘솔에 오류 로그 출력 */
console.log({error});
	}
}

/* iframe 객체에 Same Origin, Cross Origin 주소를 로드하는 함수 */
const
loadSameOrigin = () => {
myFrame.src =
'https://same-origin.com/frame.html';}
const
loadCrossOrigin = () => {
myFrame.src = 
'https://cross-origin.com/frame.html';}
</script>

<-!-- 버튼 2생성(Same Origin 버튼, Cross Origin 버튼) -->
<button
onclick=loadSameOrigin()>SameOrigin</button>
<br>
<button
onclick=loadCrossOrigin()>CrossOrigin</button>

<-!--
frame.html의 코드가 아래와 같다. secret-element라는 id를 가진 div 객체 안에 treasure라고 하는 비밀 값을 넣어두었다.
-->
<div id="secret-element">treasure</div>

Same Origin Policy 제한 완화


SOP는 클라이언트 사이드 웹 보안에서 중요한 요소지만, 브라우저가 이러한 SOP에 구애 받지 않고 외부 출처에 대한 접근을 허용해주는 경우가 존재함

이미지나 자바스크립트, CSS 등의 리소스를 불러오는 <img>, <style>, <script> 등의 태그는 SOP의 영향을 받지 않음

웹 서비스에서 동일 출처 정책인 SOP를 완화해 다른 출처의 데이터를 처리 해야 하는 경우도 존재함

→ 특정 포털 사이트가 카페, 블로그, 메일 서비스를 아래의 주소로 운영하고 있다고 할 때, 각 서비스의 Host가 다르기 때문에 브라우저는 각 사이트의 origin이 다르다고 인식함

이러한 환경에서 이용자가 수신한 메일의 개수를 메인 페이지에 출력하려면, 개발자는 메인 페이지에서 메일 서비스에 관련된 리소스를 요청하도록 해야함.

이때, 두 사이트는 origin이 다르므로 SOP를 적용받지 않고 리소스를 공유 할 방법이 필요함

⇒ 그 방법이 교차 출처 리소스 공유(Cross Origin Resource Sharing, CORS)

CORS와 관련된 HTTP 헤더를 추가해 전송하는 방법을 사용

+) JSON with Padding(JSONP) 방법을 통해 CORS 대체 가능

Cross Origin Resource Sharing(CORS)


HTTP 헤더에 기반해 Cross Origin간에 리소스를 공유하는 방법

발신측에서 CORS 헤더를 설정해 요청하면, 수신측에서 헤더를 구분해 정해진 규칙에 맞게 데이터를 가져갈 수 있도록 설정

/* 웹 리소스 요청 코드 */
/*
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'}");
/* 발신측의 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/
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헤더 값에 해당하는 Method의 요청만 처리
Access-Control-Allow-Credentials쿠키 사용 여부를 판단한다.
예시의 경우 쿠키의 사용을 허용한다.
Access-Control-Allow-Headers헤더 값에 해당하는 헤더의 사용 가능 여부를 나타냄

브라우저는 수신측의 응답이 발신측의 요청과 상응하는지 확인하고, 그때야 비로소 POST 요청을 보내 수신측의 웹 리소스를 요청하는 HTTP 요청을 보냄

JSON with Padding(JSONP)


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

그러나 함수를 활용해야 함

Cross Origin에 요청할 때 callback 파라미터에 어떤 함수로 받아오는 데이터를 핸들링할지 넘겨주면, 대상 서버는 전달된 Callback으로 데이터를 감싸 응답함

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

// Cross Origin의 데이터를 불러옴 + callback 파라미터로 myCallback을 함께 전달함
// Cross Origin에서는 응답 할 데이터를 myCallback으로 감싸 Javascript 코드를 반환해줌
// 반환된 코드는 요청측에서 실행되기 때문에 3~6번 줄에서 정의된 myCallback 함수가 전달된 데이터를 읽을 수 있음
<script src='http://theori.io/whoami?callback=myCallback'></script>
/*
수신측은 myCallback 이라는 함수를 통해 요청측에 데이터를 전달함
전달 할 데이터는 현재 theori.io에서 클라이언트가 사용 중인 계정 정보인
{'id' : 'dreamhack'}임
*/
myCallback({'id' : 'dreamhack'});

다만, JSONP는 CORS가 생기기 전에 사용하던 방법으로 현재는 거의 사용하지 않는 추세이기 때문에, 새롭게 코드를 작성할 때에는 CORS를 사용해야 함

💡 `Same Origin Policy(SOP)` : 동일 출처 정책, 현재 페이지의 출처가 아닌 다른 출처로부터 온 데이터를 읽지 못하게 하는 브라우저의 보안 메커니즘

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

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

Cross Origin Resource Sharing(CORS) : 교차 출처 리소스 공유, SOP의 제한을 받지 않고 Cross Origin의 데이터를 처리할 수 있도록 해주는 메커니즘

0개의 댓글