Security

Broken Cryptography: Signature Replay and ecrecover Bugs

Off-chain signatures are the backbone of modern Web3 UX, enabling gasless meta-transactions, permit-style token approvals, and centralized authority verification.
However, implementing cryptography in Solidity is notoriously difficult. "Rolling your own" signature verification using the raw ecrecover precompile is a common path to critical vulnerabilities.

1. The "Zero Address" Bug

The most insidious failure mode of the built-in ecrecover function is how it handles invalid signatures. If the signature is malformed or incorrect, ecrecover does not revert. Instead, it silently returns address(0).
A naive implementation looks like this:
// VULNERABLE CODE

function claim(bytes memory signature) public {

address signer = ecrecover(hash, v, r, s);

require(signer == authorizedSigner, "Invalid signature");

// ... grant reward

}
If the developer accidentally left the authorizedSigner state variable uninitialized (meaning its value is address(0)), then anyinvalid signature will result in ecrecover returning address(0), satisfying the condition signer == authorizedSigner. The attack succeeds.
Solution: Always check that the result of ecrecover is not the zero address before comparing it to the target signer.

2. Signature Replay Attacks

A valid signature is just a mathematical proof of intent. Without context, that proof is valid forever and anywhere.
If Alice signs a message "Pay Bob 1 ETH" for Contract A, Bob might take that same signature and submit it to Contract B, or submit it to Contract A a second time.
Solution: Every signed message must include:
  • A Nonce: A unique number used only once per user to prevent re-submission.
  • The Chain ID: To prevent replay across different networks (e.g., Ethereum Mainnet vs. L2s).
  • Contract Address: To tie the signature to a specific application context.

3. Signature Malleability

Elliptic Curve cryptography has a quirk: for any valid signature (v, r, s), there exists another mathematically valid signature (v', r, s') that resolves to the exact same public key.
An attacker can take a valid signature sent by a user, modify the s value mathematically, and submit a "different" looking signature that the contract accepts as valid. If your contract uses the signature hash itself as a unique identifier (e.g., to prevent replays), this malleability breaks your logic.
Conclusion: Never use raw ecrecover. Use battle-tested libraries like OpenZeppelin's ECDSA.sol, which handle malleability checks and zero-address failures automatically.