Skip to main content

Bảo mật Blockchain và Smart Contracts - An toàn là ưu tiên

· 6 min read

Bootcamp Blockchain Mastery

Bảo mật Blockchain và Smart Contracts

Security là aspect quan trọng nhất trong blockchain development. Một lỗi nhỏ có thể dẫn đến mất hàng triệu USD. Bài viết này cover các vulnerabilities phổ biến và cách phòng tránh.

Tầm quan trọng của Security

Hậu quả của lỗi bảo mật

  • Financial Loss: Hàng triệu USD bị hack
  • Reputation Damage: Mất lòng tin từ users
  • Irreversible: Smart contracts không thể sửa sau deploy
  • No Central Authority: Không có ai có thể "undo" transactions

Famous Hacks

  • The DAO (2016): $60M stolen - Reentrancy attack
  • Parity Wallet (2017): $150M locked - Access control issue
  • Ronin Bridge (2022): $625M stolen - Private key compromise

Common Vulnerabilities

1. Reentrancy Attacks

Reentrancy xảy ra khi external call được thực hiện trước khi update state.

Vulnerable Code

// ❌ VULNERABLE
mapping(address => uint256) public balances;

function withdraw() public {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] = 0; // Too late!
}

Attack Scenario

contract Attacker {
VulnerableContract target;

function attack() external {
target.withdraw();
}

receive() external payable {
if (address(target).balance >= 1 ether) {
target.withdraw(); // Reentrant call!
}
}
}

Secure Solution

// ✅ SECURE - Checks-Effects-Interactions pattern
function withdraw() public {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // Effects first
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}

// Or use ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Secure is ReentrancyGuard {
function withdraw() external nonReentrant {
// Safe withdrawal
}
}

2. Integer Overflow/Underflow

Solidity 0.8.0+ tự động check, nhưng cần lưu ý với version cũ.

// ❌ VULNERABLE (Solidity < 0.8.0)
uint8 count = 255;
count++; // Overflows to 0

// ✅ SAFE (Solidity >= 0.8.0)
uint8 count = 255;
count++; // Reverts automatically

3. Access Control Issues

Vulnerable

// ❌ Missing access control
function withdraw() public {
owner.transfer(address(this).balance);
}

Secure

// ✅ With access control
address public owner;

modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}

function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}

4. Front-running

Attackers có thể thấy pending transactions và submit với higher gas.

Mitigation

  • Commit-Reveal Scheme: Two-phase transactions
  • Private Mempool: Use Flashbots
  • Batch Auctions: Reduce MEV

5. Unchecked External Calls

// ❌ Dangerous
function transfer(address to, uint256 amount) public {
to.call{value: amount}(""); // No error handling
}

// ✅ Safe
function transfer(address to, uint256 amount) public {
(bool success, ) = to.call{value: amount}("");
require(success, "Transfer failed");
}

6. Denial of Service (DoS)

// ❌ Vulnerable to DoS
address[] public users;

function refundAll() public {
for (uint i = 0; i < users.length; i++) {
payable(users[i]).transfer(1 ether);
// Could fail if one user is contract without receive()
}
}

// ✅ Safe - Pull pattern
mapping(address => uint256) public refunds;

function refund() public {
uint256 amount = refunds[msg.sender];
refunds[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}

7. Randomness Issues

Blockchain không có randomness thực sự.

// ❌ Predictable
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, blockhash(block.number))));

// ✅ Use Chainlink VRF or similar
// Or use commit-reveal scheme

Security Best Practices

1. Use Established Libraries

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

2. Checks-Effects-Interactions Pattern

Always follow this order:

  1. Checks: Validate inputs và conditions
  2. Effects: Update state variables
  3. Interactions: Call external contracts
function transfer(address to, uint256 amount) public {
// 1. Checks
require(to != address(0), "Invalid address");
require(balances[msg.sender] >= amount, "Insufficient balance");

// 2. Effects
balances[msg.sender] -= amount;
balances[to] += amount;

// 3. Interactions
emit Transfer(msg.sender, to, amount);
}

3. Least Privilege Principle

Chỉ grant minimum permissions cần thiết:

mapping(address => bool) public canMint;
mapping(address => bool) public canBurn;

modifier onlyMinter() {
require(canMint[msg.sender], "Not minter");
_;
}

4. Input Validation

function setPrice(uint256 _price) public {
require(_price > 0, "Price must be positive");
require(_price <= MAX_PRICE, "Price too high");
price = _price;
}

5. Use Safe Math (Solidity < 0.8.0)

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

using SafeMath for uint256;

function add(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b); // Safe addition
}

6. Proper Error Handling

// Use custom errors (gas efficient)
error InsufficientBalance(uint256 required, uint256 available);

function withdraw(uint256 amount) public {
if (balances[msg.sender] < amount) {
revert InsufficientBalance(amount, balances[msg.sender]);
}
}

7. Time-based Logic

// ❌ Vulnerable to miner manipulation
require(block.timestamp > deadline, "Too early");

// ✅ Use block.number for longer periods
require(block.number > deadlineBlock, "Too early");

Security Tools

Static Analysis

  • Slither: Fast static analysis framework
  • MythX: Security analysis service
  • Oyente: Older but still useful

Fuzzing

  • Echidna: Property-based fuzzing
  • Medusa: Coverage-guided fuzzing

Formal Verification

  • Certora: Formal verification tool
  • K Framework: Formal semantics

Testing

  • Hardhat: Testing framework với coverage
  • Foundry: Fast testing với fuzzing
  • Truffle: Traditional testing

Example với Foundry

// Fuzz test
function testFuzzTransfer(address to, uint256 amount) public {
vm.assume(to != address(0));
vm.assume(amount <= 1000);

token.transfer(to, amount);
assert(token.balanceOf(to) == amount);
}

Smart Contract Auditing

Audit Process

  1. Code Review: Manual review của code
  2. Automated Analysis: Tools như Slither
  3. Testing: Comprehensive test suite
  4. Formal Verification: Mathematical proofs
  5. Report: Document findings

What Auditors Look For

  • Common vulnerabilities
  • Logic errors
  • Gas optimization opportunities
  • Best practices compliance
  • Architecture issues

Audit Checklist

  • ✅ Reentrancy protection
  • ✅ Access control
  • ✅ Integer overflow/underflow
  • ✅ Input validation
  • ✅ Error handling
  • ✅ Front-running mitigation
  • ✅ DoS resistance
  • ✅ Upgradeability (if applicable)

Upgrade Patterns

Proxy Patterns

Cho phép update logic while keeping same address:

  • Transparent Proxy: Simple proxy pattern
  • UUPS: Universal Upgradeable Proxy Standard
  • Beacon Proxy: Shared implementation

Upgrade Best Practices

  • Maintain backward compatibility
  • Test upgrades thoroughly
  • Have rollback plan
  • Time-lock upgrades

Incident Response

If Contract is Hacked

  1. Immediate Actions:

    • Pause contract (if possible)
    • Notify users
    • Document attack vector
  2. Investigation:

    • Analyze attack
    • Identify vulnerability
    • Estimate losses
  3. Recovery:

    • Fix vulnerability
    • Deploy new contract
    • Migrate users (if needed)
  4. Post-mortem:

    • Document lessons learned
    • Update security practices
    • Share knowledge

Kết luận

Security là trách nhiệm của mọi developer. Hiểu về common vulnerabilities, sử dụng best practices, và thực hiện audits đầy đủ là cách duy nhất để bảo vệ users và funds.

Tiếp tục học về Advanced Topics trong Bootcamp Blockchain Mastery!