Security of Solana Smart Contracts: two caveats of the SPL Associated Token Account
September 23, 2022

The SPL Associated Token Program is used frequently in Solana smart contracts. We reviewed its technical details in a prior article. In this article, we focus on two important caveats of using associating token accounts as learned by the Sec3 core team.

  1. Be careful of setting any associated token account as a token account’s owner
  2. Be careful of validating any input associated token account by its owner and mint

1. do not set an associated token account (ATA) as the owner

If you set an associated token account as the owner of a token account by mistake, you may lose all your tokens and those tokens may not be recovered

When creating a new account with the System program, you can pass in any Pubkey as the owner of the new account:

see source code

However, if you pass in an associate token account (ATA) as the owner by mistake, then you would lose your control of the account, i.e., not able to modify the account data.

The reason is that an ATA is a PDA (i.e., program-derived account), and does not have a corresponding private key to sign.

This problem has happened to some users (e.g., https://github.com/solana-labs/solana-program-library/pull/2889), and Solana has implemented an instruction called RecoverNested to help recover nested ATAs (see detail here).

If an ATA X is owned by another ATA Y, then X is called a nested ATA, and its tokens can be recovered by the RecoverNested instruction. The instruction will transfer all tokens in X to Y and close X.

Importantly, the RecoverNested instruction does not recover non-nested ATAs (as of September 2022). That is, if a token account is owned by an ATA, but the token account itself is not an ATA, then it cannot be recovered.

2. do not validate an associated token account only by owner and mint

If you validate an associate token account by only owner/mint rather than using get_associated_token_address, an unexpected token account may be allowed to pass in, which may mess with your protocol

It is not sufficient to validate an associated token account by only owner and mint. For example, in Anchor account validation, using the following constraints (instead of get_associated_token_address):

#[account(mut,
    constraint = vault_ata.owner == vault.key(),
    constraint = vault_ata.mint == vault.mint
)]    
pub vault_ata: Box>,

The above only validates vault_ata’s owner is vault and it has the same mint as vault.mint, but vault_ata may not be an ATA at all.

An attacker may pass in an arbitrary token account (set its owner and mint to vault.key() and vault.mint respectively), and pass the validation. Depending on the protocol logic, the fake ATA passed in may cause security issues.

To validate ATA, use get_associated_token_address:

#[account(mut,
    address = get_associated_token_address(&vault.key(), &vault.mint)
)]
pub vault_ata: Box>,

About Sec3 (formerly Soteria)

Security is front and center for Web3. sec3 is a security research firm that prepares Solana projects for millions of users. sec3’s Launch Audit is a rigorous, researcher-led code examination that investigates and certifies mainnet-grade smart contracts; sec3’s continuous auditing software platform, X-ray, integrates with Github to progressively scan pull requests, helping projects fortify code before deployment; and sec3’s post-deployment security solution, WatchTower, ensures funds stay safe. sec3 is building technology-based scalable solutions for Web3 projects to ensure protocols stay safe as they scale. To learn more about sec3, please visit https://www.sec3.dev