SOP와 CORS

2024. 9. 18. 22:35Web Security/웹 해킹

웹 개발 및 해킹을 하다 보면 자꾸 보안 정책을 만나게 된다. 이에 보안을 위한 정책을 설명 드리고자 한다. 다음 설명 드릴 정책들은 모두 브라우저 단에서 적용된다.

 

일단 이러한 정책이 나오게 된 이유는 xss 공격에 대한 고찰로 시작됩니다. xss 공격을 하다 보면 굳이 힘들게 필터링을 우회시켜서 쿠키를 탈취할 필요가 있을까? 라는 생각이 든다. 그리고 아래와 같은 공격 방법을 생각해 낸다.

  • 내가 웹 사이트를 개발 한 뒤 거기에 js 코드를 넣고, 피해자에게 내가 만든 사이트의 링크를 크릭하도록 유도한다. 피해자가 웹 사이트에 접속하면, 브라우저 단에서 나의 js 코드가 실행될 되고, js코드를 통해서 쿠키값을 읽어온다.
  • ifame 을 통해서 다른 사이트(구글 메일)를 child로 가져온다. 이후 parent(해커사이트)에서의 js코드가 child 값을 읽어서 해커의 데이터베이스의 저장한다.

  • 혹은, js 코드를 통해서 얻고 싶은 사이트에 비동기식 통신을 한다. http 통신을 할 때 자동적으로 저장되어 있는 쿠키값이 보내진다. 그렇다면, 서버에서 받아온 값은 로그인 된 상태이다. 받아온 값을 다시 hacker라는 사이트에 보내서 데이터베이스에 저장하면 된다.

 

위와 같은 방식은 SOP 정책이 나오기 전에 실제로 가능한 방법이었다.

 

그렇다면 SOP는 무엇일까??

 

SOP는 same-origin-policy이다. 동일 출처 정책이라고 하는 것인데, 이것은 같은 오리진을 가지고 있지 않다면 상호작용을 브라우저 단에서 막는다는 것이다. 일단 SOP 를 이해하기 위해서 같은 오리진이 무엇인지 알아야 한다.

 

URL은 다음과 같이 구성되어 있다.

 

같은 도메인이란 URL에서 프로토컬, 전체 주소 도메인 네임, 포트가 같아야 한다.

 

호스트와 도메인을 헷갈리는 분들을 위해서 호스트, 도메인, 전체 도메인에 대해서 설명해드리도록 하겠다. 네이버로 예시를 들면, www.naver.com이 전체 주소 도메인 네임 - FQDN (fully qualified domain name)이다. 여기서, www는 호스트 네임이다. naver는 도메인이며, com의 경우 최상위 레벨 도메인이다. 우리가 네트워크 상에서 www.naver.com의 ip를 요구했을 때 naver.com의 네트워크를 찾은 뒤, www를 통해서 네트워크 상의 어느 서버에 접속하는 지 결정한다. 위 설명이 부족하면 DNS 프로토컬을 찾아보는 것을 추천한다.

 

다시 돌아와서, 앞서 우리가 생각해낸 3가지 공격방법을 브라우저의 SOP 정책이 막는다.

 

1.첫 번째 사례에서 해커의 사이트의 JS 코드가 다른 사이트의 쿠키를 탈취하는 것.

 

일단 쿠키는 오리진 마다 하나의 데이터베이스로 관리한다. 네이버와 구글이 다른 데이터베이스를 통해서 쿠키를 저장한다. 다른 오리진의 JS 코드가 쿠키를 읽는 것을 SOP 정책에서 막는다.

 

2.ifame 을 통해서 다른 사이트(구글 메일)를 child로 가져온다. 이후 parent(해커사이트)에서의 js코드가 child 값을 읽어서 해커의 데이터베이스의 저장한다.

 

child 윈도우와 parent 윈도우의 상호작용 막아서 childe의 객체를 읽는 것을 막는다.

 

3.혹은, js 코드를 통해서 얻고 싶은 사이트에 비동기식 통신을 한다. http 통신을 할 때 자동적으로 저장되어 있는 쿠키값이

 

