Conceptos básicos de seguridad
8 ejemplos para empezar con la seguridad de programas: 5 básicos y 3 intermedios. Cubre el modelo de amenazas de Solana, la validación de cuentas, las comprobaciones de firmantes y el diseño de cierre por fallo en Anchor 0.32.1.
Prerrequisitos
- Proyecto Anchor 0.32.1 con
anchor-lang 0.32.1. - Leer Conceptos básicos de CPI - muchos ataques cruzan los límites del programa.
anchor --version # 0.32.1Ejemplos básicos
1. Requerir un firmante
Cualquier cuenta que mueva fondos o cambie la autoridad debe firmar.
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut)]
pub vault: Account<'info, Vault>,
pub authority: Signer<'info>,
}Signer<'info>falla si la cuenta no firmó la transacción.- La falta de comprobaciones de firmantes es el hallazgo crítico más común.
- Los PDA no pueden ser
Signera menos que usesinvoke_signed.
Relacionado: Comprobaciones de firmante y propietario - patrones más profundos
2. Validar el propietario de la cuenta
Solo el programa propietario debe interpretar los datos de la cuenta.
#[account(
mut,
constraint = vault.owner == program_id @ ErrorCode::InvalidOwner,
)]
pub vault: Account<'info, Vault>,- Anchor
Account<'info, T>comprueba automáticamenteowner == program_idpara tus tipos. AccountInfoyUncheckedAccountomiten esto; valida manualmente.- Los ataques de confusión de tipos pasan tipos de cuenta incorrectos sin comprobaciones de propietario.
Relacionado: Ataques de validación de cuentas
3. Restringir semillas de PDA
Los PDA deben derivarse con semillas canónicas y un "bump".
#[account(
seeds = [b"vault", authority.key().as_ref()],
bump = vault.bump,
)]
pub vault: Account<'info, Vault>,- Los usuarios no pueden falsificar PDA sin las reglas de semillas del programa.
- Almacena el "bump" en la inicialización para evitar el coste de búsqueda en tiempo de ejecución y la ambigüedad de las semillas.
- Las semillas incorrectas permiten a los atacantes sustituir su propio diseño de PDA.
Relacionado: Ataques de PDA y semillas
4. Usar matemáticas comprobadas
Las cantidades de tokens y las participaciones se desbordan silenciosamente en las compilaciones de lanzamiento sin comprobaciones.
let new_balance = old_balance
.checked_add(amount)
.ok_or(ErrorCode::Overflow)?;- Solana BPF utiliza aritmética de desbordamiento por defecto en
+simple. - Usa
checked_*,saturating_*o intermediariosu128. - El redondeo favorece al protocolo, no al usuario, en la división.
Relacionado: Aritmética y desbordamiento
5. Proteger la inicialización
Evita la reinicialización de cuentas ya inicializadas.
#[account(
init,
payer = payer,
space = 8 + Vault::INIT_SPACE,
)]
pub vault: Account<'info, Vault>,- Usa
inituna vez, luegomuten instrucciones posteriores. init_if_neededes peligroso sin una protección de banderais_initialized.- El atacante puede volver a ejecutar la inicialización para restablecer campos de administrador.
Relacionado: Ataques de reinicialización
Ejemplos intermedios
6. Validar el ID del programa CPI
Los llamados deben ser el programa que esperas, no un impostor.
let cpi_program = ctx.accounts.token_program.to_account_info();
require_keys_eq!(cpi_program.key(), anchor_spl::token::ID, ErrorCode::BadProgram);- Nunca confíes en las cuentas de programa proporcionadas por el usuario sin
require_keys_eq!. - SPL Token vs Token-2022 tienen diferentes IDs de programa.
- Un programa malicioso puede no hacer nada y devolver éxito.
Relacionado: Riesgos de CPI y de recurrencia
7. Cerrar cuenta de forma segura
Drena los lamports al destino y pon a cero los datos para evitar su reactivación.
#[account(
mut,
close = destination,
)]
pub temp: Account<'info, Temp>,- El atributo
closede Anchor asigna lamports y pone a cero el discriminador. - Asegúrate de que no haya referencias abiertas ni cierres duplicados en la misma transacción.
- Cerrar a una cuenta controlada por el atacante filtra el alquiler si falta la comprobación de autoridad.
8. Cerrar por fallo ante desactualización de oráculo
La lógica dependiente del precio debe rechazar fuentes desactualizadas.
let price = feed
.get_price_no_older_than(clock.unix_timestamp, 60)
.ok_or(ErrorCode::StaleOracle)?;- Las lecturas de oráculo abiertas permiten pedir prestado contra precios obsoletos.
- Siempre combínalo con comprobaciones de confianza para decisiones de alto valor.
- Prueba las rutas desactualizadas en LiteSVM 0.6.x.
Relacionado: Oráculos - integración de fuentes
Versiones de la pila: Esta página se escribió para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0 y LiteSVM 0.6.x.