@@ -15,6 +15,106 @@ timezone: UTC+8
1515## Notes
1616
1717<!-- Content_START -->
18+ # 2025-08-16
19+
20+ ### 存取控制(Access Control)
21+ ``` solidity=
22+ contract BadVault {
23+ mapping(address => uint256) public balance;
24+
25+ // 使用者存錢,沒問題
26+ function deposit() external payable {
27+ balance[msg.sender] += msg.value;
28+ }
29+
30+ // 沒有存取控制的提款函數
31+ function withdraw() public {
32+ payable(msg.sender).transfer(address(this).balance);
33+ }
34+ }
35+ ```
36+
37+ #### 如何防範
38+ ``` solidity=
39+ // SPDX-License-Identifier: MIT // (1)
40+ pragma solidity ^0.8.20;
41+
42+ contract SafeUserVault {
43+ address public immutable owner; // 部署者 // (2)
44+ mapping(address => uint256) public balance; // 每個使用者的存款紀錄
45+
46+ constructor() {
47+ owner = msg.sender; // 部署者
48+ }
49+
50+ // 存錢:任何人都能存
51+ function deposit() external payable {
52+ balance[msg.sender] += msg.value;
53+ }
54+
55+ // 使用者自己提款 (CEI模式)
56+ function withdraw(uint256 amount) external {
57+ // Check
58+ require(balance[msg.sender] >= amount, "Not enough balance");
59+
60+ // Effects (先改狀態)
61+ balance[msg.sender] -= amount;
62+
63+ // Interactions (再轉錢)
64+ (bool ok, ) = msg.sender.call{value: amount}("");
65+ require(ok, "Transfer failed");
66+ }
67+
68+ // 部署者提取「整個金庫」
69+ function withdrawAll() external {
70+ require(msg.sender == owner, "Not owner");
71+
72+ uint256 amount = address(this).balance;
73+ require(amount > 0, "Vault empty");
74+
75+ (bool ok, ) = owner.call{value: amount}("");
76+ require(ok, "Transfer failed");
77+ }
78+ }
79+ ```
80+ 1 . MIT授權是任何人都可以自由使用、修改、散佈你的程式碼,甚至可以用在商業用途。但必須保留原始的授權聲明(像上面這一行)。
81+ 2 . immutable 表示這個變數只能在 建構子(constructor)裡設定一次,之後不可更改,避免被惡意修改。
82+ 3 . call 跟 transfer 差異:
83+ * ** transfer:**
84+ ``` solidity
85+ payable(to).transfer(amount);
86+ ```
87+ * Gas 限制:
88+ 最多只會給接收方 2300 gas。2300 gas 幾乎只能觸發 event 或寫個簡單的 log。接收方的 fallback 或 receive 函數裡,不能做複雜操作(例如寫入 storage、呼叫其他合約)。
89+
90+ * 錯誤處理:
91+ 如果轉帳失敗(例如接收方的 fallback revert),整個交易會自動 revert。不需要手動檢查。
92+
93+ * 安全性:
94+ 因為 gas 很低,幾乎無法被重入攻擊。
95+
96+ * 缺點:在 EIP-1884(以太坊升級) 之後,某些操作 gas 變貴,2300 gas 有時不足,會造成正常的轉帳失敗。
97+
98+
99+ * ** call:**
100+ ``` solidity
101+ (bool ok, ) = to.call{value: amount}("");
102+ require(ok, "Transfer failed");
103+
104+ // 如果想限制 gas,可以寫
105+ to.call{value: amount, gas: 2300}("");
106+ ```
107+
108+ * Gas 控制:
109+ 預設會把「所有剩餘 gas」都交給接收方。接收方可以在 fallback/receive 裡執行很複雜的邏輯(甚至再呼叫回來 → 可能重入)。
110+
111+ * 錯誤處理:
112+ call 會回傳 (success, data)。不會自動 revert,要自己檢查 success。這讓開發者可以決定是否忽略錯誤或 revert。
113+
114+ * 安全性:
115+ 因為可能給太多 gas,所以如果沒做好防護(例如重入鎖),可能被重入攻擊。
116+ 但 call 比 transfer 彈性大,也比較不會因為 gas 限制而失敗。現在建議用call+檢查。
117+
18118# 2025-08-15
19119
20120## 安全實踐 -- 常見攻擊手段
0 commit comments