Skip to main content
Sigvex

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.

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.

References