Propiedad y Permisos de Cuentas
La regla del propietario escribe es el invariante de seguridad central de Solana: solo el programa propietario de una cuenta puede modificar sus data (y ciertos metadatos). Los firmantes autorizan las transferencias de lamports; los propietarios autorizan las mutaciones de datos.
Receta
#[derive(Accounts)]
pub struct SecureUpdate<'info> {
#[account(
mut,
has_one = authority @ MyError::Unauthorized,
constraint = data.is_active @ MyError::Inactive,
)]
pub data: Account<'info, MyData>,
pub authority: Signer<'info>,
}Cuándo usar esto:
- Cada instrucción que muta cuentas propiedad del programa
- Auditoría de programas para verificar la falta de comprobaciones de firmante o propietario
- Diseño de flujos CPI donde el llamado debe confiar en las cuentas pasadas
- Comprender por qué
UncheckedAccountes peligroso por defecto
Ejemplo de Trabajo
use anchor_lang::prelude::*;
#[account]
pub struct MyData {
pub authority: Pubkey,
pub is_active: bool,
pub balance: u64,
}
#[program]
pub mod permissions {
use super::*;
pub fn update_balance(ctx: Context<UpdateBalance>, new_balance: u64) -> Result<()> {
ctx.accounts.data.balance = new_balance;
Ok(())
}
pub fn steal_attempt(ctx: Context<StealAttempt>) -> Result<()> {
// Esto FALLA en tiempo de ejecución - el llamador no es el propietario de la cuenta víctima
Ok(())
}
}
#[derive(Accounts)]
pub struct UpdateBalance<'info> {
#[account(mut, has_one = authority)]
pub data: Account<'info, MyData>,
pub authority: Signer<'info>,
}
#[derive(Accounts)]
pub struct StealAttempt<'info> {
#[account(mut)]
pub data: Account<'info, MyData>, // Anchor verifica propietario == programa
pub attacker: Signer<'info>,
}Lo que esto demuestra:
Account<'info, MyData>verifica que el propietario == este programahas_one = authorityvincula los derechos de mutación a una pubkey almacenada- El atacante no puede pasar la cuenta de otro usuario a menos que sea la autoridad
Análisis Profundo
Capas de Permisos
| Comprobación | Aplicada Por | Propósito |
|---|---|---|
| Programa propietario | Tiempo de ejecución | Solo el propietario escribe datos |
| Firmante | Tiempo de ejecución | Firma Ed25519 presente |
has_one / constraint | Anchor | Autenticación a nivel de aplicación |
| Banderas de metadatos de cuenta | Tiempo de ejecución | Escribible vs. solo lectura |
Permisos CPI
- El llamador pasa cuentas al llamado; el llamado revalida
- El programa propietario puede usar CPI para delegar transferencias de tokens (SPL)
invoke_signedpermite a los programas firmar para PDAs
Trampas Comunes
- UncheckedAccount sin restricciones - sustitución arbitraria de cuentas. Solución: añadir comprobaciones de propietario, semillas o
has_one. - Signer != authority - el usuario firma pero no es la autoridad almacenada. Solución:
has_one = authorityenSigner. - Banderas escribibles en cuentas no autorizadas - el tiempo de ejecución puede rechazar o el programa propietario debe permitir. Solución: marcar solo las cuentas escribibles que su programa posee y tiene la intención de mutar.
- Falta de
muten cuentas que cambian - operación silenciosa sin efecto o error de compilación. Solución:#[account(mut)]en cada cuenta modificada. - Confundir el propietario del token con la autoridad de la cuenta - SPL tiene propietario y delegado separados. Solución: usar restricciones SPL o CPI correctamente.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
has_one | Pubkey de autoridad única en los datos | Multisig (usar restricción personalizada) |
seeds + bump | Autoridad PDA | La billetera del usuario es la autoridad |
Expresiones constraint | Reglas booleanas complejas | Coincidencia simple de pubkey (usar has_one) |
require! manual | Comprobaciones dinámicas | Comprobaciones de diseño estático (preferir restricciones) |
Preguntas Frecuentes
¿Qué es la regla del propietario escribe?
Solo el programa listado en account.owner puede modificar el búfer de datos de la cuenta.
¿Puede un firmante mutar cualquier cuenta?
No. Los firmantes autorizan transacciones; las escrituras de datos aún requieren lógica del programa propietario en la instrucción.
¿Qué comprueba has_one?
Un campo pubkey dentro de los datos de la cuenta coincide con la pubkey de otra cuenta en la estructura.
¿Pueden los programas cambiar el propietario de la cuenta?
El programa propietario puede llamar a assign del Programa del Sistema para transferir la propiedad; es raro y peligroso si se usa mal.
¿Por qué usar Signer en lugar de UncheckedAccount para la autoridad?
Signer demuestra que firmó la transacción. UncheckedAccount no lo hace; se debe añadir una comprobación manual de firmante.
¿Qué es una vulnerabilidad común de drenaje?
La falta de comprobación de propietario/autoridad permite al atacante pasar la cuenta de la víctima con la bandera escribible.
¿Las cuentas de solo lectura necesitan firmante?
No, a menos que su lógica requiera prueba de identidad para rutas de lectura (poco común).
¿Cómo encajan los PDAs en los permisos?
El programa firma a través de invoke_signed con semillas; no se necesita firmante humano para la lógica propiedad de PDA.
¿Pueden dos firmantes co-autorizar?
Sí. Pase múltiples cuentas Signer y valide ambas en las restricciones.
¿Cuál es el impacto de la tabla de búsqueda de direcciones?
Las ALT cambian cómo se referencian las direcciones, no las reglas de propiedad.
¿Anchor verifica el propietario de la cuenta de token?
Las restricciones InterfaceAccount / token verifican la propiedad del programa SPL Token.
¿Cómo audito los permisos?
Enumere cada cuenta mut; rastree las comprobaciones de firmante y propietario requeridas por instrucción.
Relacionado
- Cuentas del Sistema vs. Cuentas de Programa - quién posee qué
- Estado Propiedad del Programa - diseño de cuentas de estado
- Conceptos Básicos del Modelo de Cuentas - ejemplos de restricciones
Versiones de Stack: Esta página fue escrita 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.