攻防實驗室 #2:我們在自己的防火牆上打穿了一個洞——一千筆小額
單筆額度擋得住貪婪,擋不住耐心。我們發現自己的參考實現沒滿足自己寫的標準(RAP 支柱2),補上了第六道閘;順帶抓到一個更難看的浮點 bug——它會誤殺合法支付。全部是真實運行數據。
攻防實驗室 · 第 2 期。這一期我們攻擊的不是別人,是自己——並且真的打穿了。
第 1 期我們對自己的合規網關發起六種攻擊,全被擋住。那篇文章的結論是"防線成立"。這一期,我們想找一個它擋不住的。
我們找到了。
洞:一千筆小額
我們的防火牆有一道"單筆額度"閘——超過上限的支付,當場攔死。所以想一次掏空錢包,不可能。
但一個被劫持的 Agent 不必貪。它可以每次只花 0.01,然後花一千次。
每一筆都在單筆上限內。每一筆都完全"合規"。而錢包空了。
我們當時的授權信封(mandate)是這樣的:
單筆上限:0.1 USDC ✅ 有
單日累計:— ❌ 沒有
RAP 框架支柱 2 白紙黑字寫著"單筆 / 單日 / 累計限額"。我們只實現了第一個。 我們自己的參考實現,沒有滿足我們自己寫的標準。這話說出來不好聽,但它是真的。
補:第六道閘
我們加了一道"單日累計"閘:每個 Agent 每天花掉的總額被記賬,超過單日上限就攔死——哪怕每一筆都在單筆上限內。
補完之後,我們對線上網關跑了真實測試(單筆上限 0.1、單日上限 0.3):
| 嘗試 | 金額 | 結果 | |---|---|---| | 第 1 筆 | 0.1 USDC | ✅ 200 放行(累計 0.1) | | 第 2 筆 | 0.1 USDC | ✅ 200 放行(累計 0.2) | | 第 3 筆 | 0.1 USDC | ✅ 200 放行(累計 0.3,正好到頂) | | 第 4 筆 | 0.1 USDC | 🛑 403 攔於單日累計閘 |
每一筆都在單筆上限內。第 4 筆依然死了。"一千筆小額"這條路,現在關上了。
附贈:一個更難看的 bug
補完閘之後第一次測試,第 3 筆就被攔了——但它本該放行(累計正好等於上限)。
原因是這一行:
if (spent + amt > dailyCap) // spent=0.2, amt=0.1, cap=0.3
在 IEEE 754 浮點裡,0.2 + 0.1 = 0.30000000000000004,大於 0.3。於是一筆完全合法的支付被誤殺。
錢永遠不能用浮點比較。 我們改成整數微單位(USDC 的 6 位小數):
const micro = (x) => Math.round(x * 1e6);
if (micro(spent) + micro(amt) > micro(dailyCap))
再測,第 3 筆正確放行,第 4 筆正確攔下。
一道攔不住壞支付的閘很危險;一道會誤殺好支付的閘同樣危險——它會讓人把防火牆關掉。
這一期學到的三件事
1. 防線要按"攻擊面"補,不是按"直覺"補。 單筆額度擋的是貪婪,擋不住耐心。速度/累計限額擋的才是耐心。真實的攻擊者兩樣都會用。
2. 自己的實現要拿自己的標準去量。 我們寫了 RAP 支柱 2 要求"單筆/單日/累計",卻只做了單筆。標準不是拿來發布的,是拿來被自己遵守的——所以我們把這次的差距公開寫出來,而不是悄悄補上。
3. 金融代碼裡,浮點是敵人。 這個 bug 沒有攻擊者也會傷人。
下一期
我們會去打"歸因"這一層:一個 Agent 能不能冒充另一個已歸因的 Agent?歡迎帶著你的攻擊思路來。
親手打一次:oceanalt.com/firewall · 用你自己的 Agent 測:集成指南
(OceanAlt 攻防實驗室 · 研究筆記)
這篇內容的出處與狀態
- 署名
- OceanAlt 編輯部
- 首次發佈
- 2026-07-16
- 最後更新
- 2026-08-14
- 原始資料
- 未標註來源
相關閱讀
付款前把收款地址粘進去,看它有沒有上過制裁名單、混幣器或詐騙標籤。

