Skip to main content
Sigvex

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.

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

the account-validation framework’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.

References