It is sometimes necessary to perform signature verification in smart contracts to achieve better usability or to save gas cost. A secure implementation needs to protect against Signature Replay Attacks by for example keeping track of all processed message hashes and only allowing new message hashes to be processed. A malicious user could attack a contract without such a control and get message hash that was sent by another user processed multiple times.
In order to protect against signature replay attacks consider the following recommendations:
- Store every message hash that has been processed by the smart contract. When new messages are received check against the already existing ones and only proceed with the business logic if it's a new message hash.
- Include the address of the contract that processes the message. This ensures that the message can only be used in a single contract.
- Include a nonce in the signature. Callers must sign their message using the current nonce, contracts must check the signed message against the current nonce, store the current nonce as used and increment it.
- Include the chain_id in the signature to prevent cross-chain replay attacks; contracts and addresses often have the same address across multiple chains, signatures which don't incorporate chain_id can be replayed from one chain onto other chains.
- Include all relevant parameters in the signature; a signature the enables the spending of tokens should include the exact amount as part of the signature.
- Include an expiration timestamp, such that the signature will cease to be valid after a user-supplied deadline.
- Signature implementions using ecrecover() directly must explicitly check the return of ecrecover() which returns 0 if the signature is invalid, and be especially careful when comparing the return of ecrecover() to a user-supplied value, as an attacker can supply 0 therefore passing such a check with an invalid signature.