前兩天判讀的是弱掃報告。今天這份份量更重:一份外部白帽單位出具、蓋著「高風險」的滲透測試報告,說系統的 JWT(一種帶簽章的身分權杖)簽章金鑰太弱,攻擊者可以偽造任意身分。
先講結論:這個弱點是真的。 金鑰確實太弱、確實被猜了出來,報告沒有冤枉我。但我盯著報告裡那張「驗證通過」的截圖,怎麼看都覺得哪裡不對——它秀出來的那個「證明」,邏輯上好像根本沒有證明到它想證明的東西。這篇就是這個「怪」的來龍去脈,以及一個非資安專業的人,可以怎麼靠一個工具把這種疑問查到水落石出。
報告展示的流程大致是:拿到一組系統發的合法 token、解碼看到裡面的帳號;接著測試者用一個很弱的字串(就叫它 secret)當金鑰,把帳號改成 admin、重新簽出一組新 token,然後驗證這組新 token——畫面顯示「簽章通過」。報告據此判定金鑰是弱密鑰、風險「高」。
我卡住的地方是:他用 secret 這把金鑰簽了一組 token,再用 secret 這把金鑰去驗這組 token——這不是廢話嗎? 我自己拿一把鑰匙鎖門、再用同一把鑰匙開門,當然開得了,這能證明什麼?於是我把這個疑問丟給 AI。
AI 第一輪解釋了離線字典攻擊:JWT 的簽章驗證可以在自己電腦上離線做,不碰伺服器、不會被鎖定,所以可以拿一份常見弱密鑰字典逐一去試,直到某個字算出來的簽章跟 token 對上——secret 這種開發者偷懶的預設值,字典裡一定有。
這個回答本身沒錯,但它答的是「一把弱金鑰怎麼被猜出來」,還沒回答我真正卡住的:報告秀的那個「自己簽、自己驗」的展示步驟,到底能不能算數。所以我把問題講得更精確。
我把話說白:先不管暴力破解——就報告秀的那一步,他拿 secret 簽新 token、再用 secret 驗,這在邏輯上必然成功,跟金鑰強不強根本無關。AI 這次接住了,而且點破:對,這是同義反覆(circular)。「我知道密碼是什麼,所以我能用這個密碼登入」——這不是漏洞證明,這只是套套邏輯。
那,一個邏輯上真正站得住的證明,該長什麼樣?這裡就是這篇最值得學的一段。
關鍵在 JWT 的簽章是怎麼算出來的。它用的 HS256 演算法,簽章是這樣產生的:
簽章 = HMAC-SHA256( base64(header) + "." + base64(payload) , 金鑰 )
HMAC 是確定性函數:同一段訊息配同一把金鑰,永遠算出同一個簽章,沒有隨機性。正因為如此,有一個乾淨俐落的驗證法——而這個方法,其實正是我一開始腦中假設「報告應該是這樣做」的那個流程:
secret,對這段原封不動的原始 header + payload 重新簽一次如果相同,就證明了系統當初就是用 secret 簽的。 因為要「重現一個伺服器已經簽好的簽章」,除非你手上的金鑰正是它用的那把,否則做不到——要湊出另一把不同金鑰、卻對同一段訊息算出相同 HMAC,是密碼學上不可行的碰撞。
看出差別了嗎?就差在一個字:原始。
| 簽的是哪段訊息、驗的是哪個簽章 | 能不能證明 | |
|---|---|---|
| 報告秀的那步 | 自己新造一組 token,驗自己簽的 | ❌ 不能,自簽自驗必然過 |
| 真正的證明 | 拿原始 token 的內容重簽,比對原始簽章 | ✅ 能,你在重現伺服器簽過的東西 |
自己造一個新 token 再驗自己,兩邊金鑰都是你的,當然通過,什麼都沒證明;而比對原始簽章,是拿系統已經簽好、你沒參與製造的那個結果當基準——能重現它,才代表你手上這把,就是它用的那把。
(一個學理上的小註腳:嚴格說「簽章相符」不能百分之百排除「宇宙中剛好存在另一把金鑰也算出同一值」,但那是天文數字級、湊不出來的碰撞,鑑識上直接當證明沒問題。)
想通這個,答案就清楚了:報告的結論是對的(金鑰確實是 secret、確實弱),但它秀出來的證明步驟不完整——它大概是漏貼了「用 secret 驗證原始 token 成功」那張關鍵截圖,只放了後面「既然金鑰是 secret,那就來偽造一個 admin」的展示。於是我讀報告時,看到的就變成一段「自己簽、自己驗」的循環論證。弱點是真的,只是舉證記錄少了最關鍵的那一步。
我後來沒有去追測試單位補件——金鑰本來就該換、也已經換掉了,弱點實質上排除了,追證明步驟的完整性意義不大。
老實說,這篇跟前面很多篇不一樣,它沒什麼「人跟 AI 來回把東西做出來」的協作味道。我沒有糾正 AI 什麼,AI 也沒幫我把什麼做完。真正發生的事更簡單:我帶著一個「這報告怎麼看都怪」的直覺,用 AI 把背後的密碼學原理問清楚,確認我這個直覺到底站不站得住。
但這恰恰是這系列想講的另一面。人的價值,不總是「做」或「抓錯」,有時候就只是那個**「不因為它蓋了高風險大印、出自專業白帽單位,就照單全收」的直覺**——覺得哪裡怪,就不放過。而 AI 的價值,是把一個我不熟的領域(密碼學)的原理,講到能讓我確認自己那個模糊直覺是對是錯。這兩件事湊起來,才讓一份看起來理所當然的報告,被讀出「它的證明其實漏了一步」。如果我沒有那個直覺,再強的 AI 也不會主動跳出來說「欸這份報告的舉證不完整喔」;如果我沒有 AI,那個「怪怪的」感覺大概就停在心裡、不了了之。
既然是教學,把這個弱點的根因跟該怎麼修補講清楚。
根因:JWT 的簽章金鑰用了弱密鑰。最常見的成因有兩種——一是直接沿用了教學文件、線上除錯工具的預設示範值(secret、your-256-bit-secret 這類),上線忘了改;二是把金鑰硬編碼寫死在原始碼裡,一旦原始碼外流(公開的 repo、前端 JS、設定檔),金鑰就等於公開。
改善方向(由基本到進階):
明天換一組主題:從「判讀別人丟過來的報告」,轉到「合規要求真正要落地時」——通用的資安最佳實務,碰上台灣公部門的在地現實,會產生什麼落差。先從組態基準(GCB)的導入談起。