Hash Collision Remediation
Overview
abi.encodePacked concatenates values without length prefixes, so abi.encodePacked("ab", "c") and abi.encodePacked("a", "bc") produce identical bytes. When two or more dynamic arguments (strings, bytes, dynamic arrays) are packed and then hashed for signatures, Merkle proofs, or storage keys, an attacker can craft different inputs that hash to the same value, bypassing authentication or forging proofs.
Related Detector: Hash Collision
Recommended Fix
Before (Vulnerable)
bytes32 hash = keccak256(abi.encodePacked(name, symbol));
After (Fixed)
bytes32 hash = keccak256(abi.encode(name, symbol));
abi.encode writes a length and offset header for each dynamic argument, so the boundary between arguments is unambiguous. Different argument splits produce different encodings and therefore different hashes, closing the collision vector. The encoding is slightly larger but the security guarantee is worth it for any hash used in a trust decision.
Alternative Mitigations
- If you must use packed encoding (for example, to match an off-chain format), insert a fixed-length separator or length field between dynamic arguments yourself.
- Place at most one dynamic argument last in a packed encoding; a single trailing dynamic value cannot be ambiguously split.
- For typed-data signatures, follow EIP-712, which defines an unambiguous structured hashing scheme.
Common Mistakes
- Switching to
abi.encodein one code path but leaving the off-chain signer (or another contract) still using packed encoding, breaking verification. - Assuming a single dynamic argument is safe to pack alongside a leading dynamic argument; the ambiguity comes from adjacency of two or more.
- Replicating packed concatenation manually and reintroducing the same collision.