今天我們要來學Session,這個東西與JWT有點類似,所以我們今天先學Session,這樣明天我們就能針對兩者進行比較。
Session是儲存在伺服器端的使用者狀態資訊,伺服器通常會透過Cookie(通常是sessionid)來識別用戶,並在伺服器端的資料庫或記憶體中保存資料,而Session的特點是將資料儲存在伺服器端,但同時會占用伺服器資源。
Session Fixation(會話固定攻擊)
攻擊者先給受害者一個已知的Session ID,讓受害者登入後,攻擊者就能用同一個Session進入帳號。
範例:http://victim.com/login?sessionid=12345
Session Hijacking (會話劫持)
攻擊者竊取使用者的 Session(通常透過Cookie偷到sessionid)
Session Timeout 不當設定
如果Session永遠不過期,攻擊者可以長期利用竊取到的Session
今天我們將會針對Session的攻擊展開演練,其中包含了攻擊方法以及如何針對弱點進行修正
首先請先將以下的session_attack.py儲存
#session_attack.py
from flask import Flask, request, make_response, redirect, url_for, render_template_string
import uuid
app = Flask(__name__)
# Server-side session store (very naive)
SESSIONS = {} # sid -> {username, last_active}
LOGIN_PAGE = """
<h2>Login (For Sesion Attack)</h2>
<form method="POST" action="/login">
Username: <input name="username"/>
<input type="submit" value="Login"/>
</form>
"""
HOME_PAGE = """
<h2>Home</h2>
<p>Hi {{username}}! (sid={{sid}})</p>
<form method="POST" action="/logout"><input type="submit" value="Logout"/></form>
"""
@app.route("/")
def index():
# If attacker supplies ?sessionid=XYZ, we accept it (vulnerability)
sid_from_query = request.args.get("sessionid")
sid = request.cookies.get("sid") or sid_from_query
if sid and sid in SESSIONS:
user = SESSIONS[sid]["username"]
resp = make_response(render_template_string(HOME_PAGE, username=user, sid=sid))
# note: cookie has no HttpOnly / Secure / SameSite set
return resp
resp.set_cookie("sid", sid)
return render_template_string(LOGIN_PAGE)
@app.route("/login", methods=["POST"])
def login():
username = request.form.get("username", "guest")
# Vulnerable behavior: if client already sent a sid (cookie or query), we DO NOT rotate it.
sid = request.cookies.get("sid") or request.args.get("sessionid") or str(uuid.uuid4())
SESSIONS[sid] = {"username": username}
resp = make_response(redirect(url_for("index")))
# insecure cookie: no HttpOnly, no Secure, no SameSite
resp.set_cookie("sid", sid)
return resp
@app.route("/logout", methods=["POST"])
def logout():
sid = request.cookies.get("sid")
if sid and sid in SESSIONS:
del SESSIONS[sid]
resp = make_response(redirect(url_for("index")))
resp.set_cookie("sid", "", expires=0)
return resp
if __name__ == "__main__":
app.run(host='127.0.0.1',port=5000,debug=False)
1.先執行session_attack.py
2.開啟分頁A,http://127.0.0.1:5000/?sessionid=attacker-sid-001 ,這個步驟可以建立一個已知的Session ID
3.接下來我們在分頁A進行登入,輸入attacker後按登入,這樣程式就會把attacker-sid-001與attacker進行關聯,之後按登出
4.現在我們開啟分頁B模擬受害者,輸入victim後進行登入,因為server接受query sid,登入後victim的帳號會和attacker-sid-001進行綁定
5.攻擊者這時重新打開http://127.0.0.1:5000/?sessionid=attacker-sid-001,就會看到 victim 的帳號被讀出
在這個測試中有幾個漏洞存在導致會被利用
1.程式接受?sessionid=作為sid,如果攻擊者先發一個已知 sessionid(例如 12345)給受害者,受害者登入後 server 用那個 sid 關聯帳號(session fixation)
2.Cookie沒有HttpOnly/Secure/SameSite,session管理非常簡單且可被猜測或重複使用。
3.登入時沒有產生新的隨機sid(session rotation),舊sid繼續有效
這項程式我們可以針對幾項地方進行修正,使網站變安全
1.不接受客戶端提供的sid作為登入後的Session ID
2.登入時強制產生新的Session ID
3.伺服器端記錄最後活動時間實現到期後刪除
4.登出時清除伺服器 session
今天我們學習了Session的基礎知識,以及基本的Session實作,了解Session的運作模式,以及如何防範Session被惡意利用遭到攻擊,明天我將針對Session與JWT進行比較與分析。