Token Account Spoofing Remediation
How to fix token account spoofing and aliasing vulnerabilities.
Token Account Spoofing Remediation
Overview
Related Detector: Token Account Spoofing
Token account spoofing allows attackers to substitute fake token accounts with wrong mints or attacker-controlled owners. The fix requires validating mint, owner, and key for every token account, plus anti-aliasing checks when multiple accounts are involved.
Recommended Fix
Before (Vulnerable)
pub fn swap(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let token_a = &accounts[0]; // No validation!
let token_b = &accounts[1]; // No validation!
transfer_tokens(token_a, token_b, amount)?;
Ok(())
}
After (Fixed)
pub fn swap(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let token_a = &accounts[0];
let token_b = &accounts[1];
// Validate mint
let data_a = Account::unpack(&token_a.data.borrow())?;
let data_b = Account::unpack(&token_b.data.borrow())?;
if data_a.mint != EXPECTED_MINT_A || data_b.mint != EXPECTED_MINT_B {
return Err(ProgramError::InvalidAccountData);
}
// Validate owner
if data_a.owner != *accounts[2].key {
return Err(ProgramError::IllegalOwner);
}
// Prevent aliasing
if token_a.key == token_b.key {
return Err(ProgramError::InvalidArgument);
}
transfer_tokens(token_a, token_b, amount)?;
Ok(())
}
Alternative Mitigations
1. Anchor token constraints
#[derive(Accounts)]
pub struct Swap<'info> {
#[account(mut, token::mint = mint_a, token::authority = user)]
pub token_a: Account<'info, TokenAccount>,
#[account(mut, token::mint = mint_b,
constraint = token_a.key() != token_b.key() @ ErrorCode::Aliasing)]
pub token_b: Account<'info, TokenAccount>,
}
2. Associated Token Account derivation
Use ATAs to ensure the correct token account is used for a given owner and mint:
let expected_ata = get_associated_token_address(user.key, &mint);
require!(token_account.key() == expected_ata, ErrorCode::InvalidTokenAccount);
Common Mistakes
Mistake 1: Checking owner but not mint
// INCOMPLETE: correct owner but wrong mint allows worthless token spoofing
if data.owner != *user.key { return Err(...); }
// Missing: if data.mint != EXPECTED_MINT { return Err(...); }
Mistake 2: No anti-aliasing check
// WRONG: if source == destination, accounting breaks
// Must check: require!(source.key() != destination.key())
Mistake 3: Validating only one of multiple token accounts
// INCOMPLETE: token_a validated but token_b is not