Skip to content

Latest commit

 

History

History
145 lines (106 loc) · 6.06 KB

ownership_check.md

File metadata and controls

145 lines (106 loc) · 6.06 KB

SIGNER AUTHORIZATION:

A "missing owner check" vulnerability in the context of Solana Anchor refers to a situation where a program does not properly validate the ownership of an account before allowing certain operations to be performed on that account. This can lead to unauthorized access or manipulation of accounts by malicious actors, which can have serious security and financial implications.

  1. Improper Ownership Validation: In Solana Anchor programs, each account is associated with a specific owner, usually identified by a public key. Programs often need to verify that the caller or initiator of an operation has the necessary ownership rights over a particular account.

  2. Missing or Insufficient Validation: The vulnerability occurs when a program fails to properly validate the ownership of an account before allowing a certain operation to proceed. This could be due to missing checks or incorrect assumptions about the ownership of the account.

  3. Potential Consequences:

  • Unauthorized Operations: Malicious actors could take advantage of the vulnerability to perform operations on accounts they do not own. For example, an attacker could manipulate another user's account, steal funds, or disrupt the functionality of the program.

  • Data Manipulation: If the program relies on certain account data being controlled by specific owners, a missing owner check could lead to unauthorized modification of critical data, leading to incorrect program behavior or compromised security.

  • Financial Loss: Unauthorized access to accounts can result in the theft of funds, which can lead to financial losses for users of the program.

Consider following code but here the function check_owner can be called by anyone as there is no owner check implemented

// lib.rs

use anchor_lang::prelude::*;

declare_id!("YourProgramIDGoesHere"); // Replace this with your actual program ID

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

    pub const PROGRAM_ID: Pubkey = id!("YourProgramIDGoesHere"); // Replace this with your actual program ID

    #[derive(Accounts)]
    pub struct MissingOwnerCheck<'info> {
        #[account(signer)]
        pub owner: AccountInfo<'info>, // Account to check the owner
    }

    #[error]
    pub enum ErrorCode {
        #[msg("Incorrect owner provided")]
        IncorrectOwner,
    }

    pub fn check_owner(ctx: Context<CheckOwner>, expected_owner: Pubkey) -> ProgramResult {
        // Verify if the owner provided matches the expected owner
        msg!("You can access the message")

        Ok(())
    }
}

By adding the following check the issue with ownership check can be mitigated very easily.

pub fn check_owner(ctx: Context<CheckOwner>, expected_owner: Pubkey) -> ProgramResult {
        // Verify if the owner provided matches the expected owner
        if *ctx.accounts.owner.key != expected_owner {
            return Err(ErrorCode::IncorrectOwner.into());
        }
        msg!("You can access the message")

        Ok(())
    }

Mitigation Strategies:

  • Proper Owner Validation: Always ensure that the ownership of accounts is properly validated before performing any sensitive operations. This involves checking if the provided public key matches the actual owner's public key.

  • Context and Signer Accounts: Solana Anchor provides the Context struct, which contains information about the transaction and its signer accounts. Use this information to verify the signer's identity and their relationship to the accounts involved.

  • Access Control Best Practices: Follow access control best practices, such as using the #[account(signer)] attribute to specify that an account must be signed by its owner, or using #[account(has_one = owner)] to ensure an account is controlled by a particular signer.

  • Testing: Rigorous testing, including both unit tests and integration tests, can help identify missing owner checks and other vulnerabilities in your program.

  • Code Review: Conduct thorough code reviews to catch any instances where ownership validation might be missing or insufficiently implemented.

  • Documentation: Clearly document the expected ownership requirements for each account and operation in your program to make sure other developers are aware of the security requirements.

In summary, the missing owner check vulnerability in Solana Anchor can lead to unauthorized access and manipulation of accounts, potentially resulting in financial loss and compromised security. Implementing proper owner validation, following access control best practices, and conducting thorough testing and code reviews are essential to mitigate this vulnerability.

Let's understand this with example

Consider the following code where user is allowed to update the value. initialize function allows to set the the value of the data of the account my_account update function allows user to update the value of the data

use anchor_lang::prelude::*;

declare_id!("9b9M3AMGMM2yE9P9WgDfyvXqrBvnfcrjAcVvdETymb6n");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = 0; 
        Ok(())
    }

    pub fn update(ctx: Context<Update>, data: u32) -> Result<()> {
        let my_account = &mut ctx.accounts.my_account;
        my_account.data = data; 
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    my_account: AccountInfo<'info, MyAccount>,
    user: Signer<'info>,
    system_program: SystemProgram<'info>,
}

#[derive(Accounts)]
pub struct Update<'info> {
    my_account: AccountInfo<'info, MyAccount>,
}

#[account]
pub struct MyAccount {
    data: u32,
}

Let's check the results after running the test:

ownership-check
  ✔ Is Initialize (80ms)
  ✔ update the value  (45ms)

Now let's update the code with secure measures:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(constraint = user.key == &my_account.owner)]
    my_account: AccountInfo<'info, MyAccount>,
    user: Signer<'info>,
    system_program: SystemProgram<'info>,
}

You can check the code HERE