보내진다. 그렇다면, 서버에서 받아온 값은 로그인 된 상태이다. 받아온 값을 다시 hacker라는 사이트에 보내서 데이터베이스에 저장하면 된다.값을 읽어서 저장할 때, 읽는 것을 막는다.

 

이렇듯 SOP 정책은 특정한 상호작용을 막는데, 모두 막는 것이 아니다. 아래는 막는 상호작용과 허용하는 상호작용을 정리한 것이다.

  • cross-origin writes 허용
    • cors-origin writes 이란 비동기식 허용으로 다른 도메인에서 다른 도메인의 서버로 요청을 보내는 것이다.
  • cross-origin embed 허용
    • <scrip src=””> 및 img,video,aoudio, 등등 태그에서 다른 도메인을 src에 넣는 것
    • 다른 도메인을 child 프로세스로 만드는 것, iframe 같은 것
    • css에서 href 등을 통해서 다른 도메인의 css를 가져오는 것
    • 위 예시는 프로그래밍을 해봤다면 자연스럽게 했을 것이다.
  • Cross-origin reads 불가능
    • 다른 도메인의 내용을 브라우저로 받아온 뒤, 그것을 다른 도메인이 읽는 것

위에서 cross-origin-writes 허용을 한다. 이를 악용하는 공격에는 CSRF가 존재한다.

 

다시 돌아와서 프로그래밍을 하다 보면 위에서 SOP가 막아 논 행위를 해야 하는 경우가 있다. 이때 사용하는 기술은 아래와 같으며, 여기서 또한 취약점이 발생한다.

  • post message (ifram의 경우)
  • JSONP
  • CORS 정책

post message

postMessage() 메서드는 Window 객체 간의 안전한 통신방법을 제공한다. 대표적인 사용 예로 새창으로 띄워진 팝업창, 페이지 안에 포함된 iframe을 말한다. 또 데이터를 보내는 HTTP 요청을 생성하지 않으며, DOM 기반 통신에 사용된다.

 

아래는 사용예시입니다.

 

부모 윈도우

    
        // 메시지 수신 리스너 추가
        window.addEventListener('message', (event) => {
            // 올바른 출처에서 온 메시지인지 확인
            if (event.origin !== "<http://example.com>http://example.com>") {
                return; // 출처가 다르면 무시
            }
            console.log('Received message from child:', event.data);
        });

        // iframe이 로드된 후 메시지 전송
        document.getElementById('childFrame').onload = function() {
            var childFrame = document.getElementById('childFrame').contentWindow;
            childFrame.postMessage('Hello from parent!', '<<a href=http://example.com>http://example.com</a>>');
        };
    

 

자식 윈도우

        // 메시지 수신 리스너 추가
        window.addEventListener('message', (event) => {
            // 올바른 출처에서 온 메시지인지 확인
            if (event.origin !== "<http://example.com>http://example.com>") {
                return; // 출처가 다르면 무시
            }
            console.log('Received message from parent:', event.data);
            // 부모에게 응답 메시지 전송
            event.source.postMessage('Hello from child!', event.origin);
        });
    

 

postmessage의 설명에서도 적절하게 사용한 경우 안전하다고 했다. 이렇게 아래 코드처럼 origin을 필터링 하지 않는다면 postmessage가 사용되는 사이트에 특정값을 주입하여 해킹가능하다. 예를 들어서 악성 parent 윈도우를 생성하여 xss 코드를 child 윈도우에게 전송 한다. 그 뒤에 child의 쿠키 탈취한다.

 if (event.origin !== "<http://example.com>") {
                return; // 출처가 다르면 무시
            }

 

* 를 지정하는 경우 origin 상관없이 보내라는 뜻이다. 윈도우는 지속적으로 변경된다. 웹 문서는 일반적으로 출처가 고정되어 있는 반면, 창의 경우에는 사용자가 하이퍼링크를 방문하거나 스크립트가 다른 문서로 리다이렉트시켜 Origin이 변경될 수 있습니다.

targetwindow.posMessage("비밀키", *);

 

