Metaplex Authority Bypass Remediation
How to fix missing update authority verification in Metaplex metadata operations.
Metaplex Authority Bypass Remediation
Overview
Related Detector: Metaplex Authority Bypass
A Metaplex authority bypass allows unauthorized callers to modify NFT metadata — name, symbol, URI, creators, royalties — by invoking Metaplex Token Metadata program instructions without the update_authority signing the transaction. The fix requires verifying the signer matches the metadata’s update_authority and checking the is_mutable flag before any modification.
Recommended Fix
Before (Vulnerable)
pub fn update_nft_uri(accounts: &[AccountInfo], new_uri: String) -> ProgramResult {
let metadata_program = &accounts[0];
let metadata = &accounts[1];
let authority = &accounts[2];
// VULNERABLE: no check that authority == metadata.update_authority
// VULNERABLE: no is_mutable check
let ix = update_metadata_accounts_v2(
*metadata_program.key, *metadata.key, *authority.key,
None, Some(DataV2 { uri: new_uri, ..Default::default() }), None,
);
invoke(&ix, accounts)?;
Ok(())
}
After (Fixed)
pub fn update_nft_uri(accounts: &[AccountInfo], new_uri: String) -> ProgramResult {
let metadata_program = &accounts[0];
let metadata_account = &accounts[1];
let authority = &accounts[2];
// Verify authority signed the transaction
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// Verify signer matches the metadata's update_authority
let metadata = Metadata::from_account_info(metadata_account)?;
if metadata.update_authority != *authority.key {
return Err(MetadataError::InvalidUpdateAuthority.into());
}
// Verify metadata is still mutable
if !metadata.is_mutable {
return Err(MetadataError::DataIsImmutable.into());
}
let ix = update_metadata_accounts_v2(
*metadata_program.key, *metadata_account.key, *authority.key,
None, Some(DataV2 { uri: new_uri, ..Default::default() }), None,
);
invoke(&ix, accounts)?;
Ok(())
}
Alternative Mitigations
1. Anchor has_one constraint
#[derive(Accounts)]
pub struct UpdateMetadata<'info> {
pub authority: Signer<'info>,
#[account(has_one = update_authority @ ErrorCode::Unauthorized)]
pub metadata: Account<'info, MetadataAccount>,
}
2. Collection authority delegation
For collection-level operations, use Metaplex’s ApproveCollectionAuthority to delegate specific capabilities instead of sharing the update authority.
3. Make metadata immutable after setup
Call UpdateMetadataAccountV2 with is_mutable: false once metadata is finalized to prevent future modifications entirely.
Common Mistakes
Mistake 1: Checking signer but not matching against update_authority
// INCOMPLETE: any signer can modify metadata
if !authority.is_signer { return Err(...); }
// Missing: authority.key == metadata.update_authority
Mistake 2: Skipping is_mutable check
// RISKY: allows modifying metadata marked as immutable
// The Metaplex program itself checks is_mutable, but your program
// should validate first to provide clear error messages
Mistake 3: Using UncheckedAccount for authority in Anchor
// WRONG: bypasses Anchor's automatic signer validation
pub authority: UncheckedAccount<'info>, // Should be Signer<'info>