• YouTube Channel
  • System Status
  • VS Code Extension
  • Signature Replay Attacks

    Overview

    What are Signature Replay Attacks?

    Signature replay attacks occur when a valid signature, originally used for a specific transaction or action, can be reused by an attacker to perform the same action multiple times. This vulnerability arises when a smart contract uses signatures for authorization without implementing proper measures to prevent the reuse of these signatures.

    Why are Signature Replay Attacks Dangerous?

    Signature replay attacks can be highly dangerous because:

    Technical Example of Vulnerable Code

    
    pragma solidity ^0.8.0;
    
    contract VulnerableWallet {
        mapping(address => uint256) public balances;
    
        function deposit() public payable {
            balances[msg.sender] += msg.value;
        }
    
        function transferWithSignature(
            address _to,
            uint256 _amount,
            bytes memory _signature
        ) public {
            bytes32 message = keccak256(abi.encodePacked(_to, _amount));
            address signer = recoverSigner(message, _signature);
            
            require(balances[signer] >= _amount, "Insufficient balance");
            balances[signer] -= _amount;
            balances[_to] += _amount;
        }
    
        function recoverSigner(bytes32 message, bytes memory signature) internal pure returns (address) {
            // Implementation of signature recovery
            // ...
        }
    }
    
    

    In this example, the `transferWithSignature` function uses a signature to authorize a transfer. However, there's no mechanism to prevent a signature from being used multiple times. An attacker could intercept a valid signature and replay it to perform the same transfer repeatedly.

    Technical Example of Fixed Code

    
    pragma solidity ^0.8.0;
    
    contract SecureWallet {
        mapping(address => uint256) public balances;
        mapping(address => uint256) public nonces;
    
        function deposit() public payable {
            balances[msg.sender] += msg.value;
        }
    
        function transferWithSignature(
            address _to,
            uint256 _amount,
            uint256 _nonce,
            bytes memory _signature
        ) public {
            require(_nonce == nonces[msg.sender], "Invalid nonce");
            bytes32 message = keccak256(abi.encodePacked(_to, _amount, _nonce));
            address signer = recoverSigner(message, _signature);
            
            require(balances[signer] >= _amount, "Insufficient balance");
            balances[signer] -= _amount;
            balances[_to] += _amount;
            
            nonces[msg.sender]++;
        }
    
        function recoverSigner(bytes32 message, bytes memory signature) internal pure returns (address) {
            // Implementation of signature recovery
            // ...
        }
    }
    
    

    In this improved version, we've introduced a nonce system. Each user has a nonce that increments with each transaction. The nonce is included in the signed message, ensuring that each signature can only be used once. This effectively prevents replay attacks.