Advanced Security

Back

Loading concept...

šŸ›”ļø Advanced Security: Protecting Your Blockchain Castle

Imagine your smart contract is a magical castle. Everyone can see it, but only YOU should control what happens inside. Let’s learn how sneaky attackers try to break in—and how to stop them!


šŸŽÆ What We’ll Learn

Think of blockchain security like protecting a treasure chest. We’ll discover:

  1. Time Manipulation Attacks — When someone messes with the clock
  2. Griefing Attacks — Bullies who ruin the game for everyone
  3. Access Control Vulnerabilities — When the wrong person gets the keys
  4. Denial of Service — Blocking the castle doors
  5. Formal Verification — Math-powered protection spells
  6. Commit-Reveal Schemes — The secret envelope trick

ā° Time Manipulation Attacks

The Story

Imagine a game where you win a prize if you guess the exact second. But what if the game host could secretly move the clock forward or backward to make you lose?

In blockchain, miners are like game hosts. They choose when to add your transaction to the blockchain. They can wiggle the time a little bit—usually up to 15 seconds in either direction!

Why This Matters

// āŒ DANGEROUS: Using block.timestamp
function lottery() public {
  if (block.timestamp % 2 == 0) {
    // Winner!
    sendPrize(msg.sender);
  }
}

A miner can wait and pick the perfect second to submit their own transaction—and always win!

The Fix

// āœ… SAFE: Don't use time for
// important decisions
function lottery(uint256 seed) public {
  // Use random number from
  // trusted source (Chainlink VRF)
  uint256 result = getRandomNumber(seed);
}

🧠 Remember

Bad Idea Good Idea
Use block.timestamp for prizes Use external randomness (Chainlink)
Trust exact seconds Allow time windows (hours, not seconds)

😈 Griefing Attacks

The Story

Imagine you’re playing tag. To tag someone, you need to tap their shoulder. But what if someone wears a suit covered in super-sticky glue? When you try to tag them, YOU get stuck!

In blockchain, a griefing attack is when someone makes your contract fail—not to steal money, but just to be mean or to break things.

Real Example: The Refund Trap

// āŒ VULNERABLE: Refund everyone
function refundAll() public {
  for (uint i = 0; i < players.length; i++) {
    // If ONE player rejects,
    // EVERYONE fails!
    payable(players[i]).transfer(refunds[i]);
  }
}

An attacker can deploy a contract that rejects all payments:

// 😈 Evil contract
receive() external payable {
  revert("I reject your money!");
}

Now nobody can get refunds!

The Fix: Pull Over Push

// āœ… SAFE: Let users withdraw
mapping(address => uint256) public balances;

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

Each person withdraws their own money. If the attacker’s contract fails, only THEY lose—not everyone else!

🧠 Remember

graph TD A["Push Pattern"] -->|Dangerous| B["Contract sends to users"] B --> C["One failure = All fail"] D["Pull Pattern"] -->|Safe| E["Users withdraw themselves"] E --> F["One failure = Only they fail"]

šŸ” Access Control Vulnerabilities

The Story

Imagine your house has a magic door. You say ā€œOpen sesame!ā€ and it opens. But what if you forgot to tell the door that ONLY YOU can say the magic words? Anyone could walk right in!

The Classic Mistake

// āŒ DANGEROUS: Anyone can call this!
function withdrawAll() public {
  payable(msg.sender).transfer(
    address(this).balance
  );
}

There’s no check! Anyone in the world can steal everything!

Common Access Control Bugs

Vulnerability What Happens
Missing onlyOwner Anyone can call admin functions
Wrong modifier order Checks run after damage is done
Hardcoded addresses Can’t update if compromised
No role separation One key controls everything

The Fix: Proper Guards

// āœ… SAFE: Only owner can call
address public owner;

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

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

🧠 Pro Tip: Use OpenZeppelin

import "@openzeppelin/contracts/access/Ownable.sol";

contract SafeVault is Ownable {
  function withdrawAll() public onlyOwner {
    // Already protected!
  }
}

🚧 Denial of Service (DoS) in Contracts

The Story

Imagine a slide at the playground. Everyone takes turns—one kid goes down, then the next. But what if a bully sits at the top and refuses to move? Nobody else can use the slide!

That’s a Denial of Service attack. The attacker blocks everyone else from using the contract.

Example: The Gas Monster

// āŒ VULNERABLE: Unbounded loop
function payEveryone() public {
  // What if there are 1 MILLION users?
  for (uint i = 0; i < users.length; i++) {
    users[i].transfer(payments[i]);
  }
}

If the list gets too long, the transaction runs out of gas and FAILS. Nobody gets paid—ever!

Types of DoS Attacks

