solidity-security-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSolidity Security Best Practices
Solidity智能合约安全最佳实践
When to Apply
适用场景
- Reviewing smart contract code for common vulnerabilities.
- Implementing critical patterns like Checks-Effects-Interactions.
- Auditing access control and upgradeability logic.
- Preparing for a security audit or bug bounty.
- Debugging unexpected behavior in external contract interactions.
- 检查智能合约代码中的常见漏洞。
- 实现Checks-Effects-Interactions(CEI)等关键模式。
- 审计访问控制与可升级性逻辑。
- 为安全审计或漏洞赏金活动做准备。
- 调试外部合约交互中的异常行为。
Critical Security Patterns
核心安全模式
Checks-Effects-Interactions (CEI)
Checks-Effects-Interactions(CEI)模式
Always update state variables before making external calls to prevent reentrancy.
solidity
// BAD
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool s, ) = msg.sender.call{value: amount}("");
balances[msg.sender] -= amount;
}solidity
// GOOD
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool s, ) = msg.sender.call{value: amount}("");
require(s);
}在进行外部调用前务必先更新状态变量,以防止重入攻击。
solidity
// BAD
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool s, ) = msg.sender.call{value: amount}("");
balances[msg.sender] -= amount;
}solidity
// GOOD
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool s, ) = msg.sender.call{value: amount}("");
require(s);
}Access Control
访问控制
Restrict sensitive functions to authorized addresses using standard patterns.
solidity
// BAD
function setOwner(address _new) public {
owner = _new;
}solidity
// GOOD
function setOwner(address _new) public onlyOwner {
owner = _new;
}使用标准模式将敏感函数限制为仅授权地址可调用。
solidity
// BAD
function setOwner(address _new) public {
owner = _new;
}solidity
// GOOD
function setOwner(address _new) public onlyOwner {
owner = _new;
}Reentrancy Protection
重入防护
Use mutexes to prevent recursive calls into the same function or contract.
solidity
// BAD
function claim() public {
require(!claimed[msg.sender]);
msg.sender.call{value: 1 ether}("");
claimed[msg.sender] = true;
}solidity
// GOOD
function claim() public nonReentrant {
require(!claimed[msg.sender]);
claimed[msg.sender] = true;
payable(msg.sender).transfer(1 ether);
}使用互斥锁防止对同一函数或合约的递归调用。
solidity
// BAD
function claim() public {
require(!claimed[msg.sender]);
msg.sender.call{value: 1 ether}("");
claimed[msg.sender] = true;
}solidity
// GOOD
function claim() public nonReentrant {
require(!claimed[msg.sender]);
claimed[msg.sender] = true;
payable(msg.sender).transfer(1 ether);
}Safe External Calls
安全外部调用
Handle token transfer failures and validate external contract interactions.
solidity
// BAD
function pay(IERC20 token, uint amount) public {
token.transfer(msg.sender, amount);
}solidity
// GOOD
using SafeERC20 for IERC20;
function pay(IERC20 token, uint amount) public {
token.safeTransfer(msg.sender, amount);
}处理代币转账失败情况并验证外部合约交互。
solidity
// BAD
function pay(IERC20 token, uint amount) public {
token.transfer(msg.sender, amount);
}solidity
// GOOD
using SafeERC20 for IERC20;
function pay(IERC20 token, uint amount) public {
token.safeTransfer(msg.sender, amount);
}High Priority Patterns
高优先级模式
Input Validation
输入验证
Sanitize all user-provided data to prevent unexpected state changes.
solidity
// BAD
function setRate(uint _rate) public {
rate = _rate;
}solidity
// GOOD
function setRate(uint _rate) public {
if (_rate == 0) revert InvalidRate();
rate = _rate;
}对所有用户提供的数据进行清理,以防止意外的状态变更。
solidity
// BAD
function setRate(uint _rate) public {
rate = _rate;
}solidity
// GOOD
function setRate(uint _rate) public {
if (_rate == 0) revert InvalidRate();
rate = _rate;
}Upgrade Safety
升级安全性
Protect implementation contracts and ensure storage compatibility.
solidity
// BAD
contract MyProxy is Initializable {
uint public x;
function init(uint _x) public { x = _x; }
}solidity
// GOOD
contract MyProxy is Initializable {
uint public x;
function init(uint _x) public initializer { x = _x; }
}保护实现合约并确保存储兼容性。
solidity
// BAD
contract MyProxy is Initializable {
uint public x;
function init(uint _x) public { x = _x; }
}solidity
// GOOD
contract MyProxy is Initializable {
uint public x;
function init(uint _x) public initializer { x = _x; }
}Circuit Breakers
断路器机制
Implement emergency stop mechanisms to pause functionality during attacks.
solidity
// BAD
function trade() public {
_performTrade();
}solidity
// GOOD
function trade() public whenNotPaused {
_performTrade();
}实现紧急停止机制,以便在攻击期间暂停功能。
solidity
// BAD
function trade() public {
_performTrade();
}solidity
// GOOD
function trade() public whenNotPaused {
_performTrade();
}Medium Priority Patterns
中优先级模式
Signature Security
签名安全
Prevent replay attacks by including nonces and domain separators.
solidity
// BAD
function exec(bytes memory sig) public {
address signer = recover(sig);
_doWork(signer);
}solidity
// GOOD
function exec(bytes memory sig, uint nonce) public {
require(!used[nonce]);
used[nonce] = true;
address signer = recover(sig, nonce);
}通过包含随机数(nonce)和域分隔符防止重放攻击。
solidity
// BAD
function exec(bytes memory sig) public {
address signer = recover(sig);
_doWork(signer);
}solidity
// GOOD
function exec(bytes memory sig, uint nonce) public {
require(!used[nonce]);
used[nonce] = true;
address signer = recover(sig, nonce);
}Randomness
随机性实现
Avoid using on-chain data for randomness; use verifiable random functions.
solidity
// BAD
uint rand = uint(keccak256(abi.encodePacked(block.timestamp)));solidity
// GOOD
// Use Chainlink VRF
uint rand = IVRF(vrf).requestRandomWords();避免使用链上数据生成随机性;使用可验证随机函数。
solidity
// BAD
uint rand = uint(keccak256(abi.encodePacked(block.timestamp)));solidity
// GOOD
// Use Chainlink VRF
uint rand = IVRF(vrf).requestRandomWords();Event Emission
事件触发
Emit events for all significant state changes to facilitate off-chain tracking.
solidity
// BAD
function update(uint _v) public {
val = _v;
}solidity
// GOOD
function update(uint _v) public {
val = _v;
emit Updated(_v);
}对所有重要的状态变更触发事件,以便于链下追踪。
solidity
// BAD
function update(uint _v) public {
val = _v;
}solidity
// GOOD
function update(uint _v) public {
val = _v;
emit Updated(_v);
}Common Vulnerability Checklist
常见漏洞检查表
| Vulnerability Name | SCWE ID | Quick Fix |
|---|---|---|
| tx.origin Authentication | SCWE-018 | Use msg.sender instead |
| Unprotected SELFDESTRUCT | SCWE-038 | Add access control to selfdestruct |
| Reentrancy - External Call Before State Update | SCWE-046 | Use CEI pattern or ReentrancyGuard |
| Unchecked External Call Return Value | SCWE-109 | Use require() or SafeERC20 |
| Delegatecall Injection | SCWE-132 | Validate target address for delegatecall |
| Integer Overflow/Underflow | SCWE-106 | Use Solidity 0.8+ or SafeMath |
| Default Function Visibility | SCWE-128 | Explicitly define visibility |
| Unencrypted On-Chain Private Data | SCWE-136 | Encrypt data or store off-chain |
| Weak Randomness | SCWE-015 | Use Chainlink VRF or commit-reveal |
| Unprotected Ether Withdrawal | SCWE-029 | Add access control to withdrawal |
| Uninitialized Storage Pointer | SCWE-043 | Initialize all storage pointers |
| Signature Replay Attack | SCWE-020 | Use nonces and EIP-712 |
| Hash Collision with abi.encodePacked | SCWE-025 | Use abi.encode for dynamic types |
| DoS with Block Gas Limit | SCWE-031 | Avoid loops over unbounded arrays |
| Floating Pragma | SCWE-058 | Lock pragma to a specific version |
| Front-Running | SCWE-063 | Use commit-reveal or slippage protection |
| Unprotected Initializer | SCWE-053 | Use 'initializer' modifier |
| Incorrect Access Control | SCWE-048 | Use OpenZeppelin AccessControl/Ownable |
| DoS with Unexpected Revert | SCWE-030 | Use pull-over-push for payments |
| Assert Violation | SCWE-067 | Use require() for input validation |
| Lack of Reentrancy Guard | SCWE-077 | Add nonReentrant modifier |
| Forced Ether via Self-Destruct | SCWE-079 | Don't rely on address(this).balance |
| Hardcoded Gas Amount | SCWE-100 | Avoid .transfer(), use .call() |
| 漏洞名称 | SCWE ID | 快速修复方案 |
|---|---|---|
| tx.origin身份验证 | SCWE-018 | 使用msg.sender替代 |
| 未受保护的SELFDESTRUCT | SCWE-038 | 为selfdestruct添加访问控制 |
| 重入攻击 - 外部调用先于状态更新 | SCWE-046 | 使用CEI模式或ReentrancyGuard |
| 未检查外部调用返回值 | SCWE-109 | 使用require()或SafeERC20 |
| Delegatecall注入 | SCWE-132 | 验证delegatecall的目标地址 |
| 整数溢出/下溢 | SCWE-106 | 使用Solidity 0.8+版本或SafeMath |
| 默认函数可见性 | SCWE-128 | 显式定义函数可见性 |
| 链上未加密私有数据 | SCWE-136 | 加密数据或存储在链下 |
| 弱随机性 | SCWE-015 | 使用Chainlink VRF或提交-揭示方案 |
| 未受保护的以太币提取 | SCWE-029 | 为提取功能添加访问控制 |
| 未初始化的存储指针 | SCWE-043 | 初始化所有存储指针 |
| 签名重放攻击 | SCWE-020 | 使用nonce和EIP-712 |
| abi.encodePacked哈希碰撞 | SCWE-025 | 对动态类型使用abi.encode |
| 区块Gas限制导致的DoS | SCWE-031 | 避免遍历无界数组 |
| 浮动版本声明 | SCWE-058 | 将pragma锁定到特定版本 |
| 抢先交易(Front-Running) | SCWE-063 | 使用提交-揭示方案或滑点保护 |
| 未受保护的初始化函数 | SCWE-053 | 使用'initializer'修饰符 |
| 错误的访问控制 | SCWE-048 | 使用OpenZeppelin AccessControl/Ownable |
| 异常回滚导致的DoS | SCWE-030 | 采用拉取式而非推送式支付 |
| Assert断言违规 | SCWE-067 | 使用require()进行输入验证 |
| 缺少重入防护 | SCWE-077 | 添加nonReentrant修饰符 |
| 自毁强制转入以太币 | SCWE-079 | 不要依赖address(this).balance |
| 硬编码Gas值 | SCWE-100 | 避免使用.transfer(),改用.call() |
Enhanced with MCP
结合MCP增强功能
If you have the MCP server configured, these tools provide automated security analysis:
solidity-agent-toolkit- for static analysis
run_slither - for heuristic detection
match_vulnerability_patterns - for SCWE pattern matching
check_vulnerability - for OWASP knowledge base lookup
search_vulnerabilities
如果你配置了 MCP服务器,这些工具可提供自动化安全分析:
solidity-agent-toolkit- :静态分析
run_slither - :启发式检测
match_vulnerability_patterns - :SCWE模式匹配
check_vulnerability - :OWASP知识库查询
search_vulnerabilities
References
参考资料
- OWASP Smart Contract Top 10 (2026)
- OWASP智能合约Top 10(2026版)