Bump Seed Brute Force Remediation
How to fix user-controlled bump seeds in PDA derivation.
Bump Seed Brute Force Remediation
Overview
Related Detector: Bump Seed Brute Force
Bump seed brute force vulnerabilities allow attackers to supply non-canonical PDA bumps, creating alternative valid PDAs that point to attacker-controlled accounts. The fix is to always derive and validate the canonical bump using find_program_address.
Recommended Fix
Before (Vulnerable)
let bump = instruction_data[32]; // User-controlled
let pda = create_program_address(&[b"vault", user, &[bump]], pid)?;
After (Fixed)
let (pda, canonical_bump) = find_program_address(&[b"vault", user], pid);
// Use canonical_bump, never accept bump from user input
Alternative Mitigations
1. Validate user bump against canonical
If you must accept bumps from input (e.g., for gas optimization with stored bumps):
let user_bump = instruction_data[32];
let (_, canonical_bump) = find_program_address(&[b"vault", user], pid);
if user_bump != canonical_bump {
return Err(ProgramError::InvalidSeeds);
}
let pda = create_program_address(&[b"vault", user, &[user_bump]], pid)?;
2. Store canonical bump at initialization
// During init: derive and store canonical bump
let (pda, bump) = find_program_address(&[b"vault", user], pid);
state.bump = bump;
// During use: load stored canonical bump
let stored_bump = state.bump;
let pda = create_program_address(&[b"vault", user, &[stored_bump]], pid)?;
3. Anchor bump storage
#[account(
init,
seeds = [b"vault", user.key().as_ref()],
bump,
payer = user,
)]
pub vault: Account<'info, Vault>,
// Vault struct stores the bump
#[account]
pub struct Vault {
pub bump: u8,
// ...
}
Common Mistakes
Mistake 1: Accepting bump from instruction data without validation
// WRONG: no validation against canonical
let bump = data[0];
let pda = create_program_address(&seeds_with_bump, pid)?;
Mistake 2: Using find_program_address only for the address, ignoring the bump
let (expected_pda, _) = find_program_address(&seeds, pid); // Bump discarded!
let user_bump = data[0];
let pda = create_program_address(&[..seeds, &[user_bump]], pid)?;
// Even if pda == expected_pda check passes, non-canonical bumps may exist
Mistake 3: Storing a user-provided bump
// WRONG: storing unvalidated bump
state.bump = instruction_data[32]; // Could be non-canonical
Always derive the canonical bump before storing.