촉촉한초코칩
DreamHack Web Hacking Stage 3 본문
Stage 3 - Cookie & Session
Background:Cookie & Session
HTTP 프로토콜로 웹 서버와 통신 > 웹 서버에 명령을 내리기 위해 GET, POST와 같은 메소드와 자원의 위치를 가리키는 URL등이 포함되어 있음 + 헤더(Header)를 통해 웹 서버에게 요청, 웹 서버는 헤더를 읽고 클라이언트에게 결과 값 반환
헤더(쿠키, 세션)에는 클라이언트의 정보와 요청의 내용을 구체화하는 등의 데이터와 클라이언트의 인증 정보가 포함됨
쿠키
- 클라이언트의 IP 주소, User-Agent : 매번 변경될 수 있는 정보
- HTTP 프로토콜의 Connectionless와 Stateless : 웹 서버는 클라이언트를 기억할 수 없음 > Connectionless, Stateless 특징을 갖는 HTTP에서 상태를 유지하기 위해 쿠키 탄생
HTTP 프로토콜 특징 | |
Connectionless | 하나의 요청에 하나의 응답을 한 후 연결을 종료하는 것. 특정 요청에 대한 연결은 이후의 요청과 이어지지 않고 새 요청이 있을 때마다 항상 새로운 연결을 맺는다. |
Stateless | 통신이 끝난 후 상태 정보를 저장하지 않는 것. 이전 연결에서 사용한 데이터를 다른 연결에서 요구할 수 없다. |
쿠키
- Key, Value로 이루어진 일종의 단위
- 서버 > 클라이언트에게 쿠키 발급, 클라이언트는 서버에 요청을 보낼 때마다 쿠키 같이 전송
- 서버 : 클라이언트의 요청에 포함된 쿠키를 확인해 클라이언트 구분
쿠키 용도
1) 클라이언트의 정보 기록
ex) 웹 서버, 클라이언트의 팝업 옵션을 기억하기 위해 쿠키에 해당 정보 기록하고 쿠키를 통해 팝업 창 표시 여부 판단
하지만 쿠키는 서버와 통신할 때마다 전송되기 때문에 쿠키가 필요 없는 요청을 보낼 때 리소스 낭비 발생
> Modern Storage APIs를 통해 데이터 저장 방식 권장
2) 상태 정보 표현
웹 서버는 수많은 클라이언트의 로그인 상태와 이용자를 구별해야 하므로 클라이언트를 식별할 수 있는 값을 쿠키에 저장해 사용한다.
쿠키가 없는 통신 vs 있는 통신
1) 쿠키가 없는 통신 : 서버는 요청을 보낸 클라이언트가 누군지 알 수 없기 때문에 어떤 클라이언트와 통신하는지도 알 수 없다.
2) 쿠키가 있는 통신 : 클라이언트는 서버에 요청을 보낼 때마다 쿠키를 포함하고, 서버는 해당 쿠키를 통해 클라이언트를 식별한다.
쿠키 변조
- 쿠키 정보를 변조하여 서버에 요청을 보낼 수 있다.
- 만약, 서버가 별다른 인증 없이 쿠키를 통해 이용자의 인증 정보를 식별한다면 공격자가 타 이용자를 사칭해 정보를 탈취할 수도 있다.
세션 (Session)
- 쿠키에 인증 상태를 저장하지만 클라이언트가 인증 정보를 변조할 수 없게 하기 위해 세션을 사용한다.
- 인증 정보 서버에 저장 > 해당 데이터에 접근할 수 있는 키(유추할 수 없는 랜덤 문자열)를 만들어 클라이언트에게 전달
- 해당 키 : Session ID
- 브라우저 : 해당 키를 쿠키에 저장, 이후에 HTTP 요청을 보낼 때 사용
- 서버 : 요청에 포함된 키에 해당하는 데이터를 가져와 인증 상태 확인
쿠키와 세션의 차이점
- 쿠키 : 데이터 자체를 이용자가 저장
- 세션 : 서버가 저장
쿠키 적용법
- 쿠키는 클라이언트에 저장되기 때문에 클라이언트는 저장된 쿠키를 조회, 수정, 추가할 수 있다.
- 클라이언트가 서버에 요청을 보낼 때 저장된 쿠키를 요청 헤더에 넣어 전송하기 때문에 이용자가 요청을 보낼 때 쿠키 헤더를 변조할 수 있다.
- 쿠키를 설정할 때는 만료 시간을 지정할 수 있으며 만료 시간 후에는 클라이언트에서 쿠키가 삭제된다. 쿠키의 만료는 클라이언트(브라우저)에서 관리된다.
쿠키는 서버와 클라이언트 둘 다 설정할 수 있다.
1) 서버 - HTTP 응답 중 헤더에 쿠키 설정 헤더(Set-Cookie)를 추가하면 클라이언트의 브라우저가 쿠키를 설정한다.
HTTP/1.1 200 OK Server: Apache/2.4.29 (Ubuntu) Set-Cookie: name=test; Set-Cookie: age=30; Expires=Fri, 30 Sep 2022 14:54:50 GMT;... |
2) 클라이언트 - 자바스크립트를 사용해 쿠키를 설정한다.
document.cookie = "name=test;" document.cookie = "age=30; Expires=Fri, 30 Sep 2022 14:54:50 GMT;" |
* 크롬 Console 창 활용 - document.cookie를 입력하면 쿠키의 정보를 확인할 수 있다.
* 크롬 Application 활용 - 좌측의 Cookie를 누르면 Origin 목록을 확인할 수 있다. Origin(해당 URL)을 누르면 설정된 쿠키 정보를 확인/수정할 수 있다.
세션 연습
세션 하이재킹 (Session Hijacking) : 쿠키에는 세션 정보가 저장되어 있고 서버는 이를 통해 이용자를 식별하고 인증을 처리한다. 이때 공격자가 이용자의 쿠키를 훔칠 수 있으며 세션 하이재킹은 세션에 해당하는 이용자의 인증상태를 훔치는 것을 말한다.
정리
- Connectionless: 하나의 요청에 하나의 응답을 한 후 연결을 종료하는 것을 의미합니다.
- Stateless: 통신이 끝난 후 상태 정보를 저장하지 않는 것을 의미합니다.
- 쿠키 (Cookie): HTTP에서 상태를 유지하기 위해 사용하는 Key-Value 형태의 값
- 세션 (Session): 쿠키에 포함된 Session ID를 사용해 서버에 저장된 세션 데이터에 접근하는 방식
- 세션 하이재킹 (Session Hijacking): 타 이용자의 쿠키를 훔쳐 인증 정보를 획득하는 공격
Same Origin Plicy (SOP)
- 쿠키 : 인증상태를 나타내는 민감한 정보 보관, 브라우저 내부에 저장
- 브라우저 : 인증 정보로 사용될 수 있는 쿠키를 브라우저 내부에 보관, 이용자가 웹 서비스에 접속할 때, 브라우저는 해당 웹 서비스에서 사용하는 인증 정보인 쿠키를 HTTP 요청에 포함시켜 전달
- + 사이트에 직접 접속할 때 뿐만 아니라 웹 리소스를 통해 간접적으로 타 사이트에 접근할 때도 인증 정보인 쿠키를 함께 전송한다.
- 때문에 악의적인 페이지가 클라이언트의 권한을 이용해 대상 사이트에 HTTP 요청을 보내고, HTTP 응답 정보를 획득하는 코드를 실행할 수도 있다.
> 클라이언트 입장에서는 가져온 데이터를 악의적인 페이지에서 읽을 수 없도록 해야 한다. 이것이 바로 브라우저의 보안 매커니즘인 동일 출처 정책(Same Origin Policy, SOP)이다.
Same Origin Plicy의 오리진 (Origin) 구분 방법
- 오리진 구성 요소 : 프로토콜(Protocol, Scheme), 포트(Port), 호스트(Host)
- 구성요소가 모두 일치해야 동일한 오리진이다.
ex) https://same-origin.com/ 과 비교
URL | 결과 | 이유 |
https://same-origin.com/frame.html | Same Origin | Path만 다름 |
http://same-origin.com/frame.html | Cross Origin | Scheme 다름 |
https://cross.origin.com/frame.html | Cross Origin | Host 다름 |
https://same-origin.com:1234/ | Cross Origin | Port 다름 |
-> SOP는 Same Origin일 때만 정보를 읽을 수 있도록 한다.
* javascript를 이용해 SOP를 테스트 하는 코드
* window.open : 새로운 창을 띄우는 함수
* object.location.href : 객체가 가리키고 있는 URL주소를 읽어오는 코드
Cross Origin 데이터 읽고 쓰기
- 외부 출처에서 읽어온 데이터를 읽으려고 할 때는 오류가 발생해 읽지 못한다.
- 하지만 읽는 것 외에 데이터를 쓰는 것은 문제 없이 동작한다.
crossNewWindow = window.open('https://theori.io');
crossNewWindow.location.href = "https://dreamhack.io";
Same Origin Policy 데모
- 코드
<!-- 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 : 현재 웹 페이지 안에 또 다른 하나의 웹 페이지를 삽입하는 HTML 태그. src 요소를 설정함으로써 삽입할 웹 페이지의 주소가 결정된다.
- 10번째 줄의 onload : 이벤트 핸들러로써, 해당 객체가 성공적으로 로드되었을 때 동작한다. 10~23번 줄이 iframe 객체에 페이지가 로드되면 동작하는 코드이다.
- 14~15번째 줄 : 로드가 완료되면 iframe 내에 삽입된 주소에서 secret-element 객체의 값인 treasure를 읽어와 콘솔에 출력한다.
Cross Origin Resource Sharing (CORS) - Same Origin Policy 제한 완화
브라우저가 SOP에 구애받지 않고 외부 출처에 대한 접근을 허용해주는 경우 존재
ex) 이미지, JS, CSS 등의 리소스를 불러오는 <img>, <style>, <script>등의 태그는 SOP의 영향을 받지 않는다.
위 경우 외에도 웹 서비스에서 동일 출처 정책인 SOP를 완화하여 다른 출처의 데이터를 처리해야 하는 경우도 있다.
ex) 특정 포털 사이트의 카페, 블로그, 메일 서비스의 Host가 다를 때, 브라우저는 각 사이트의 오리진이 다르다고 인식한다. > 이용자가 메일을 메인 페이지에 출력하려면 개발자는 메인 페이지에서 메일 서비스 관련된 리소스를 요청해야 한다. 이때, 두 사이트는 오리진이 다르므로 SOP를 적용받지 않고 리소스를 공유할 방법이 필요하다.
교차 출처 리소스 공유(Cross Origin Resource Sharing, CORS) : 자원을 공유하기 위해 사용할 수 있는 공유 방법
- CORS와 관련된 HTTP 헤더를 추가하여 전송
- 이 외에도 JSON with Padding (JSONP) 방법을 통해 CORS 대체 가능
1) Cross Origin Resource Sharing (CORS)
- HTTP 헤더에 기반하여 Cross Origin 간에 리소스를 공유하는 방법
- 발신측 : CORS 헤더 설정해서 요청 > 수신측 : 헤더를 구분해 정해진 규칙에 맞게 데이터를 가져갈 수 있도록 설정
* Access-Control-Request : 해당하는 헤더 뒤에 따라오는 Method와 Headers는 각각 메소드와 헤더를 추가적으로 사용할 수 있는지 질의한다.
Header | 설명 |
Access-Control-Allow-Origin | 헤더 값에 해당하는 Origin에서 들어오는 요청만 처리 |
Access-Control-Allow-Methods | 헤더 값에 해당하는 메소드의 요청만 처리 |
Access-Control-Allow-Credentials | 쿠키 사용 여부 판단. |
Access-Control-Allow-Headers | 헤더 값에 해당하는 헤더의 사용 가능 여부 |
발신측에서 POST 방식으로 HTTP 요청 -> Options 메소드를 가진 HTTP 요청이 전달됨
: CORS preflight (수신측에서 웹 리소스를 요청해도 되는지 질의하는 과정)
> 브라우저는 수신측의 응답이 발신측의 요청과 상응하는지 확인한 뒤에 POST 요청을 보내 수신측의 웹 리소스를 요청하는 HTTP 요청을 보낸다.
2) JSON with Padding (JSONP)
- 이미지, JS, CSS 등의 리소스는 SOP에 구애받지 않고 외부 출처에 대해 접근을 허용한다.
- JSONP 방식은 이러한 특징을 이용해 <script> 태그로 Cross Origin 데이터를 불러온다.
- 하지만 <script> 태그에서는 데이터를 자바스크립트 코드로 인식하기 때문에 Callback 함수를 활용해야 한다.
- Cross Origin에 요청할 때 callback 파라미터에 어떤 함수로 받아오는 데이터를 핸들링할지 넘겨주면 대상 서버는 전달된 callback으로 데이터를 감싸 응답한다.
- 13번째 줄 : Cross Origin 데이터 불러오기.
- callback 파라미터로 myCallback 전달 > Cross Origin에서는 응답할 데이터를 myCallback 함수의 인자로 전달될 수 있도록 myCallback으로 감싸 JS 코드를 반환해준다.
- 반환된 코드는 요청측에서 실행됨. > 3-6번 줄에서 정의된 myCallback 함수가 전달된 데이터를 읽을 수 있다.
* 하지만 JSONP는 CORS가 생기기 전에 사용하던 방법으로 CORS를 사용하는 것이 좋다.
정리
- Same Origin Policy (SOP): 동일 출처 정책, 현재 페이지의 출처가 아닌 다른 출처로부터 온 데이터를 읽지 못하게 하는 브라우저의 보안 메커니즘
- Same Origin: 현재 페이지와 동일한 출처
- Cross Origin: 현재 페이지와 다른 출처
- Cross Origin Resource Sharing (CORS): 교차 출처 리소스 공유, SOP의 제한을 받지 않고 Cross Origin의 데이터를 처리 할 수 있도록 해주는 메커니즘
'Study > Web Hacking' 카테고리의 다른 글
Dreamhack - xss-1 (0) | 2022.07.05 |
---|---|
DreamHack Web Hacking Stage 4 (0) | 2022.07.05 |
Dreamhack - session-basic (0) | 2022.07.01 |
Dreamhack - Cookie (0) | 2022.07.01 |
DreamHack Web Hacking Stage 1, 2 (0) | 2022.03.22 |