Skip to content
Last updated: March 11, 2025

SCWE-055: Missing Protection against Signature Replay Attacks

Stable Version v0.0.1

This content is in the version-(v0.0.1) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

Signature replay attacks occur when a valid signature from a previous transaction is reused in a different context, such as a different transaction or contract call. Without proper protection, an attacker can capture and replay a signature, potentially bypassing authentication checks and causing unauthorized actions or transactions.

Remediation

To prevent signature replay attacks, include additional checks that ensure the signature is valid for a specific transaction or context. This can be done by incorporating unique identifiers like a nonce, timestamp, or a unique transaction hash into the signature to bind it to a specific use. Always verify that the signature is only valid for the intended action.

Vulnerable Contract Example

contract ReplayAttackExample {
    mapping(address => uint256) public nonces;

    function authenticate(bytes32 message, uint8 v, bytes32 r, bytes32 s) public {
        address signer = ecrecover(message, v, r, s);
        require(signer == msg.sender, "Invalid signature");
        // No protection against replay attacks; attacker can reuse the same signature
    }
}

Fixed Contract Example

  pragma solidity ^0.8.0;

  import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

  contract SecureReplayProtectionExample {
      using ECDSA for bytes32;

      mapping(address => uint256) public nonces;

      function authenticate(bytes32 message, bytes memory signature) public {
          uint256 nonce = nonces[msg.sender]++;
          bytes32 messageWithContext = keccak256(abi.encodePacked(message, nonce, block.chainid));  
          address signer = messageWithContext.toEthSignedMessageHash().recover(signature);
          require(signer == msg.sender, "Invalid signature");
      }
  }
Fixes Implemented: - Binds signature to a nonce (ensuring it's only usable once). - Includes chainId (prevents cross-chain replay attacks). - Uses OpenZeppelin’s ECDSA library (avoids signature malleability risks).