Back to Blog

Secure the Solana Ecosystem (4) — Account Validation

April 1, 2022

0. Review

1. Overview

In the previous blog, we discussed how to upgrade a program. In this post, we will introduce the access control related problems, which is one of the most common and basic security topics in the area of DeFi.

2. Instructions

In Solana, each program exports one single entrypoint, which is defined with entrypoint!. Different from Ethereum, clients can only invoke one single function defined as the entrypoint, which is usually called process_instruction. The entrypoint function receives three parameters. They are the program ID of the smart contract, the accounts that the program will operate on, and the instruction data. The instruction data specifies which instruction will be invoked. The figure below shows an example. By unpacking the instruction data, different instructions (e.g., Lock, Unlock) are chosen. Thus, the instructions that can be reached from entrypoint are public to everyone and can be executed with specified instruction data.

3. Account Validation

As mentioned, the program receives the accounts that it needs to read or write. It brings two questions with this design. For the accounts to be read, how to guarantee that the data stored in the accounts are trusted. For the accounts to be written, how to guarantee only privileged users can invoke the instructions to write into the accounts. In the following, we illustrate the access control problem . All test codes can be found here.

3.1 Code Review (PrivilegeOwner)

We first define two structs and they are Door and Config. Only the key account (line 17) specified in the struct door can open the created door. However, the door can not be opened when the system state is locked, which is specified in structure Config (line 81).

As mentioned, the Config account specifies whether the door can be opened. In this case, there should be only one Config account in the program. To achieve this, we use PDA to store the data of Config. After initializing the Config account, we set the attribute is_initialized to be true so that it cannot be initialized again by attackers (line 108 - line 110).

Instruction Open() is used to open the doors. The instruction receives several accounts including the door account to be opened, the config account, and the owner account that aims to open the door. To guarantee the door belongs to the program and the configuration is valid, we check the owner of the door account and the config account (line 204 -line 205). This prevents malicious users from feeding fake accounts. This answers our first question. To guarantee the account to be read is trusted, we need to check the owner of the account!. Note that only the owner of the door account can open the door. In this case, we check whether the owner account is the real owner of the door and more importantly, whether the instruction is authorized by the owner (line 217 - line 219).

In function validate_owner(), we first check if the public key of these two accounts are same, and then check the signature of the owner. This answers the second question, to guarantee that only privileged users can invoke the open instruction, we need to check the owner and the signer of the account. The close instruction is similar to open and the details can be found in the code.

We deployed the program on testnet and it can be find in the following link.

https://explorer.solana.com/address/2Q7FFMWCthBvc6ubLQRx9TRswvaimmd66VaCAfHwsYuC?cluster=testnet

All the test transactions are listed below. The whole process of this transaction is AllocatePDA()-> InitializeDoor()-> InitializeConfig()-> Unlock() -> Open() -> Close().

https://explorer.solana.com/tx/2X9CyMrHTNEvbzXTE95gem2j8spnvsQsabFeSpV8hiNpYjiQPPzLRqt5KN86ZYRjnQvydvs7y5eUjJK7no8knDhk?cluster=testnet
https://explorer.solana.com/tx/2XfVWiXeQeHbpqAEYm3AH2RU6hunnqtr155EC4EAM5Bq9VVZNP6QocAav9cPjEQdJFcQrbsSSxiKadr4HPMov8pz?cluster=testnet
https://explorer.solana.com/tx/5Em41sg7yFXeNpnEJnhUQJanfLWKwjMqiBeNAqEEzFrSN9P8zKKafcv5F7RKT2pseB171qeoa8Uz4fKgazzayCnW?cluster=testnet
https://explorer.solana.com/tx/2PMtzpSgjnKDLGmRWBdUSFBPimWnudCPekUYbWzPzokENFYa4N4ab4HCtynfGrzswFPTgGYKHU8PccUMHv3mXHkR?cluster=testnet
https://explorer.solana.com/tx/3kviP9MqkWGMV4yA7k7yPQ5BGfXmcYLcctmY1u2D7n56eT1nx8jMtDumkUNJy8yA3KkmzrmfQLjqpigc8ehGZzBN?cluster=testnet
https://explorer.solana.com/tx/38iEaJBzuGMLbfcszdVB8pkniezH8JrA3XGq7JdADZTQ4hNQC82GSTUA2bmcypdVy3t7htWnUzkZ4F8EakmNvqz8?cluster=testnet

3.2 Attack Transaction

To show the importance of the owner check and the signer check, we use two attack scenarios as examples.

First Scenario

