ScaleBit

Jun 14, 2024

Best Solana Security Practices: A Comprehensive Guide for Developers

solana-security-practices-guide

Solana stands as a high-performance blockchain designed to facilitate decentralized applications and crypto-currencies. It employs a unique consensus mechanism known as Proof of History (PoH), combined with the efficiency of Proof of Stake (PoS), to enhance the network’s scalability. Through this hybrid protocol, Solana achieves remarkable speeds and efficiency, processing thousands of transactions per second while maintaining lower transaction costs.

The architecture of Solana not only ensures rapid processing but also maintains a high degree of security and decentralization. Solana’s innovative approach is exemplified by its ability to support smart contracts and decentralized finance (DeFi) applications, making it a competitive player in the blockchain space.

solana-ecosystem-map

When diving into Solana development, it’s imperative to consider the following advice. These recommendations are pivotal for refining code, bolstering security, and harmonizing with the unique features of Solana. By following these best practices, developers can boost code efficiency, strengthen security protocols, and fully leverage the distinctive elements of Solana’s architecture. This adherence to guidelines ensures that developers can take full advantage of Solana’s high throughput and low latency capabilities, paving the way for innovative and scalable decentralized applications.

0X00: Solana Specific Issues

CHECK THE ACCOUNT

In Solana, since all accounts are provided as inputs when invoking a Solana program, users can supply arbitrary accounts and there’s no built-in stopping a malicious user from doing so with fake data. Therefore, Solana programs must check the validity of the input accounts.

1. Account ownership validation

If I could only tell you one piece of advice about Solana auditing, it would be:

Always CHECK THE OWNERSHIP!

The designated public key is the exclusive identifier permitted to modify the data of the account in question. Ownership of the account by any individual or entity other than the anticipated owner introduces the risk of the presence of malevolent data, rendering the account unreliable for secure transactions. It is imperative that the integrity of account ownership is verified to maintain trust within the system.

solana-attack

Example

The vulnerable code segment:

solana-attack-example-1

This is a common refund function. It first takes out each required account and deserializes them into instances.

solana-attack-example-2

The config object is particularly important. It records the admin information. Only by passing the check can the business logic continue to be executed.

But here’s the problem: you don’t know who created the config. In fact, in this unpack() function, it doesn’t check who created the account. So we always have to check who the owner of the account is – make sure it comes from a safe creator.

The fixed code:
solana-attack-example-3

2. Account state validation

Omitting validation for account state or data is a critical error that developers might inadvertently commit. Take, for instance, the Solana token-lending program, where a reserve’s data linked to a specific lending market is defined by a given structure. The initialization of the reserve will trigger the creation of these fields.

solana-attack-example-4

Example

On August 19, 2021, at 12:40 GMT, a security breach was identified in the Solend smart contract. An attacker exploited a vulnerability in the authentication check of the UpdateReserveConfig () function. This manipulation made accounts with outstanding loans vulnerable to liquidation and artificially increased the borrow Annual Percentage Yield (APY) to an exorbitant 250% across all markets. Fortunately, the Solend team promptly detected and thwarted the malicious attempt, ensuring no funds were compromised. Although five users experienced liquidation due to this incident, they were fully compensated with approximately \$16,000 from the liquidator’s excess profits.

The vulnerable code segment:
solana-attack-example-5

Can you see the problem?

Lending pool may not match config. The checks above were insufficient, as the attacker was able to pass in any lending market they created and owned but change other lending pool configration.In addition to paying attention to the ownership of the accounts, we also need to pay attention to the status and consistency of the accounts.

The fixed code:
solana-attack-example-6

3. Account types check

The problem is always like this: the user can pass in any account. When the data in the account is deserialized into a type, no one knows what type the bytes is. It all depends on how you deserialize. Sometimes this kind of problem just causes the smart contract to not work, and sometimes some genius hackers will construct some serious damage attacks.

solana-attack-example-7

Example

The vulnerable code segment:
solana-attack-example-8

We provide a common attack idea, let’s say the same bytes can be deserialized to TypeA or TpyeB:

  1. TypeA (config) is easy to create in the program.

  2. TypeB (User) is difficult to construct because it may involve the management of funds and permissions.

  3. We will construct typeA to convert it into typeB. In this way, we can use this created account to pass the program check and construct some permissions and fund theft.

solana-attack-example-9

When we create a new account, we set the TYPE field to a unique value for that type of account. Our deserialization function must also verify the type, and it will throw an error if the account does not have the type we expect

0x01: General Issues

1. Integer overflow or underflow

When programming, it is very important to consider computational overflow. Most programming languages may encounter overflow issues when dealing with large numbers or high-precision operations. Simply using basic arithmetic operators such as addition (+), subtraction (-), multiplication (*), and division (/) can lead to unexpected results, especially when the values operated on exceed the range that the language’s data types can represent.

Example

The vulnerable code segment:
solana-attack-example-10

To avoid the risk mentioned above, it is recommended to use safer mathematical libraries for computation. These libraries typically provide mechanisms to prevent overflow, are capable of handling large number operations, and ensure the accuracy and reliability of the computational results. For example, they may use special data types or algorithms to extend the range of numerical representation, or provide additional checks during operations to capture potential overflows.

