-
웹해킹 기초(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