The first one is that the ‘door’ owner try to open the door when the config is locked. To achieve this, we create a fake config account in another program, and assign the attribute is_lock with value false. The code of the custom program is shown below.

We send the transaction to create the fake config account, the public key of the fake config account is: 2MtSrbWp24VjPZQcSUkiWrvNro7qqKemVCsh3Yxc8LTy.

https://explorer.solana.com/tx/2qSyrL5gdQXmgGCFzmzMm1StFQRkDgWpss9A9jV11q2fgDGM5C1XRuXvbX1N5Dt3q2pRqnmyXHVtXGF5dqadAzpJ?cluster=testnet

Once the fake config account is created, we feed it into the program (line 423).

The result is shown below, the log prints that incorrect program id for instruction, which means the config account's owner must be the program. Thus, the attacker cannot bypass this check.

Second Scenario

The second scenario is that a malicious user tries to open the door when the door is unlocked.

In this case, we feed the real owner account to the program(line 419) and send the transaction. The result is shown below.

It prints that Signature verification failed, which means the real owner must sign the transaction to open the door, so our second attack fails as well.

4. Summary

In Solana, instructions implement specified logic based on different accounts, which are feed by clients or the other programs. Therefore, the proper check on the accounts is rather important.

In this article, we introduce how to properly check the account and use two attack scenarios to illustrate the importance of these checks. Keep following and more articles will be shared.


About BlockSec

BlockSec is a pioneering blockchain security company established in 2021 by a group of globally distinguished security experts. The company is committed to enhancing security and usability for the emerging Web3 world in order to facilitate its mass adoption. To this end, BlockSec provides smart contract and EVM chain security auditing services, the Phalcon platform for security development and blocking threats proactively, the MetaSleuth platform for fund tracking and investigation, and MetaSuites extension for web3 builders surfing efficiently in the crypto world.

To date, the company has served over 300 esteemed clients such as MetaMask, Uniswap Foundation, Compound, Forta, and PancakeSwap, and received tens of millions of US dollars in two rounds of financing from preeminent investors, including Matrix Partners, Vitalbridge Capital, and Fenbushi Capital.

Official website: https://blocksec.com/

Official Twitter account: https://twitter.com/BlockSecTeam

Sign up for the latest updates
The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis
Security Insights

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis

This BlockSec deep-dive analyzes the KelpDAO $290M rsETH cross-chain bridge exploit (April 18, 2026), attributed to the Lazarus Group, tracing a causal chain across three layers: how a single-point DVN dependency enabled the attack, how DeFi composability cascaded the damage through Aave V3 lending markets to freeze WETH liquidity exceeding $6.7B across Ethereum, Arbitrum, Base, Mantle, and Linea, and how the crisis forced decentralized governance to exercise centralized emergency powers. The article examines three parameters that shaped the cascade's severity (LTV, pool depth, and cross-chain deployment count) and provides an exclusive technical breakdown of Arbitrum Security Council's forced state transition, an atomic contract upgrade that moved 30,766 ETH without the holder's signature.

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026

This BlockSec weekly security report covers four attack incidents detected between April 13 and April 19, 2026, across multiple chains such as Ethereum, Unichain, Arbitrum, and NEAR, with total estimated losses of approximately $310M. The highlighted incident is the $290M KelpDAO rsETH bridge exploit, where an attacker poisoned the RPC infrastructure of the sole LayerZero DVN to fabricate a cross-chain message, triggering a cascading WETH freeze across five chains and an Arbitrum Security Council forced state transition that raises questions about the actual trust boundaries of decentralized systems. Other incidents include a $242K MMR proof forgery on Hyperbridge, a $1.5M signed integer abuse on Dango, and an $18.4M circular swap path exploit on Rhea Finance's Burrowland protocol.

Weekly Web3 Security Incident Roundup | Apr 6 – Apr 12, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Apr 6 – Apr 12, 2026

This BlockSec weekly security report covers four DeFi attack incidents detected between April 6 and April 12, 2026, across Linea, BNB Chain, Arbitrum, Optimism, Avalanche, and Base, with total estimated losses of approximately $928.6K. Notable incidents include a $517K approval-related exploit where a user mistakenly approved a permissionless SquidMulticall contract enabling arbitrary external calls, a $193K business logic flaw in the HB token's reward-settlement logic that allowed direct AMM reserve manipulation, a $165.6K exploit in Denaria's perpetual DEX caused by a rounding asymmetry compounded with an unsafe cast, and a $53K access control issue in XBITVault caused by an initialization-dependent check that failed open. The report provides detailed vulnerability analysis and attack transaction breakdowns for each incident.