SPL Token Authority Confusion Remediation
How to fix authority confusion between owner, delegate, close, mint, and freeze authorities.
SPL Token Authority Confusion Remediation
Overview
Related Detector: SPL Token Authority Confusion
Authority confusion occurs when a program checks the wrong authority for a token operation — for example, validating owner but performing a close that requires close_authority, or using Transfer instead of TransferChecked. The fix requires matching each operation to its correct authority and using checked instruction variants.
Recommended Fix
Before (Vulnerable)
// Uses Transfer (no decimal check) and doesn't verify authority
let ix = spl_token::instruction::transfer(
&spl_token::id(), source.key, dest.key, authority.key, &[], amount,
)?;
invoke(&ix, accounts)?;
After (Fixed)
// Verify authority signed
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// Use TransferChecked for decimal validation
let ix = spl_token::instruction::transfer_checked(
&spl_token::id(), source.key, mint.key, dest.key,
authority.key, &[], amount, expected_decimals,
)?;
invoke(&ix, &[source.clone(), mint.clone(), dest.clone(), authority.clone()])?;
Alternative Mitigations
1. Anchor typed accounts
#[derive(Accounts)]
pub struct Transfer<'info> {
#[account(mut, token::authority = authority)]
pub source: Account<'info, TokenAccount>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
Anchor’s token::authority constraint validates the correct authority at deserialization.
2. Explicit authority type matching
For operations that can use either owner or delegate, explicitly check which authority type is provided:
let token_data = Account::unpack(&token_account.data.borrow())?;
if *authority.key == token_data.owner {
// Owner transfer -- full balance available
} else if token_data.delegate == COption::Some(*authority.key) {
// Delegated transfer -- limited to delegated_amount
require!(amount <= token_data.delegated_amount, InsufficientDelegation);
} else {
return Err(ProgramError::InvalidAccountData);
}
Common Mistakes
Mistake 1: Using Transfer instead of TransferChecked
// WRONG: Transfer does not validate decimals
spl_token::instruction::transfer(...)?;
// CORRECT: TransferChecked validates decimal precision
spl_token::instruction::transfer_checked(..., amount, decimals)?;
Mistake 2: Confusing mint_authority with freeze_authority
// WRONG: using freeze_authority to mint tokens
// mint_authority and freeze_authority are different fields on the Mint account
Mistake 3: Using owner for close when close_authority is set
// WRONG: if close_authority is set, owner cannot close
// Must check: if token_account.close_authority.is_some(), use that authority