Skip to main content

On This Page

Two Questions That Defend Solana Accounts: Owner Check and Signer Verification

2 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

The shift: from “what did I mean” to “what did I forget to forbid”

“When you write a program, you imagine the user you designed for. An attacker is every user you didn’t.” Account security on Solana collapses into two questions: who owns this account, and did the authority actually sign?

Why This Matters

On Solana, every instruction hands your program a list of accounts, and any of them can be attacker-controlled. Unlike a Web2 backend with a trusted database, there is no trusted edge—only what your code proves. This difference is behind some of the largest losses in Solana history, as missing owner or signer checks allowed attackers to forge configs or bypass permissions.

Key Insights

    • Owner check requirement: Every account whose data your program trusts must have its owner verified—forged accounts owned by the System Program are rejected (Solana protocol constraint).
    • Signer verification trap: Comparing an authority’s public key without checking its signature proves nothing; the pubkey is public on chain. The signature itself must be confirmed (bridge exploit pattern).
    • Anchor automation: Account<'info, T> checks owner + discriminator automatically; Signer<'info> verifies signature. Both prevent common exploit classes without manual code.
    • Escape hatch risks: UncheckedAccount skips all validation; used by teams who forget to implement hand-checks, leading to bugs (Anchor requires /// CHECK: comment).
    • Binding constraints: has_one, seeds, address, and constraint tie accounts together declaratively—rules enforced before handler logic runs.

Working Examples

#[derive(Accounts)]
pub struct Withdraw<'info> {
// owner + discriminator checked automatically
pub config: Account<'info, Config>,
// signature checked automatically
pub authority: Signer<'info>,
}
#[derive(Accounts)]
pub struct Withdraw<'info> {
pub authority: Signer<'info>,
#[account(
mut,
has_one = authority,
)]
pub config: Account<'info, Config>,
}

Practical Applications

    • Use case (Solana programs): Implement typed Anchor accounts (Account<T>) for all data structures—automatically verifies owner and discriminator before handler execution.
    • Pitfall (Common anti-pattern): Reaching for UncheckedAccount to make compilation faster—skipping both owner and signer checks leads directly to forgeable inputs.
    • Use case (Authority delegation): Use Signer type instead of comparing pubkeys manually—avoids the vulnerability where anyone can supply an admin’s known pubkey without signing.
    • Pitfall (Constraint neglect): Burying relational checks in handler logic instead of using Anchor’s has_one/seeds constraints—easily forgotten during refactors.

References:


Continue reading

Next article

Every FIFA World Cup Stadium Site Fails Security Check — Guardr Finds Weak CSP and Cookie Flaws

Related Content