graph TD A["DoS Attacks"] --> B["Gas Exhaustion"] A --> C["External Call Failure"] A --> D["State Bloat"] B --> E["Unbounded loops"] C --> F["Revert in receive"] D --> G["Spam storage slots"]

The Fix: Batching & Pull Pattern

// āœ… SAFE: Process in batches
function payBatch(
  uint256 start,
  uint256 count
) public {
  uint256 end = start + count;
  for (uint i = start; i < end; i++) {
    balances[users[i]] += payments[i];
  }
}

// Users withdraw themselves
function withdraw() public {
  uint256 amount = balances[msg.sender];
  balances[msg.sender] = 0;
  payable(msg.sender).transfer(amount);
}

šŸ”¬ Formal Verification

The Story

Imagine you build a LEGO castle. You think it’s perfect. But what if a math wizard could check EVERY possible way someone might bump into it, and prove it will NEVER fall down?

That’s formal verification! It uses math to PROVE your code is correct—not just test it.

Testing vs. Formal Verification

Testing Formal Verification
ā€œI tried 100 inputs, all workedā€ ā€œI proved ALL inputs workā€
Might miss edge cases Covers EVERY possibility
Fast but incomplete Slow but thorough
Like checking some locks Like proving doors can’t open

How It Works

  1. Write specifications — What should ALWAYS be true?
  2. Model your code — Turn it into math
  3. Run the prover — Computer checks all possibilities
  4. Get proof — Mathematical guarantee!

Example Specification

/// @notice Balance never goes negative
/// @custom:invariant balance >= 0

/// @notice Total supply never changes
/// @custom:invariant
///   sum(balances) == totalSupply

Popular Tools

Tool What It Does
Certora Proves smart contract properties
Echidna Fuzzing + property testing
Mythril Finds security bugs automatically
Slither Static analysis for vulnerabilities

🧠 Key Insight

Testing says: ā€œI didn’t find bugs.ā€ Formal verification says: ā€œThere ARE no bugs.ā€


🤫 Commit-Reveal Schemes

The Story

You and your friend play rock-paper-scissors over the phone. But there’s a problem—whoever says their choice SECOND can cheat!

Solution: Both of you write your choice on paper, put it in a sealed envelope, and mail it. Once BOTH envelopes arrive, you open them together. No cheating!

That’s a commit-reveal scheme!

The Blockchain Problem

Everything on blockchain is PUBLIC. If you submit ā€œrockā€ first, your opponent sees it and picks ā€œpaperā€!

The Solution: Two Phases

graph TD A["Phase 1: COMMIT"] --> B["Hash your secret"] B --> C["Submit hash to contract"] C --> D["Wait for others to commit"] D --> E["Phase 2: REVEAL"] E --> F["Submit original secret"] F --> G["Contract verifies hash"] G --> H["Determine winner!"]

Code Example

// Step 1: Player commits
mapping(address => bytes32) public commits;

function commit(bytes32 hashedChoice) public {
  commits[msg.sender] = hashedChoice;
}

// Step 2: Player reveals
function reveal(
  string memory choice,
  string memory secret
) public {
  bytes32 hash = keccak256(
    abi.encodePacked(choice, secret)
  );
  require(
    hash == commits[msg.sender],
    "Hash doesn't match!"
  );
  // Now process the revealed choice
}

Creating the Commit

// Off-chain: Create your commitment
const choice = "rock";
const secret = "my_random_secret_123";
const hash = ethers.utils.keccak256(
  ethers.utils.toUtf8Bytes(choice + secret)
);
// Submit 'hash' to commit()
// Later, submit choice + secret to reveal()

Why This Works

Property Explanation
Hiding Hash reveals nothing about your choice
Binding You can’t change your choice later
Fairness Everyone commits before anyone reveals

šŸ† Summary: Your Security Shield

You now know how to protect your blockchain castle from:

Attack Defense
ā° Time Manipulation Use external randomness, not timestamps
😈 Griefing Pull pattern over push pattern
šŸ” Access Control Always add onlyOwner / role checks
🚧 Denial of Service Batch processing, avoid unbounded loops
šŸ”¬ Need Guarantees Use formal verification tools
🤫 Front-running Commit-reveal schemes

šŸŽ‰ You Made It!

You’ve learned the advanced secrets of blockchain security. You now know:

  • Why miners can wiggle time (and how to not care)
  • How bullies can break contracts (and how to ignore them)
  • Why ā€œwho can call this?ā€ is the MOST important question
  • How to avoid gas explosions with smart batching
  • That math can PROVE your code is safe
  • The envelope trick that beats cheaters

Go forth and build secure, unstoppable smart contracts! šŸš€


Remember: In blockchain, everyone can see your code. The best defense is writing code so good that even when attackers see it, they can’t break it!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.