PDA Seed Validation Remediation
How to fix incorrect PDA seed derivation patterns.
PDA Seed Validation Remediation
Overview
Related Detector: PDA Seed Validation
Incorrect PDA seed derivation causes account confusion and authorization bypasses. The fix is to follow a consistent seed pattern: discriminator first, then entity identifiers, using only deterministic data.
Recommended Fix
Before (Vulnerable)
// Wrong order, missing discriminator
let (pda, _) = find_program_address(&[user.key.as_ref()], program_id);
After (Fixed)
// Discriminator first, then entity keys
let (pda, _) = find_program_address(
&[b"user-vault", user.key.as_ref()],
program_id,
);
Alternative Mitigations
1. Standardized seed template
Adopt a consistent pattern across the program:
// Pattern: [b"type", authority, entity, optional_qualifier]
let (vault, _) = find_program_address(&[b"vault", user.as_ref(), mint.as_ref()], pid);
let (escrow, _) = find_program_address(&[b"escrow", user.as_ref(), counterparty.as_ref()], pid);
let (config, _) = find_program_address(&[b"config"], pid);
2. Anchor seeds constraints
#[account(
seeds = [b"vault", user.key().as_ref(), mint.key().as_ref()],
bump = vault.bump,
)]
pub vault: Account<'info, Vault>,
3. Avoid non-deterministic seeds
// WRONG: timestamp makes PDA non-deterministic
let (pda, _) = find_program_address(&[b"session", &ts.to_le_bytes()], pid);
// CORRECT: use deterministic identifiers
let (pda, _) = find_program_address(&[b"session", user.as_ref()], pid);
Common Mistakes
Mistake 1: Too many seeds
// Excessive: 5+ seeds increase collision risk and gas cost
let (pda, _) = find_program_address(&[s1, s2, s3, s4, s5, s6], pid);
// Keep to 1-4 seed components
Mistake 2: Inconsistent ordering across functions
// Function A: [b"vault", user, mint]
// Function B: [b"vault", mint, user] -- WRONG ORDER
Maintain the same seed order everywhere a PDA type is referenced.