Secondary Signer Validation Remediation
How to fix missing signer validation on identity-checked accounts.
Remediating Missing Secondary Signer Validation
Overview
Related Detector: Secondary Signer Validation
When an account is validated by address (CheckKey) but not by signer status (CheckSigner), anyone can reference that address without proving they control it. The fix is to add a signer check wherever an address check exists for authorization-critical accounts.
Recommended Fix
Before (Vulnerable)
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
const AUTHORITY: Pubkey = /* authority address */;
pub fn authorized_action(accounts: &[AccountInfo]) -> ProgramResult {
let authority = &accounts[0];
// Only checks address -- anyone can pass AUTHORITY without signing
if authority.key != &AUTHORITY {
return Err(ProgramError::InvalidArgument);
}
perform_sensitive_operation()?;
Ok(())
}
After (Fixed)
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
const AUTHORITY: Pubkey = /* authority address */;
pub fn authorized_action(accounts: &[AccountInfo]) -> ProgramResult {
let authority = &accounts[0];
// Check identity
if authority.key != &AUTHORITY {
return Err(ProgramError::InvalidArgument);
}
// Check signer status
if !authority.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
perform_sensitive_operation()?;
Ok(())
}
Alternative Mitigations
- Use Anchor’s
Signertype: TheSigner<'info>account type enforces signer validation automatically.
#[derive(Accounts)]
pub struct AuthorizedAction<'info> {
#[account(constraint = authority.key() == AUTHORITY @ ErrorCode::Unauthorized)]
pub authority: Signer<'info>,
}
-
PDA-based authorization: For program-controlled operations, use PDA signing via
invoke_signedinstead of relying on external signers. PDAs are validated by derivation, not by signer checks. -
Multi-sig pattern: For operations requiring multiple approvals, check signer status on all required co-signers, not just the primary signer.
if !co_signer_a.is_signer || !co_signer_b.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
Common Mistakes
- Adding signer check to PDA accounts: PDAs cannot sign transactions in the traditional sense. They sign via
invoke_signedduring CPI. Addingis_signerchecks to PDA accounts will cause legitimate transactions to fail. - Checking signer on the wrong account: In multi-account instructions, ensure the signer check is on the authorization account, not on a data account or program account.
- Relying on address check for authorization: Address checks prove identity, not intent. A known admin address can be referenced by anyone; only a signer check proves the admin actively authorized the transaction.
- Missing signer check on secondary approvers: In multi-sig scenarios, developers often check the primary signer but forget secondary approvers. All co-signers must have both identity and signer validation.