그렇기 때문에 아래와 같은 공격이 가능합니다.

실제 공격 시나리오는 다음과 같습니다.

  1. Parent Window에서 Child Window 생성
  2. Child Window가 Parent Window한테 postMessage로 메시지 및 비밀 값 전송
  3. Parent Window가 공격자의 다른 웹 사이트로 리다이렉트
  4. Child Window는 여전히 Parent Window에게 메시지 및 비밀 값 전송
  5. 공격자 사이트가 Child Window가 보내주는 메시지 수신

JSONP 방식

JSONP는 JSON with Padding의 준말로, CORS 기술이 도입되기 전 SOP를 우회하기 위해 흔히 쓰였던 방식입니다. JSONP API는 JSON API과 유사하나, 응답 데이터를 특정 콜백 함수를 호출하는 코드로 감싸고 요청 시 XHR이 아니라 다음과 같이 스크립트로 포함시켜 동작한다는 점이 다릅니다.

 

이에 대한 동작은 다음 링크에서 확인하세요

https://www.youtube.com/watch?v=3AoeiQa8mY8&t=3s

 

이것이 작동하는 이유는 script의 src는 소스코드를 브라우저로 가져와서 실행하라는 뜻이기 때문이다.

 

다음 방식을 사용하면 JSONP Hijacking 에 취약해집니다. 아래는 공격 시나리오입니다. 이러한 공격 시나리오는 우리가 앞서 위해서 설명했던 sop가 나오기 전의 시나리오와 유사합니다. jsonp는 sop를 우회하는 정책이기 때문에 그렇습니다.

 

아래는 공격 시나리오 입니다.

 

일단 취약한 사이트가 JSONP를 사용해야 합니다. 해커는 자신이 제어하는 웹사이트에 사용자가 방문할 때 JSONP 요청을 수행하도록 설정할 수 있습니다. 이 요청은 피해자가 로그인된 상태에서, 본래 신뢰할 수 있는 사이트의 API를 호출합니다.이 스크립트는 사용자의 쿠키가 함께 전송되는 상황을 노리며, 사용자의 인증 쿠키를 통해 신뢰할 수 있는 사이트로부터 민감한 데이터를 가져오게 됩니다.

 

위와 같은 JSONP를 해결하기 위해서 CORS 정책이 나왔습니다.

 

CORS란?

 

브라우저에서는 보안적인 이유로 cross-origin HTTP 요청들을 제한합니다. 그래서 cross-origin 요청을 하려면 서버의 동의가 필요합니다. 만약 서버가 동의한다면 브라우저에서는 요청을 허락하고, 동의하지 않는다면 브라우저에서 거절합니다.

 

이러한 허락을 구하고 거절하는 메커니즘을 HTTP-header를 이용해서 가능한데, 이를 CORS(Cross-Origin Resource Sharing)라고 부릅니다.

 

이의 작동은 아래와 같습니다.

  1. 서버로 요청을 합니다.
  2. 서버의 응답이 왔을 때 브라우저가 요청한 Origin과 응답한 헤더의 Access-Control-Request-Headers의 값을 비교하여 유효한 요청이라면 리소스를 응답합니다. 만약 유효하지 않은 요청이라면 브라우저에서 이를 막고 에러가 발생합니다

헤더는 여러 출처, null 값 또는 와일드카드를 허용할 수 있습니다. 그러나 와일드카드 사용 시 쿠키를 제거한 뒤에 요청을 보내야지만이 브라우저는 요청을 허용합니다.

 

이러한 CORS 는 안전하게 타 사이트가 타 사이트를 읽어올 수 있는 메커니즘이며, 이를 통해서 우리가 앞서 생각한 해킹 방식 3가지를 막을 수 있습니다.

 

 

'Web Security > 웹 해킹' 카테고리의 다른 글

CSP : Content-Security-Policy  (1) 2024.09.19
CSRF - CSRF token, SameSite Cookie  (1) 2024.09.19
XSS에 대해서 기초적인 내용  (2) 2024.09.16
Document Object Model??  (4) 2024.09.01
Is unicode safe?  (4) 2024.08.28