智能合約安全與審計
存取控制漏洞
存取控制漏洞是一個遺漏或錯誤的授權檢查,讓不該動手的人得以呼叫特權函式。它是智能合約安全裡那扇沒鎖的門:一個會鑄造代幣、提走金庫、暫停系統或升級實作的函式,本應只有擁有者或被授權的角色能呼叫,但守衛被忘了加、加在錯的函式上、或接到了錯的條件。儘管不起眼,遺漏存取控制始終名列現實世界中破壞力最大的漏洞類別。
具體來說,這些缺陷長這樣:一個沒有 onlyOwner 修飾子的 setOwner 或 mint 函式;一個任何人都能呼叫(或再次呼叫)以奪取剛部署代理擁有權的初始化函式;一個被留在預設可見性、因而能被外部呼叫的函式;或一個被授予卻從未撤銷的角色。2017 年的 Parity 多簽災難,根源就是存取控制失誤:一個共用函式庫的初始化函式未受保護,某帳號宣告了擁有權,接著呼叫 selfdestruct,凍結了每一個依賴它的錢包中約 51.3 萬枚 ETH。
防禦之道嚴謹且廣為人知。採用經過審視的模式,例如單一擁有者合約用 OpenZeppelin 的 Ownable、角色式權限用 AccessControl;為每一個會改變狀態的特權函式掛上正確的修飾子;用初始化守衛保護初始化函式;身分判斷上偏好 msg.sender 而非 tx.origin。關鍵在於撰寫測試與不變量,斷言非擁有者無法走到特權路徑,因為「少了一道檢查」正是那種在被人利用之前不會報任何錯的漏洞。
// VULNERABLE: anyone can mint
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
// SAFE
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}單單少了一個修飾子,就是一個重大漏洞。
最炫的攻擊上了頭條,但實務上,一個被遺忘的存取修飾子或一個未受保護的代理初始化函式,才是重大損失最常見的單一根因。永遠要測試反例:證明一個未授權的地址會回滾。
又称
另见