HTTP 프로토콜 특징
Connectionless : 요청 하나에 하나의 응답을 한 후 연결을 종료
Stateless : 통신이 끝난 후 상태 정보를 저장하지 않는 것
쿠키는 데이터 자체를 이용자가 저장 / 세션은 서버가 저장
Console
탭 > document.cookie
를 입력하면 쿠키 정보를 확인가능Application
탭 > Cookies
를 펼치면 쿠키 정보 확인가능@app.route('/') # / 페이지 라우팅
def index():
username = request.cookies.get('username', None) # 이용자가 전송한 쿠키의 username 입력값을 가져옴
if username: # username 입력값이 존재하는 경우
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}') # "admin"인 경우 FLAG 출력, 아닌 경우 "you are not admin" 출력
return render_template('index.html')
메소드 요청마다 다른 기능을 수행 하는 것을 확인 가능
@app.route('/login', methods=['GET', 'POST']) # login 페이지 라우팅, GET/POST 메소드로 접근 가능
def login():
if request.method == 'GET': # GET 메소드로 요청 시
return render_template('login.html') # login.html 페이지 출력
elif request.method == 'POST': # POST 메소드로 요청 시
username = request.form.get('username') # 이용자가 전송한 username 입력값을 가져옴
password = request.form.get('password') # 이용자가 전송한 password 입력값을 가져옴
try:
pw = users[username] # users 변수에서 이용자가 전송한 username이 존재하는지 확인
except:
return '<script>alert("not found user");history.go(-1);</script>' # 존재하지 않는 username인 경우 경고 출력
if pw == password: # password 체크
resp = make_response(redirect(url_for('index')) ) # index 페이지로 이동하는 응답 생성
resp.set_cookie('username', username) # username 쿠키 설정
return resp
return '<script>alert("wrong password");history.go(-1);</script>' # password가 동일하지 않은 경우
try:
FLAG = open('./flag.txt', 'r').read() # flag.txt 파일로부터 FLAG 데이터를 가져옴.
except:
FLAG = '[**FLAG**]'
users = {
'guest': 'guest',
'admin': FLAG # FLAG 데이터를 패스워드로 선언
}
요청 패킷 내 username 변수가 포함된 쿠키에 의해 결정되어 문제가 발생함
서버는 별 다른 검증없이 이용자 요청에 포함된 쿠키를 신뢰하고 식별하기 때문에 공격자는 쿠키에 타 계정 정보를 삽입해 계정 탈취 가능
@app.route('/') # / 페이지 라우팅
def index():
username = request.cookies.get('username', None) # 이용자가 전송한 쿠키의 username 입력값을 가져옴
if username: # username 입력값이 존재하는 경우
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}') # "admin"인 경우 FLAG 출력, 아닌 경우 "you are not admin" 출력
return render_template('index.html')
<!-- 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>
<img>
, <style>
, <script>
등교차 출처 리소스 공유 (Cross Origin Resource Sharing, CORS) 는 HTTP헤더에 기반하여 Cross Origin간에 리소스를 공유하는 방법임
/*
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'}");
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
<script>
태그로 Cross Origin의 데이터를 불러옴<script>
태그에서는 자바스크립트로 인식하기 때문에 Callback함수를 활용<script>
/* myCallback이라는 콜백 함수를 지정합니다. */
function myCallback(data){
/* 전달받은 인자에서 id를 콘솔에 출력합니다.*/
console.log(data.id)
}
</script>
<!--
https://theori.io의 스크립트를 로드하는 HTML 코드입니다.
단, callback이라는 이름의 파라미터를 myCallback으로 지정함으로써
수신측에게 myCallback 함수를 사용해 수신받겠다고 알립니다.
-->
<script src='http://theori.io/whoami?callback=myCallback'></script>
/*
수신측은 myCallback 이라는 함수를 통해 요청측에 데이터를 전달합니다.
전달할 데이터는 현재 theori.io에서 클라이언트가 사용 중인 계정 정보인
{'id': 'dreamhack'} 입니다.
*/
myCallback({'id':'dreamhack'});