-
-
Save dhl/b71b2379d796f973c05df61c47338ae6 to your computer and use it in GitHub Desktop.
Solana - Security for Builders
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use borsh::{BorshDeserialize, BorshSerialize}; | |
use solana_program::{ | |
account_info::{next_account_info, AccountInfo}, | |
entrypoint, | |
entrypoint::ProgramResult, | |
msg, | |
program::invoke, | |
program_error::ProgramError, | |
pubkey::Pubkey, | |
rent::Rent, | |
system_instruction::create_account, | |
sysvar::Sysvar, | |
}; | |
entrypoint!(process_instruction); | |
pub fn process_instruction( | |
program_id: &Pubkey, // Current program ID being invoked | |
accounts: &[AccountInfo], // The list of accounts | |
instruction_data: &[u8], // The instruction to execute | |
) -> ProgramResult { | |
let instruction = Instructions::try_from_slice(instruction_data)?; | |
match instruction { | |
Instructions::Initialize { init_timestamp } => { | |
process_initialize(program_id, accounts, init_timestamp) | |
} | |
Instructions::CreateUser { user, amount } => { | |
process_create_user(program_id, accounts, user, amount) | |
} | |
} | |
} | |
pub fn process_initialize( | |
program_id: &Pubkey, | |
accounts: &[AccountInfo], | |
init_timestamp: u64, | |
) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let new_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
let system_program = next_account_info(accounts_iter)?; | |
if !signer.is_signer { | |
msg!("Signer account is not a valid signer."); | |
return Err(ProgramError::MissingRequiredSignature); | |
} | |
// No strict check for data or ownership to maintain the vulnerability | |
if new_account.owner != program_id && !new_account.data_is_empty() { | |
msg!("Target account already initialized or owned by another program."); | |
return Err(ProgramError::AccountAlreadyInitialized); | |
} | |
let account_data = Admin { | |
admin: *signer.key, | |
init_timestamp, | |
}; | |
let size = account_data.try_to_vec()?.len(); | |
let lamports = Rent::get()?.minimum_balance(size); | |
invoke( | |
&create_account( | |
signer.key, | |
new_account.key, | |
lamports, | |
size as u64, | |
program_id, | |
), | |
&[signer.clone(), new_account.clone(), system_program.clone()], | |
)?; | |
account_data.serialize(&mut *new_account.data.borrow_mut())?; | |
msg!("Initialized admin account with timestamp {}"); | |
Ok(()) | |
} | |
pub fn process_create_user( | |
program_id: &Pubkey, | |
accounts: &[AccountInfo], | |
user: Pubkey, | |
amount: u64, | |
) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let admin_account = next_account_info(accounts_iter)?; | |
if admin_account.owner != program_id { | |
msg!("Target admin account is owned by another program."); | |
return Err(ProgramError::InvalidAccountData); | |
} | |
let user_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
let system_program = next_account_info(accounts_iter)?; | |
let admin_data = Admin::try_from_slice(&admin_account.data.borrow())?; | |
if signer.key != &admin_data.admin { | |
msg!("Signer is not the admin of this account."); | |
return Err(ProgramError::InvalidAccountData); | |
} | |
if !signer.is_signer { | |
msg!("Signer account is not a valid signer."); | |
return Err(ProgramError::MissingRequiredSignature); | |
} | |
let user_account_data = User { user, amount }; | |
let size = user_account_data.try_to_vec()?.len(); | |
let lamports = Rent::get()?.minimum_balance(size); | |
invoke( | |
&create_account( | |
signer.key, | |
user_account.key, | |
lamports, | |
size as u64, | |
program_id, | |
), | |
&[signer.clone(), user_account.clone(), system_program.clone()], | |
)?; | |
user_account_data.serialize(&mut *user_account.data.borrow_mut())?; | |
msg!("Created user account with {} tokens.", amount); | |
Ok(()) | |
} | |
#[derive(BorshSerialize, BorshDeserialize)] | |
pub enum Instructions { | |
Initialize { init_timestamp: u64 }, | |
CreateUser { user: Pubkey, amount: u64 }, | |
} | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct Admin { | |
pub admin: Pubkey, | |
pub init_timestamp: u64, | |
} | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct User { | |
pub user: Pubkey, | |
pub amount: u64, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use borsh::{BorshDeserialize, BorshSerialize}; | |
use solana_program::{ | |
account_info::{next_account_info, AccountInfo}, | |
entrypoint, | |
entrypoint::ProgramResult, | |
msg, | |
program::invoke, | |
program_error::ProgramError, | |
pubkey::Pubkey, | |
rent::Rent, | |
system_instruction::create_account, | |
sysvar::Sysvar, | |
}; | |
entrypoint!(process_instruction); | |
pub fn process_instruction( | |
program_id: &Pubkey, // Current program ID being invoked | |
accounts: &[AccountInfo], // The list of accounts | |
instruction_data: &[u8], // The instruction to execute | |
) -> ProgramResult { | |
let instruction = Instructions::try_from_slice(instruction_data)?; | |
match instruction { | |
Instructions::Initialize {} => process_initialize(program_id, accounts), | |
Instructions::CreateUser { user } => process_create_user(program_id, accounts, user), | |
} | |
} | |
pub fn process_initialize(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let new_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
let system_program = next_account_info(accounts_iter)?; | |
if !signer.is_signer { | |
msg!("Signer account is not a valid signer."); | |
return Err(ProgramError::MissingRequiredSignature); | |
} | |
if new_account.owner != program_id && !new_account.data_is_empty() { | |
msg!("Target account already initialized or owned by another program."); | |
return Err(ProgramError::AccountAlreadyInitialized); | |
} | |
let account_data = Admin { admin: *signer.key }; | |
let size = account_data.try_to_vec()?.len(); | |
let lamports = Rent::get()?.minimum_balance(size); | |
invoke( | |
&create_account( | |
signer.key, | |
new_account.key, | |
lamports, | |
size as u64, | |
program_id, | |
), | |
&[signer.clone(), new_account.clone(), system_program.clone()], | |
)?; | |
account_data.serialize(&mut *new_account.data.borrow_mut())?; | |
msg!("Initialized account"); | |
Ok(()) | |
} | |
pub fn process_create_user( | |
program_id: &Pubkey, | |
accounts: &[AccountInfo], | |
user: Pubkey, | |
) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let admin_account = next_account_info(accounts_iter)?; | |
let user_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
let system_program = next_account_info(accounts_iter)?; | |
let admin_data = Admin::try_from_slice(&admin_account.data.borrow())?; | |
if signer.key != &admin_data.admin { | |
msg!("Signer is not the admin of this account."); | |
return Err(ProgramError::InvalidAccountData); | |
} | |
if !signer.is_signer { | |
msg!("Signer account is not a valid signer."); | |
return Err(ProgramError::MissingRequiredSignature); | |
} | |
let user_account_data = User { user: user }; | |
let size = user_account_data.try_to_vec()?.len(); | |
let lamports = Rent::get()?.minimum_balance(size); | |
invoke( | |
&create_account( | |
signer.key, | |
user_account.key, | |
lamports, | |
size as u64, | |
program_id, | |
), | |
&[signer.clone(), user_account.clone(), system_program.clone()], | |
)?; | |
user_account_data.serialize(&mut *user_account.data.borrow_mut())?; | |
msg!("New user account"); | |
Ok(()) | |
} | |
#[derive(BorshSerialize, BorshDeserialize)] | |
pub enum Instructions { | |
Initialize {}, | |
CreateUser { user: Pubkey }, | |
} | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct Admin { | |
pub admin: Pubkey, | |
} | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct User { | |
pub user: Pubkey, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use borsh::{BorshDeserialize, BorshSerialize}; | |
use solana_program::{ | |
account_info::{next_account_info, AccountInfo}, | |
entrypoint, | |
entrypoint::ProgramResult, | |
msg, | |
program::invoke, | |
program_error::ProgramError, | |
pubkey::Pubkey, | |
rent::Rent, | |
system_instruction::create_account, | |
sysvar::Sysvar, | |
}; | |
entrypoint!(process_instruction); | |
pub fn process_instruction( | |
program_id: &Pubkey, // Current program ID being invoked | |
accounts: &[AccountInfo], // The list of accounts | |
instruction_data: &[u8], // The instruction to execute | |
) -> ProgramResult { | |
let instruction = Instructions::try_from_slice(instruction_data)?; | |
match instruction { | |
Instructions::Initialize { data } => process_initialize(program_id, accounts, data), | |
Instructions::UpdateData { data } => process_update_data(program_id, accounts, data), | |
} | |
} | |
pub fn process_initialize( | |
program_id: &Pubkey, | |
accounts: &[AccountInfo], | |
data: u64, | |
) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let new_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
let system_program = next_account_info(accounts_iter)?; | |
if !signer.is_signer { | |
msg!("Signer account is not a valid signer."); | |
return Err(ProgramError::MissingRequiredSignature); | |
} | |
if new_account.owner != program_id && !new_account.data_is_empty() { | |
msg!("Target account already initialized or owned by another program."); | |
return Err(ProgramError::AccountAlreadyInitialized); | |
} | |
let account_data = DataAccount { | |
admin: *signer.key, | |
data, | |
}; | |
let size = account_data.try_to_vec()?.len(); | |
let lamports = Rent::get()?.minimum_balance(size); | |
invoke( | |
&create_account( | |
signer.key, | |
new_account.key, | |
lamports, | |
size as u64, | |
program_id, | |
), | |
&[signer.clone(), new_account.clone(), system_program.clone()], | |
)?; | |
account_data.serialize(&mut *new_account.data.borrow_mut())?; | |
msg!("Initialized account with data: {:?}", data); | |
Ok(()) | |
} | |
pub fn process_update_data( | |
program_id: &Pubkey, | |
accounts: &[AccountInfo], | |
data: u64, | |
) -> ProgramResult { | |
let accounts_iter = &mut accounts.iter(); | |
let data_account = next_account_info(accounts_iter)?; | |
let signer = next_account_info(accounts_iter)?; | |
if data_account.owner != program_id { | |
msg!("Account is not owned by this program."); | |
return Err(ProgramError::IncorrectProgramId); | |
} | |
let mut account_data = DataAccount::try_from_slice(&data_account.data.borrow())?; | |
if signer.key != &account_data.admin { | |
msg!("Signer is not the admin of this account."); | |
return Err(ProgramError::InvalidAccountData); | |
} | |
account_data.data = data; | |
account_data.serialize(&mut *data_account.data.borrow_mut())?; | |
msg!("Updated account data to: {:?}", data); | |
Ok(()) | |
} | |
#[derive(BorshSerialize, BorshDeserialize)] | |
pub enum Instructions { | |
Initialize { data: u64 }, | |
UpdateData { data: u64 }, | |
} | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct DataAccount { | |
pub admin: Pubkey, | |
pub data: u64, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Native Solana security issues | |
This gist shows 3 vulnerable code examples in Native Rust Solana programs. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment