JWT
什么是JWT
JWT(JSON Web Token)是一種用于在各方之間作為JSON對象安全地傳輸信息的開放標準(RFC 7519)。該信息經過數字簽名,因此是可驗證和可信的。JWT 可以使用HMAC算法或使用RSA的公鑰/私鑰對進行簽名
JWT的組成
- Header(頭部):包含令牌的類型(JWT)和使用的簽名算法(如HMAC SHA256或RSA)。
- Payload(負載):包含聲明(claims)。聲明是關于實體(通常是用戶)和其他數據的斷言。聲明有三種類型:
- Registered claims(注冊聲明):這些是預定義的聲明,如
iss
(簽發者)、exp
(過期時間)、sub
(主題)、aud
(受眾)等。它們不是強制的,但建議使用。- Public claims(公共聲明):這些是可以在JWT中使用的自定義聲明。為了避免沖突,應在IANA JSON Web Token Registry中定義或使用具有防沖突前綴的命名空間。
- Private claims(私有聲明):這些是雙方之間使用的自定義聲明,通常是特定于應用程序的。
- Signature(簽名):用于驗證消息在傳遞過程中是否未被篡改。首先,您需要將編碼后的頭部和負載進行簽名,然后使用指定的簽名算法創建簽名。簽名的過程如下:
- 使用 Base64 URL 編碼對 Header 和 Payload 進行編碼。
- 使用編碼后的 Header、編碼后的 Payload 和一個密鑰(對于 HMAC 算法)或私鑰(對于 RSA)創建簽名。
[HFCTF2020]EasyLogin
正常進行注冊登錄,發現有命令框,嘗試了各種讀取方式都沒用,抓包發現存在jwt
再查看一下源碼,看有沒有什么提示
發現了關于flag的提示
解釋一下這串代碼的大致意思:
- AJAX 請求: 這段代碼使用
$.get
向/api/flag
端點發起一個 AJAX GET 請求。- 成功回調: 如果請求成功,
done
方法會被調用,并傳入返回的數據。
- 使用解構賦值從返回的數據中提取
flag
屬性。- 將 ID 為
username
的輸入元素的值設置為這個flag
。- 失敗回調: 如果請求失敗,
fail
方法會被調用。
xhr
: XMLHttpRequest 對象。xhr.responseJSON.message
: 從響應 JSON 中提取的錯誤信息,并在警告框中顯示。
猜測flag應該就在/api/flag里面,提示了koa框架;
koa-static是一個處理靜態文件資源的中間件,從大佬哪里借鑒一個koa的框架結構
controllers是項目控制器存放目錄,訪問來查看源碼
結合前面提到的flag所在的目錄,訪問api目錄
/controllers/api.js
?發現了返回flag的條件
'GET /api/flag': async (ctx, next) => {if(ctx.session.username !== 'admin'){throw new APIError('permission error', 'permission denied');}const flag = fs.readFileSync('/flag').toString();ctx.rest({flag});await next();},
簡單來說就是判斷username是否為admin,如果是admin的話就讀取'/flag'下的內容以字符串的形式存儲在flag中
想要username為admin,就只有通過JWT偽造(因為注冊為admin會報錯)
那么解題思路也就明確了,通過jwt將username偽造為admin,來讀取flag并返回
先解析返回的jwt,簽名是HS256
偽造方法,把"alg"值更改為"none",這樣就不執行簽名驗證,將secretid改為[]代表一個空值
修改之后進行base64編碼(去掉換行符和編碼后的"="),把header和payload拼接,因為簽名改為了none所以簽名認證就為空
進入/api/flag,抓包,修改一下sses:aok和sses:aok.sig,修改為偽造jwt后返回的對應的值
就完成了jwt的偽造,獲取到flag
總結:這個周從復現開始就全是token的題,前面也接觸過session,一開始只是草草了解token和session類似,但是現在把兩個做了明顯的區別對比
Token
客戶端存儲:Token是在客戶端存儲用戶身份驗證和狀態信息的一種機制。通常,服務器會在用戶登錄成功后生成一個令牌(Token),并將其發送給客戶端。客戶端通常會將Token存儲在本地,比如LocalStorage、SessionStorage或者Cookies中。
狀態管理:Token同樣用于跟蹤用戶的身份驗證狀態,但所有的狀態信息都被編碼到Token中。服務器在接收到包含Token的HTTP請求時,會解碼Token并驗證用戶的身份。
無狀態性:Token通常被設計為無狀態的,這意味著服務器不需要在內存或者數據庫中存儲任何關于用戶會話的信息。這使得系統更容易水平擴展,并提高了性能。
靈活性:Token可以在不同的服務之間輕松傳遞,因為所有的用戶信息都被編碼在Token中。這使得Token特別適合于微服務架構和分布式系統。
安全性:Token通常使用加密算法進行簽名,以確保其完整性和真實性。另外,Token還可以包含過期時間和范圍限制等信息,以增強安全性。
Session
服務器端存儲:Session是在服務器端存儲用戶的身份驗證和狀態信息的一種機制。通常,服務器會在用戶登錄后創建一個唯一的會話標識符(Session ID),并將這個ID與用戶的相關信息存儲在服務器上的內存或持久化存儲中。
狀態管理:Session用于跟蹤用戶的會話狀態,可以存儲用戶的登錄狀態、權限信息、購物車內容等。服務器可以根據Session ID來識別特定用戶的會話,并提供相應的個性化服務。
Cookies支持:通常,服務器會將Session ID 存儲在客戶端的Cookie中,以便在后續的HTTP請求中發送回服務器。這樣,服務器就可以根據Session ID 來識別用戶的會話。但是,Session ID 也可以通過其他方式在客戶端進行傳輸,比如通過URL參數或者隱藏域。
安全性:由于Session數據存儲在服務器端,因此相對來說更加安全。但是,仍然需要注意Session劫持(Session Hijacking)和會話固定(Session Fixation)等攻擊。
區別
- Session主要依賴于服務器端存儲,用于跟蹤用戶的會話狀態。
- Token主要依賴于客戶端存儲,包含了用戶的身份驗證和狀態信息,并且被設計為無狀態的、可傳遞的機制。