Admin Key Management
Detects unsafe admin key handling including hardcoded keys, missing validation, and single points of failure.
Admin Key Management
Overview
Remediation Guide: How to Fix Admin Key Management Issues
The admin key management detector identifies unsafe handling of admin keys in Solana programs. It flags hardcoded admin keys embedded in program bytecode, privileged operations that execute without verifying the caller holds the admin key, and admin functions protected by only a single signer with no multi-signature requirement. These patterns create single points of failure that attackers can exploit to compromise the entire program.
Why This Is an Issue
Admin keys control the most sensitive operations in a Solana program — fund transfers, configuration changes, upgrades, and emergency actions. Unsafe handling leads to several attack vectors:
- Hardcoded admin keys (CWE-798) are immutable once deployed. If the private key is leaked, the program cannot rotate to a new admin. Attackers can extract hardcoded keys from on-chain bytecode.
- Missing admin validation (CWE-284) allows any caller to execute privileged operations such as draining funds or modifying program state.
- Single admin key (CWE-269) with no multi-sig requirement means one compromised key grants full control. There is no separation of duties or recovery mechanism.
How to Resolve
Native Solana
use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
pub fn process_admin_action(
accounts: &[AccountInfo],
program_id: &Pubkey,
) -> Result<(), ProgramError> {
let admin = &accounts[0];
let config_account = &accounts[1];
// 1. Verify signer
if !admin.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
// 2. Load admin key from on-chain config (NOT hardcoded)
let config_data = config_account.try_borrow_data()?;
let stored_admin = Pubkey::try_from(&config_data[0..32])
.map_err(|_| ProgramError::InvalidAccountData)?;
// 3. Validate caller matches stored admin
if *admin.key != stored_admin {
return Err(ProgramError::InvalidArgument);
}
// Safe to proceed with admin operation
Ok(())
}
Anchor
#[derive(Accounts)]
pub struct AdminAction<'info> {
#[account(
constraint = admin.key() == config.admin_authority @ ErrorCode::Unauthorized
)]
pub admin: Signer<'info>,
#[account(seeds = [b"config"], bump)]
pub config: Account<'info, ProgramConfig>,
}
#[account]
pub struct ProgramConfig {
pub admin_authority: Pubkey,
}
Examples
Vulnerable Code
// VULNERABLE: hardcoded admin key
const ADMIN_KEY: Pubkey = solana_program::pubkey!("HardCodedAdminPubkeyXXXXXXXXXXXXXXXXXXXXXXXX");
pub fn withdraw_all(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let vault = &accounts[0];
let destination = &accounts[1];
// No signer check, no admin validation
**vault.try_borrow_mut_lamports()? -= amount;
**destination.try_borrow_mut_lamports()? += amount;
Ok(())
}
Fixed Code
pub fn withdraw_all(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
let admin = &accounts[0];
let vault = &accounts[1];
let destination = &accounts[2];
let config = &accounts[3];
if !admin.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
let config_data = config.try_borrow_data()?;
let stored_admin = Pubkey::try_from(&config_data[0..32])
.map_err(|_| ProgramError::InvalidAccountData)?;
if *admin.key != stored_admin {
return Err(ProgramError::InvalidArgument);
}
**vault.try_borrow_mut_lamports()? -= amount;
**destination.try_borrow_mut_lamports()? += amount;
Ok(())
}
Sample Sigvex Output
{
"detector_id": "admin-key-management",
"severity": "critical",
"confidence": 0.78,
"title": "Hardcoded Admin Key",
"description": "Admin key is hardcoded in the program bytecode. This creates an immutable single point of failure. An attacker with the hardcoded key can compromise the entire program.",
"location": { "function": "admin_transfer", "block": 0, "statement": 0 },
"cwe": 798
}
Detection Methodology
The detector performs static analysis on the program’s intermediate representation:
- Admin key identification: Variables in the parameter range that represent caller-provided accounts are flagged as potential admin keys.
- Hardcoded constant detection: Assignments of constant values to admin-like variables indicate hardcoded keys embedded in bytecode.
- Validation tracking: Signer checks, key comparisons, and owner verifications are tracked to determine which accounts have been validated before use.
- Privileged operation scanning: State writes and lamport transfers are identified as privileged operations requiring admin validation.
- Gap analysis: Privileged operations performed on accounts without prior validation generate findings. Functions with a single verified admin key and an admin-like name trigger a multi-sig warning.
- Context adjustment: Confidence is reduced for Anchor programs (where constraints may handle access control) and read-only functions.
Limitations
False positives:
- Anchor programs where
Signerandhas_oneconstraints handle admin validation before the instruction handler executes. - Programs using PDA-based authority where the admin key is derived rather than stored directly.
False negatives:
- Admin keys stored in complex nested account structures that the heuristic does not trace.
- Multi-instruction transactions where admin validation occurs in a separate instruction.
Related Detectors
- Signer Authority Role — signer checks without authority role validation
- Instruction Sender Validation — missing sender authority for privileged operations
- Role-Based Access Control — missing role checks before privileged operations
- Multi-Sig Validation — weak or missing multi-signature requirements