駭客之所以能得逞,根本原因在於該 DeFi 協議將「單一去中心化交易所(DEX)的資金池」當作唯一的價格來源。
在資安領域,我們非常忌諱「單一故障點(Single Point of Failure, SPOF)」。既然依賴單一 DEX 容易被駭客用龐大的資金砸盤扭曲價格,那我們如果把報價來源改成「同時參考全世界幾十個交易所的平均價格」呢?這就是目前 Web3 業界防禦預言機操縱的黃金標準:去中心化預言機網路 (Decentralized Oracle Network),而其中的霸主就是 Chainlink。
為什麼 Chainlink 能抵禦閃電貸攻擊?
閃電貸攻擊有一個硬性限制:它必須在「同一個區塊(單筆交易)」內完成借款與還款。
Chainlink 的防禦機制完美剋制了這一點:
實戰:在合約中整合 Chainlink Price Feed
要怎麼在我們的 Solidity 合約中取得 Chainlink 的安全報價呢?非常簡單,我們不需要自己去呼叫外部 API,Chainlink 已經在鏈上部署好了「資料聚合合約(Aggregator)」。我們只需要引入他們提供的介面(Interface)即可。
首先,在 Foundry 中安裝 Chainlink 的合約庫(或者直接在檔案頂部宣告介面)。以下是一個整合 ETH/USD 報價的標準範例:
'''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// 引入 Chainlink 官方提供的 V3 聚合器介面
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract SecureLendingProtocol {
AggregatorV3Interface internal priceFeed;
// 部署時傳入 Chainlink 報價合約的地址 (例如 Ethereum 主網的 ETH/USD 地址)
constructor(address _priceFeedAddress) {
priceFeed = AggregatorV3Interface(_priceFeedAddress);
}
// 取得最新價格
function getLatestPrice() public view returns (int256) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = priceFeed.latestRoundData();
return price;
}
}
'''
資安進階防護:那些整合 Chainlink 時容易犯的錯
很多開發者以為只要用了 latestRoundData() 就天下無敵了。身為資安稽核人員,我們要告訴你:錯了,這只是基本盤。
如果你沒有對回傳的資料做額外的校驗,駭客依然可能利用極端市況(例如 LUNA 幣價死亡螺旋崩盤事件)來發動攻擊。一個真正具備資安意識的整合寫法,必須包含以下兩項檢查:
檢查資料是否過期 (Stale Data)
如果 Chainlink 節點因為網路壅塞或不可抗力因素罷工了,合約可能會拿到好幾個小時前的「舊價格」。我們必須檢查 timeStamp:
'''require(timeStamp > 0, "Round not complete");
// 檢查這筆報價是否超過 1 小時沒更新 (假設心跳機制是 1 小時)
require(block.timestamp - timeStamp <= 3600, "Stale price data");
'''
檢查價格是否大於零 (Negative/Zero Price)
在極端情況下(例如合約發生邏輯錯誤或幣價崩盤底層斷路器觸發),價格可能會回傳 0 甚至負數(注意回傳型別是 int256 而不是 uint256)。如果不檢查,直接拿負數去算抵押率,整個借貸協議會瞬間崩潰。
'''
require(price > 0, "Invalid price: Price must be greater than zero");
'''
我們用最正統的方式,修補了昨天閃電貸造成的預言機操縱漏洞。
在 DeFi 開發中,「不要自己發明密碼學」與「不要自己實作報價預言機」是同樣重要的兩大鐵則。