The fixed code:
solana-attack-example-11

2. Verify Program ID

It is essential to meticulously identify and examine the CPI (cross-program call) code logic within the program. This involves ensuring that the code rigorously incorporates a verification check for the target program’s identifier when executing CPI. Such a check is paramount in confirming the authenticity and integrity of the target program. It acts as a safeguard against unauthorized or malicious entities attempting to invoke the program, thereby preserving the security and reliability of the system.

Example

The vulnerable code segment:
solana-attack-example-12

Ensuring that this verification step is in place is a critical aspect of security, as it helps maintain the trustworthiness of cross-program interactions within the software environment.

The fixed code:
solana-attack-example-13

3. Initialization Front Running

When initializing the contract administrator’s permissions, you may face a scenario where the address of the first call to this function is set as the administrator. This method may face a preemptive attack, causing the administrator’s settings to not be executed as expected. The attack in this scenario can be deduced into many situations.
solana-attack-example-14

Example

Let’s say I have a PDA which maintains the global state of my program. This is like a singleton. And the following snippets of code initialize it.

The vulnerable code segment:
solana-attack-example-15

And here is the struct part:
solana-attack-example-16

We should set more comprehensive permission checks for functions like these where the initial call is completely different from the rest of the calls.

The fixed code:
solana-attack-example-17

0x02: Real Case Scenario

1. Check Security Dependency Chain

Once a function needs to interact with multiple contracts, there will be multiple address inputs. We need to confirm whether the constraints of these addresses are correct, and more importantly:

Once the data security of a certain type needs to be guaranteed by another type, then this type must be safe. Otherwise, all these security dependencies will become invalid.

Example

The Brrr program bug allowed attackers to mint \$CASH tokens at minimal cost by exploiting unchecked Bank accounts and using fake accounts to bypass collateral requirements. This compromised the integrity of the \$CASH minting process, which should have been secured by Arrow LP tokens as collateral.

The vulnerable code segment:
solana-attack-example-18

solana-attack-example-19

It can be seen that

  1. The crate_mint check depends on crate_token.mint,

  2. The crate_token check depends on crate_collateral_tokens.

  3. However, crate_collateral does not make more checks in the structure. It can be forged in the security source, so all addresses that rely on it as a security check can be forged.

solana-attack-example-20

Once type3 is unsafe then all relative addresses are under risk.

Repair suggestions: sort out security dependencies and ensure that your security sources are checked.

2. Uncheck Status of Account

Once the input address is not type-checked, it may be executed successfully in unrelated functions, which may cause serious consequences.

Example

The vulnerable code segment:
solana-attack-example-21

For an address like candy_machine, it is not type checked, but only checked for contract source. This may cause all addresses created by this contract to pass this part of the check. Once the same field is modified in TypeA and TypeB, this may lead to asset loss.Or If the function does not check the user’s initialization status,it can exploit this problem and perform what is known as a re-initialization attack.

Repair suggestion: If you want to use an address as a parameter, please ensure its type or status to avoid function misuse or attack.

3. Protocol Vulnerabilities

Now we need to jump out of smart contracts and look at security issues at a higher level. Many problems come from specific business logic design rather than implementation. There is no universal review procedure for protocol vulnerabilities, so we can only focus on the protocol itself.

What I can tell you is that vulnerabilities most often occur near judgment statements. Judgment and jump are generally used in protocols to handle some different situations. At this time, we need to think in a certain direction and consider constructing some special situations to bypass or enter these functions, which may cause exceptions. Based on these exceptions, we can consider constructing attack methods.

Example

The vulnerable code segment:

This function is used to calculate the asset value and borrowing of a market position. When the account is default, it exits.
solana-attack-example-22

The introduction of the new feature that allows the closing of accounts, specifically through the close_loan_account and close_collateral_account instructions, has led to a critical issue. When these instructions are executed, they reset the pos.account to Pubkey::default(). This change causes the system to overlook any subsequent loans and collateral linked to the first Pubkey::default() it encounters, leading to miscalculations in loan and collateral values.

Ensure a thorough review of the protocol.

conclusion

In conclusion, adhering to the best practices in Solana development is not just beneficial; it’s essential for crafting robust and efficient decentralized applications. By fine-tuning code, fortifying security measures, and aligning with Solana’s architecture, developers can harness the full potential of its rapid processing and minimal delay. This commitment to excellence sets the stage for a new era of decentralized solutions that are not only innovative but also scalable and reliable, ensuring a strong foundation for the future of blockchain technology.

About ScaleBit

ScaleBit, a subsidiary brand of BitsLab, is a blockchain security team that provides security solutions for Mass Adoption of Web3. With expertise in scaling technologies like Bitcoin Layer 2, Zero-Knowledge proofs, and blockchain interoperability, it provides meticulous and cutting-edge security audits for blockchain applications. The team comprises security professionals with extensive experience in both academia and enterprise.

Requests a quote

OLDER > < NEWER