General
Keamanan Program

Keamanan Program

Pemeriksaan Pemilik

Pemeriksaan pemilik adalah garis pertahanan pertama dalam keamanan program Solana. Pemeriksaan ini memverifikasi bahwa akun yang dimasukkan ke dalam handler instruksi benar-benar dimiliki oleh program yang diharapkan, mencegah penyerang mengganti akun tiruan yang berbahaya.

Setiap akun dalam struct AccountInfo Solana berisi field owner yang mengidentifikasi program mana yang mengendalikan akun tersebut. Pemeriksaan pemilik memastikan field owner ini cocok dengan program_id yang diharapkan sebelum program Anda mempercayai data akun tersebut.

Struct AccountInfo berisi beberapa field, termasuk owner, yang merepresentasikan program yang memiliki akun tersebut. Pemeriksaan pemilik memastikan bahwa field owner dalam AccountInfo cocok dengan program_id yang diharapkan.

Tanpa pemeriksaan pemilik, penyerang dapat membuat "replika" sempurna dari struktur data akun Anda, lengkap dengan diskriminator yang benar dan semua field yang tepat, dan menggunakannya untuk memanipulasi instruksi yang bergantung pada validasi data. Ini seperti seseorang membuat ID palsu yang terlihat identik dengan yang asli, tetapi dikendalikan oleh otoritas yang salah.

Pengecualian penting adalah ketika Anda memodifikasi data internal akun. Dalam kasus tersebut, runtime Solana secara otomatis mencegah program lain menulis ke akun yang bukan miliknya. Tetapi untuk operasi baca dan logika validasi, Anda harus melakukannya sendiri.

Anchor

Perhatikan instruksi rentan ini yang mengeksekusi logika berdasarkan owner dari sebuah program_account:

rust
#[program]
pub mod insecure_check{
    use super::*;
    //..
 
    pub fn instruction(ctx: Context<Instruction>) -> Result<()> {
        let account_data = ctx.accounts.program_account.try_borrow_data()?;
        let mut account_data_slice: &[u8] = &account_data;
        let account_state = ProgramAccount::try_deserialize(&mut account_data_slice)?;
 
        if account_state.owner != ctx.accounts.owner.key() {
            return Err(ProgramError::InvalidArgument.into());
        }
 
        //..do something
 
        Ok(())
    }
 
    //..
}
 
#[derive(Accounts)]
pub struct Instruction<'info> {
    pub owner: Signer<'info>,
   #[account(mut)]
    /// CHECK: This account will not be checked by Anchor
    pub program_account: UncheckedAccount<'info>,
 
}
 
#[account]
pub struct ProgramAccount {
    owner: Pubkey,
}

Tipe UncheckedAccount adalah cara Anchor untuk mengatakan "Saya tidak memeriksa apa pun, tangani dengan sangat hati-hati." Meskipun data akun mungkin terdeserialize dengan sempurna dan terlihat sah, pemeriksaan pemilik yang hilang menciptakan kerentanan kritis.

Penyerang dapat membuat akun mereka sendiri dengan struktur data yang identik dan meneruskannya ke instruksi Anda. Program Anda akan dengan senang hati memeriksa bidang kepemilikan, tetapi karena penyerang mengendalikan akun tersebut, mereka dapat melakukan apa pun yang mereka inginkan di dalam instruksi.

Perbaikannya sederhana namun penting: selalu verifikasi bahwa akun dimiliki oleh program Anda sebelum mempercayai isinya.

Ini sangat mudah dengan Anchor karena dimungkinkan untuk melakukan pemeriksaan ini langsung di struct akun dengan hanya mengubah UncheckedAccount menjadi ProgramAccount seperti ini:

rust
#[derive(Accounts)]
pub struct Instruction<'info> {
    pub owner: Signer<'info>,
   #[account(mut)]
    /// CHECK: This account will not be checked by Anchor
    pub program_account: Account<'info, ProgramAccount>,
 
}

Atau Anda dapat menambahkan batasan akun owner seperti ini:

rust
#[derive(Accounts)]
pub struct Instruction<'info> {
    pub owner: Signer<'info>,
   #[account(mut, owner = ID)]
    /// CHECK: This account will not be checked by Anchor
    pub program_account: UncheckedAccount<'info>,
 
}

Atau Anda dapat menambahkan pemeriksaan pemilik dalam instruksi menggunakan pemeriksaan ctx.accounts.program_account.owner seperti ini:

rust
pub fn update_ownership(ctx: Context<UpdateOwnership>) -> Result<()> {
    if ctx.accounts.program_account.owner != ID {
        return Err(ProgramError::IncorrectProgramId.into());
    }
    
    //..do something
 
    Ok(())
}

Dengan menambahkan pemeriksaan ini, handler instruksi hanya akan melanjutkan jika akun memiliki program_id yang benar. Jika akun tidak dimiliki oleh program kita, transaksi akan gagal.

Pinocchio

Dalam Pinocchio, karena kita tidak memiliki kemungkinan untuk menambahkan pemeriksaan keamanan langsung di dalam struct akun, kita terpaksa melakukannya dalam logika instruksi.

Kita dapat melakukannya dengan cara yang sangat mirip dengan anchor dengan menggunakan fungsi is_owned_by() seperti ini:

rust
if !self.accounts.owner.is_owned_by(ID) {
    return Err(ProgramError::IncorrectProgramId.into());
}
Daftar Isi
Lihat Sumber
Blueshift © 2025Commit: 96f50c6