◂ labs
TIER 1 · THE ACCOUNT MODEL · 2-owner-checks

OWNER CHECKS

Any program can create an account with any bytes in it. If you deserialize an AccountInfo without checking `account.owner == expected_program`, an attacker crafts a fake account that parses perfectly and drains the vault.

seen in the wild
Owner-check bypass = the "fake account" primitive behind most pre-Anchor exploits; Anchor `Account<T>` does it for you — AccountInfo never does.
rust · anchor
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_error::ProgramError;
use anchor_lang::solana_program::program_pack::Pack;
use spl_token::state::Account as SplTokenAccount;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod owner_checks_insecure {
    use super::*;

    pub fn log_message(ctx: Context<LogMessage>) -> ProgramResult {
        let token = SplTokenAccount::unpack(&ctx.accounts.token.data.borrow())?;
        if ctx.accounts.authority.key != &token.owner {
            return Err(ProgramError::InvalidAccountData);
        }
        msg!("Your account balance is: {}", token.amount);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct LogMessage<'info> {
    token: AccountInfo<'info>,
    authority: Signer<'info>,
}