Mint Authority Abuse Remediation
How to validate mint authority before token minting operations to prevent unauthorized supply inflation.
Mint Authority Abuse Remediation
Overview
Related Detector: Mint Authority Abuse
Mint authority abuse vulnerabilities occur when a program mints tokens via CPI without verifying that the provided authority account is the actual mint authority recorded on the mint account. The fix requires validating both the signer status and the key match against the mint’s mint_authority field before any mint CPI.
Recommended Fix
Before (Vulnerable)
pub fn reward_user(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let user_token = &accounts[1];
let authority = &accounts[2]; // Caller-supplied, not validated
let token_program = &accounts[3];
let ix = spl_token::instruction::mint_to(
token_program.key, mint.key, user_token.key,
authority.key, &[], amount,
)?;
invoke(&ix, accounts)?;
Ok(())
}
After (Fixed)
pub fn reward_user(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let mint = &accounts[0];
let user_token = &accounts[1];
let authority = &accounts[2];
let token_program = &accounts[3];
// FIXED: verify signer
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// FIXED: verify authority matches the mint's mint_authority
let mint_data = mint.try_borrow_data()?;
let mint_state = spl_token::state::Mint::unpack(&mint_data)?;
match mint_state.mint_authority {
COption::Some(expected_authority) => {
if expected_authority != *authority.key {
return Err(ProgramError::InvalidAccountData);
}
}
COption::None => {
return Err(ProgramError::InvalidAccountData); // Mint has no authority
}
}
let ix = spl_token::instruction::mint_to(
token_program.key, mint.key, user_token.key,
authority.key, &[], amount,
)?;
invoke(&ix, accounts)?;
Ok(())
}
Alternative Mitigations
1. PDA mint authority
Use a PDA as the mint authority so the program itself controls minting:
// Derive PDA as mint authority
let (mint_authority_pda, bump) = Pubkey::find_program_address(
&[b"mint_authority", mint.key.as_ref()],
program_id,
);
// Mint using PDA signer seeds
let seeds = &[b"mint_authority", mint.key.as_ref(), &[bump]];
let ix = spl_token::instruction::mint_to(
token_program.key, mint.key, destination.key,
&mint_authority_pda, &[], amount,
)?;
invoke_signed(&ix, accounts, &[seeds])?;
2. Anchor mint constraint
Anchor’s #[account] constraints handle mint authority validation automatically:
#[derive(Accounts)]
pub struct MintReward<'info> {
#[account(mut)]
pub mint: Account<'info, Mint>,
#[account(mut)]
pub destination: Account<'info, TokenAccount>,
/// Anchor validates: authority.key == mint.mint_authority
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
Common Mistakes
Mistake 1: Checking signer but not matching against mint authority
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// WRONG: signer check alone is insufficient
// Anyone can sign -- must also verify key matches mint.mint_authority
invoke(&mint_ix, accounts)?;
Mistake 2: Checking mint authority after minting
invoke(&mint_ix, accounts)?; // Tokens already minted
// Too late to check
let mint_state = Mint::unpack(&mint.data.borrow())?;
assert_eq!(mint_state.mint_authority.unwrap(), *authority.key);
Mistake 3: Hardcoding the authority key instead of reading from mint
const ADMIN_KEY: Pubkey = pubkey!("Admin...");
if authority.key != &ADMIN_KEY {
return Err(ProgramError::InvalidAccountData);
}
This fails if the mint authority is ever rotated. Read the authority from the mint account data instead.