Hash Collision Remediation
How to fix hash collision vulnerabilities caused by truncation, weak algorithms, or small inputs.
Hash Collision Remediation
Overview
Related Detector: Hash Collision
Hash collision vulnerabilities arise when programs truncate hash outputs, use weak algorithms, or hash small inputs. The fix is to use full-length outputs from secure hash functions (SHA-256, Keccak-256, Blake3) and add domain separation to prevent cross-context collisions.
Recommended Fix
Before (Vulnerable)
pub fn create_identifier(data: &[u8]) -> u32 {
let hash = solana_program::hash::hash(data);
// VULNERABLE: truncating 256-bit hash to 32 bits
u32::from_le_bytes(hash.to_bytes()[..4].try_into().unwrap())
}
After (Fixed)
pub fn create_identifier(data: &[u8]) -> [u8; 32] {
// FIXED: use full 256-bit hash output
solana_program::hash::hash(data).to_bytes()
}
Alternative Mitigations
1. Domain separation for cross-context safety
Prefix hash inputs with a domain tag to prevent collisions across different uses:
pub fn hash_for_merkle(leaf: &[u8]) -> [u8; 32] {
let mut input = Vec::with_capacity(leaf.len() + 7);
input.extend_from_slice(b"MERKLE:");
input.extend_from_slice(leaf);
solana_program::hash::hash(&input).to_bytes()
}
pub fn hash_for_dedup(tx_data: &[u8]) -> [u8; 32] {
let mut input = Vec::with_capacity(tx_data.len() + 6);
input.extend_from_slice(b"DEDUP:");
input.extend_from_slice(tx_data);
solana_program::hash::hash(&input).to_bytes()
}
2. Use HKDF for derived identifiers
When shorter identifiers are needed, use a key derivation function rather than truncation:
use hmac::{Hmac, Mac};
use sha2::Sha256;
fn derive_short_id(key: &[u8], data: &[u8], length: usize) -> Vec<u8> {
let mut mac = Hmac::<Sha256>::new_from_slice(key)
.expect("HMAC key length");
mac.update(data);
let result = mac.finalize().into_bytes();
result[..length].to_vec()
}
3. Add entropy to small inputs
pub fn hash_with_nonce(small_data: &[u8], nonce: &[u8; 32]) -> [u8; 32] {
let mut input = Vec::with_capacity(small_data.len() + 32);
input.extend_from_slice(nonce);
input.extend_from_slice(small_data);
solana_program::hash::hash(&input).to_bytes()
}
Common Mistakes
Mistake 1: Using bitwise AND for “short IDs”
// WRONG: masking reduces output space to 2^N bits
let short_id = full_hash & 0xFFFF; // Only 65,536 possible values
Use the full hash for security-critical operations. If short IDs are needed for display, do not use them for security decisions.
Mistake 2: Using CRC32 or custom hash functions
// WRONG: CRC32 is not a cryptographic hash
let checksum = crc32::checksum_ieee(data);
Always use Solana’s built-in cryptographic hash syscalls: sol_sha256, sol_keccak256, or sol_blake3.
Mistake 3: Comparing truncated hashes for equality
// WRONG: comparing only first 8 bytes
if computed_hash[..8] == expected_hash[..8] {
authorize_access()?;
}
Compare the full hash output for security decisions.