ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹해킹 기초(3) - 쿠키&세션
    해킹/웹해킹 2023. 3. 30. 15:11

    📌 HTTP의 특성

     

    🧃Connectionless 

    - 하나의 요청에 하나의 응답을 한 후 연결을 종료하는 것을 의미함

    - 특정 요청에 대한 연결은 이후의 요청과 이어지지 않고 새 요청이 있을 때 마다 항상 새로운 연결을 맺게 되는 특성을 의미함

     

    🧃Stateless

    - 통신이 끝난 후 상태 정보를 저장하지 않는 것

    - 이전 연결에서 사용한 데이터를 다른 연결에서 요구할 수 없음 

     

    📢즉, 클라이언트의 IP 주소, User-Agent는 매번 변경될 수 있는 고유한 주소임

     

    📢But, 우리는 이런 상태를 초월한 고유의 HTTP에서 상태를 유지할 수 있는 무언가가 필요함!

     

    🍪쿠키

    - 서버가 클라이언트에게 쿠키를 발급함

    - 클라이언트는 서버에 요청을 보낼 때마다(connection이 될 때마다) 데이터를 쿠키에 올려 같이 전송함

    - 서버는 쿠키를 확인해 클라이언트를 구분하는 것이 가능하다.

    - key:value 쌍으로 이루어져 있음 

    - 클라이언트의 기록과 상태 정보를 표현함

    -  웹 서버에서는 수많은 클라이언트의 로그인 상태와 이용자를 구별해야 하는데, 이때 클라이언트를 식별할 수 있는 값을 쿠키에 저장한다.

     

    쿠기가 없는 통신과 있는 통신의 비교 

    쿠키 없이 통신
    쿠키 통신
    쿠키를 변조하여 통신

     

    📌📌 클라이언트의 기록과 상태 정보란? 

     

    - 우리는 웹 서비스 사용 시 종종 등장하는 팝업 창에 “다시 보지 않기”, “7일 간 표시하지 않기” 버튼이 있는 것을 자주 볼 것이다. 

    - 클라이언트는 이 상태를 기록한 쿠키를 웹서버에게 보내고 웹 서버는 각 클라이언트의 팝업 옵션을 기억하기 위해 쿠키에 해당 정보를 기록하고, 쿠키를 통해 팝업 창 표시 여부를 판단함

    - 과거에 쿠키가 이런 웹서버에 정보를 보내기 위해 자주 사용되었지만, 전송을 할 때마다 쿠키를 발급해야하기 때문에 리소스의 낭비가 생겨 Storage APIs를 통해 데이터를 저장하는 방식이 사용되고 있음 

     

    쿠키 적용하기 

    - 클라이언트가 서버에 요청을 보낼 때 저장된 쿠키를 요청 헤더에 넣어 전송함 

    - 이용자는 요청을 보낼 때 요청 헤더에서 쿠키를 변조하는 것이 가능하다. 

    -클라이언트는 자바스크립트를 사용해 쿠키 변조가 가능하다. id = value;id2=value;....이런 형식으로 만들어주면 됨!

    -  쿠키를 설정할 때에는 만료 시간을 지정할 수 있고, 만료 시간 이후에는 클라이언트에서 쿠키가 삭제된다.

     

    - 서버가 보내온 HTTP 응답에서 서버 쿠키 설정 헤더(Set-Cookie)를 추가하면 클라이언트의 브라우저가 쿠키를 설정하게 된다. 

    콘솔 창으로 쿠키 확인하기

    어플리케이션으로 확인하기

     

    좌측의 나열된 목록을 펼치면 origin을 확인할 수 있고, origin을 누르면 설정된 쿠키 정보의 확인과 변조가 가능하다.

     

    🐥 세션 

     

    - 쿠키에 인증 상태를 저장하지만 클라이언트가 인증 정보를 변조할 수 없게 하기 위해 세션(Session)을 사용함

    -  인증 정보를 서버에 저장하고 해당 데이터에 접근할 수 있는 키(유추할 수 없는 랜덤한 문자열, Session ID)를 만들어 클라이언트에 전달하는 방식으로 작동한다.

    - 브라우저는 해당 키를 쿠키에 저장하고 이후에 HTTP 요청할 때 쿠키와 세션 위에 데이터를 얹어 서버에게 요청(보내기) 함

    - 서버는 요청에 포함된 키에 해당하는 데이터를 가져와 인증 상태를 확인함

     

    세션 통신 과정

    세션을 먼저 발급함(쿠키 발급 전)
    세션과 쿠키를 동시에 발급함

    세션과 쿠키의 차이점 

    쿠키 : 데이터 자체를 클라이언트가 저장함(아이디값)

    Result에 응답 메세지 안에 쿠키의 id값이 찍히게 된다.

     

    세션 : 서버(세션 스토리지)가 저장함 : 키값

    로그인을 통한 세션 확인

     

    1. 내가 확인하고자 하는 사이트의 devtools에서 Network 탭을 누름 -> Preserve log를 체크하고 로그인을 하면 로그인 성공시 응답의 set-cookies 헤더를 통해 브라우저의 쿠키에 세션정보(키값)를 저장함 

    맨 마지막 줄 set-cookies를 확인하면 쿠키 확인가능

    2.  어플리케이션 탭으로 가서 Cookies 목록 안에 내가 확인하고자 하는 사이트의 도메인을 누르면 서버의 set-cookies를 통해 서버가 보내온 쿠키를 확인할 수 있다.

     

     

    3. sessionid 헤더의 값을 메모장에 복사해 놓은 뒤 sessionid 헤더 값을 우클릭한 후 delete를 클릭하면 브라우저 쿠키에 저장된 세션 값이 삭제 됨 -> 로그인이 풀리게 된다.

    4. 쿠키의 빈 칸을 더블 클릭하여 session id 헤더를 추가하고 이전에 복사한 세션 값을 입력하면 브라우저의 쿠키에 세션 값이 설정되고 로그인이 됨 

     

     

    SOP

    - 악의적인 페이지가 클라이언트의 권한을 이용해 대상 사이트에 HTTP 요청(쿠키)을 보내고, HTTP 응답 정보를 획득 하는 코드를 실행하는 것을 통하여, 클라이언트는 가져온 테이터를 악의적인 페이지에서 읽을 수 있을 위험이 있음 ➡ 이것을 방지하기 위해 SOP 탄생

     

    SOP의 오리진 구분 방법

     

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

    - 프로토콜, 포트, 호스트(서버)가 모두 일치해야 동일한 오리진(Same Origin)이라고 판단한다.

    - 셋 중 하나만 다르면 Cross Origin이라고 판단함

     

     

     

    javascript 콘솔창을 통하여 확인이 가능함

     

    Same Origin 

    sameNewWindow = window.open('https://dreamhack.io/lecture');
    console.log(sameNewWindow.location.href);
    결과: https://dreamhack.io/lecture

    Cross Origin 

    crossNewWindow = window.open('https://theori.io');
    console.log(crossNewWindow.location.href);
    결과: Origin 오류 발생

     

    Cross Origin에서의 데이터를 읽으려 할 때는 오류가 발생해 읽지 못하지만, 데이터를 쓰는 것은 전혀 문제 없이 동작이 가능하다. (즉, 바꿀 수 있다는 얘기)

    crossNewWindow = window.open('https://theori.io');
    crossNewWindow.location.href = "https://dreamhack.io";

     

    📌📌 코드 동작 설명 

    <!-- iframe 객체 생성 -->
    <iframe src="" id="my-frame"></iframe>
    
    <!-- Javascript 시작 -->
    <script>
    /* 2번째 줄의 iframe 객체를 myFrame 변수에 가져옵니다. */
    let myFrame = document.getElementById('my-frame')
    /* iframe 객체에 주소가 로드되는 경우 아래와 같은 코드를 실행합니다. */
    myFrame.onload = () => {
        /* try ... catch 는 에러를 처리하는 로직 입니다. */
        try {
            /* 로드가 완료되면, secret-element 객체의 내용을 콘솔에 출력합니다. */
            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()>Same Origin</button><br>
    <button onclick=loadCrossOrigin()>Cross Origin</button>
    <!--
    frame.html의 코드가 아래와 같습니다.
    secret-element라는 id를 가진 div 객체 안에 treasure라고 하는 비밀 값을 넣어두었습니다.
    -->
    <div id="secret-element">treasure</div>

    <iframe src="" id="my-frame"></iframe>

    - iframe은 현재 웹 페이지 안에 또 다른 웹 페이지를 삽입하는 html 태그

    - src 요소를 설정해준 것은 삽입할 웹 페이지의 요소가 my-frame으로 가져온 주소로 결정됨 

     

    myFrame.onLoad => () => { }

    myFrame.onload = () => {
        /* try ... catch 는 에러를 처리하는 로직 입니다. */
        try {
            /* 로드가 완료되면, secret-element 객체의 내용을 콘솔에 출력합니다. */
            let secretValue = myFrame.contentWindow.document.getElementById('secret-element').innerText;
            console.log({ secretValue });
        } catch(error) {
            /* 오류 발생시 콘솔에 오류 로그를 출력합니다. */
            console.log({ error });
        }
    }

    - 해당 객체가 성공적으로 로드되었을 때 동작한다.

    - try 구문안의 코드는 iframe에 페이지가 로드된 이후에 동작함

     

    let secretValue = myFrame.contentWindow.document.getElementById('secret-element').innerText;console.log({ secretValue });

    로드가 완료 되었을 떄 iframe 내에서 삽입된 주소에서 secret-element 객체의 값인 treasure을 읽어와 콘솔에 출력해준다. 

     

    CORS(Cross Origin Resource Sharing)

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

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

     

    웹 리소스를 요청하는 클라이언트

    /*
        XMLHttpRequest 객체를 생성합니다. 
    */
    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'}");

     XMLHttpRequest

    - 웹 브라우저와 웹 서버 간에 데이터 전송을 도와주는 객체로, 이를 통해 HTTP 요청을 보낼 수 있다.

     

    클라이언트의 HTTP 요청을 서버가 알아들을 때 (서버식)으로 메세지 해석

    OPTIONS /whoami HTTP/1.1
    Host: theori.io
    Connection: keep-alive
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: content-type /*Acces-Control-Request : 각각 메서드와 헤더를 추가적으로 사용할 수 있는지에 대한 물어보기*/
    Origin: https://dreamhack.io
    Accept: */*
    Referer: https://dreamhack.io/

     

    OPTIONS /whoami HTTP/1.1

    - 위에서 웹 리소스 요청할 때, POST로 보냈는데 웹 서버에서 받아들여지는 메세지 형식은 OPTION이다. 

    CORS preflight 

    - 클라이언트 측에서 웹 리소스를 요청해도 되는지 질의하는 과정 

     

    서버의 응답 메세지 

    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

     

    마지막으로 브라우저는 서버의 응답이 클라이언트의 요청과 일치하는지를 확인한 뒤에 POST 요청을 보낸 뒤 HTTP 요청을 보낸다. 

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

    웹 해킹(5) - CSRF  (0) 2023.03.31
    웹 해킹(4) - XSS  (0) 2023.03.31
    드림핵 워게임(1) - cookie  (0) 2023.03.30
    웹 해킹(2) - 개발자 도구 사용하기  (0) 2023.03.29
    웹해킹(1) - 기초  (0) 2023.03.27
Designed by Tistory.