前面 25 天我們介紹了區塊鏈與智能合約安全的理論、案例與漏洞。
今天要回到開發者最實際的角度——如何在日常開發中就降低風險?
這篇會彙整 Solidity 開發中被社群廣泛採用的安全實務(Best Practices),
讓你在還沒審計之前,就能自己先守住最基本的防線。
🧱 一、安全開發四大原則
原則/說明
1️⃣ Checks → Effects → Interactions
先檢查條件,再更新狀態,最後再進行外部呼叫。防止重入攻擊。
2️⃣ 最小權限原則(Least Privilege)
每個帳號、角色、模組只給它「必要的最小權限」。
3️⃣ 模組化與清晰邏輯
將複雜流程拆分為小函式,便於審計與測試。
4️⃣ 明確錯誤處理
使用 require / revert 搭配明確訊息,幫助除錯與審計。
🧩 二、常見安全 Pattern 與示範
🔹 1. Checks–Effects–Interactions
這是防止重入攻擊的經典模式:
function withdraw(uint amount) external {
// ✅ 1. 檢查條件
require(balances[msg.sender] >= amount, "Insufficient balance");
// ✅ 2. 更新狀態
balances[msg.sender] -= amount;
// ✅ 3. 執行外部互動
payable(msg.sender).transfer(amount);
}
這樣即使攻擊者在 transfer() 時重入,也無法再重複提領。
🔹 2. 使用 OpenZeppelin 套件
開發時,建議永遠不要自己重寫安全關鍵邏輯(例如 Ownable、ReentrancyGuard、ERC 標準)。
直接引用 OpenZeppelin Contracts:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Vault is ReentrancyGuard, Ownable {
mapping(address => uint) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint amount) external nonReentrant {
require(balances[msg.sender] >= amount, "Not enough");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
}
OpenZeppelin 的合約經過社群長期審查,是新手與團隊最穩的起點。
🔹 3. 使用事件(Event)紀錄關鍵操作
事件(Event)能提供上鏈可追蹤的紀錄,有助於:
• 稽核(audit)
• 錯誤追蹤
• 前端 UI 同步
event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
function deposit() external payable {
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
🔹 4. 權限控制與多重簽章
對重要動作(例如 setOwner、upgradeImplementation)應使用:
• onlyOwner
• AccessControl
• 多重簽章 (Multisig)
function setNewRate(uint256 _rate) external onlyRole(MANAGER_ROLE) {
rate = _rate;
}
這樣能避免因單一私鑰外洩導致的整體損失。
🔹 5. 限制 Gas 消耗與可重入風險
避免在單一交易中處理過多使用者或外部呼叫(例如 loop)。
若要批次處理,建議使用「分段式」操作設計,例如:
function processNext(uint256 index) external {
require(index < users.length, "done");
_process(users[index]);
}
這樣可以防止 Gas 過高導致交易失敗,也降低攻擊面。
⚙️ 三、開發時的安全 Checklist
檢查項目/是否完成
✅ 所有外部函式皆有訪問限制(public / external / onlyXXX)
✅ 所有 transfer 或 call 之後皆更新狀態變數
✅ 沒有硬編碼重要地址或私鑰
✅ 沒有直接依賴 block.timestamp 或 block.number 進行隨機
✅ 使用 OpenZeppelin 的 library(Ownable, SafeMath, ReentrancyGuard)
✅ 所有重要操作皆 emit Event
✅ 沒有多餘的 fallback 或 receive 可能接受 ETH
✅ 對輸入參數皆有 require 驗證
💭 四、設計觀念:安全與可維護並重
有時候「安全」不只是避免攻擊,更是避免開發者自己在升級時犯錯。
因此,代碼應該保持:
• 可讀(Readable)
• 模組化(Modular)
• 文件清晰(Documented)
• 測試完整(Tested)
安全性是從第一行程式就該考慮的設計哲學,而不是審計前才想到的事。
開發安全合約的過程,並不只是寫出「能跑的程式」,
而是要寫出「不會被錯誤利用的程式」。
從 Day 1 的區塊鏈基礎 到 Day 26 的實作守則,
我們已經具備一個安全工程師的思維雛形:
「思考攻擊前,先想設計如何防守。」
接下來的 Day 27~29,
我們會進入實作階段,用 Remix 來演示實際漏洞與修復過程。
那時會更直觀地看到,這些「理論安全規則」在真實世界如何發揮力量。