Volver al blog

Asegurando el Ecosistema de Solana (4) — Validación de Cuentas

1 de abril de 2022
6 min de lectura

0. Revisión

1. Descripción General

En el artículo anterior, discutimos cómo actualizar un programa. En esta publicación, presentaremos los problemas relacionados con el control de acceso, que es uno de los temas de seguridad más comunes y básicos en el área de DeFi.

2. Instrucciones

En Solana, cada programa exporta un único entrypoint, que se define con entrypoint!. A diferencia de Ethereum, los clientes solo pueden invocar una única función definida como el entrypoint, que generalmente se llama process_instruction. La función entrypoint recibe tres parámetros: el ID del programa del contrato inteligente, las cuentas sobre las que operará el programa y los datos de instrucción. Los datos de instrucción especifican qué instrucción será invocada. La figura a continuación muestra un ejemplo. Al desempaquetar los datos de instrucción, se eligen diferentes instrucciones (p. ej., Lock, Unlock). Por lo tanto, las instrucciones accesibles desde el entrypoint son públicas para todos y pueden ejecutarse con datos de instrucción específicos.

3. Validación de Cuentas

Como se mencionó, el programa recibe las cuentas que necesita leer o escribir. Este diseño plantea dos preguntas. Para las cuentas a leer, cómo garantizar que los datos almacenados en las cuentas son de confianza. Para las cuentas a escribir, cómo garantizar que solo los usuarios con privilegios puedan invocar las instrucciones para escribir en las cuentas. A continuación, ilustramos el problema de control de acceso. Todos los códigos de prueba se pueden encontrar aquí.

3.1 Revisión de Código (PrivilegeOwner)

Primero definimos dos estructuras: Door y Config. Solo la cuenta clave (línea 17) especificada en la estructura door puede abrir la door creada. Sin embargo, la puerta no puede abrirse cuando el estado del sistema está bloqueado, lo cual se especifica en la estructura Config (línea 81).

Como se mencionó, la cuenta Config especifica si la puerta puede abrirse. En este caso, solo debería haber una cuenta Config en el programa. Para lograrlo, usamos PDA para almacenar los datos de Config. Después de inicializar la cuenta Config, establecemos el atributo is_initialized en true para que no pueda ser inicializada nuevamente por atacantes (línea 108 - línea 110).

La instrucción Open() se usa para abrir las puertas. La instrucción recibe varias cuentas, incluida la cuenta de puerta a abrir, la cuenta config y la cuenta owner que intenta abrir la door. Para garantizar que la puerta pertenece al programa y que la configuración es válida, verificamos el propietario de la cuenta door y de la cuenta config (línea 204 - línea 205). Esto evita que usuarios maliciosos introduzcan cuentas falsas. Esto responde nuestra primera pregunta. Para garantizar que la cuenta a leer sea de confianza, ¡necesitamos verificar el propietario de la cuenta!. Nótese que solo el propietario de la cuenta door puede abrir la puerta. En este caso, verificamos si la cuenta owner es el owner real de la door y, más importante aún, si la instrucción está autorizada por el propietario (línea 217 - línea 219).

En la función validate_owner(), primero verificamos si la clave pública de estas dos cuentas es la misma, y luego verificamos la firma del propietario. Esto responde la segunda pregunta: para garantizar que solo los usuarios con privilegios puedan invocar la instrucción open, necesitamos verificar el propietario y el firmante de la cuenta. La instrucción close es similar a open y los detalles se pueden encontrar en el código.

Desplegamos el programa en la red de pruebas y se puede encontrar en el siguiente enlace.

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

Todas las transacciones de prueba se enumeran a continuación. El proceso completo de esta transacción es 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 Transacción de Ataque

Para mostrar la importancia de la verificación del propietario y la verificación del firmante, utilizamos dos escenarios de ataque como ejemplos.

Primer Escenario

El primero es que el propietario de la 'door' intenta abrir la puerta cuando la config está bloqueada. Para lograrlo, creamos una cuenta config falsa en otro programa y asignamos el atributo is_lock con el valor false. El código del programa personalizado se muestra a continuación.

Enviamos la transacción para crear la cuenta config falsa; la clave pública de la cuenta config falsa es: 2MtSrbWp24VjPZQcSUkiWrvNro7qqKemVCsh3Yxc8LTy.

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

Una vez creada la cuenta config falsa, la introducimos en el programa (línea 423).

El resultado se muestra a continuación; el registro imprime incorrect program id for instruction, lo que significa que el propietario de la cuenta config debe ser el programa. Por lo tanto, el atacante no puede eludir esta verificación.

Segundo Escenario

El segundo escenario es que un usuario malicioso intenta abrir la puerta cuando esta está desbloqueada.

En este caso, introducimos la cuenta del propietario real en el programa (línea 419) y enviamos la transacción. El resultado se muestra a continuación.

Se imprime Signature verification failed, lo que significa que el propietario real debe firmar la transacción para abrir la puerta, por lo que nuestro segundo ataque también falla.

4. Resumen

En Solana, las instrucciones implementan lógica específica basada en diferentes cuentas, que son proporcionadas por clientes u otros programas. Por lo tanto, la verificación adecuada de las cuentas es sumamente importante.

En este artículo, presentamos cómo verificar correctamente las cuentas y usamos dos escenarios de ataque para ilustrar la importancia de estas verificaciones. Continúa siguiéndonos y se compartirán más artículos.

Lee otros artículos de esta serie:


Acerca de BlockSec

BlockSec es una empresa pionera en seguridad blockchain fundada en 2021 por un grupo de expertos en seguridad de renombre mundial. La empresa está comprometida con mejorar la seguridad y la usabilidad del emergente mundo Web3 para facilitar su adopción masiva. Con este fin, BlockSec ofrece servicios de auditoría de seguridad de contratos inteligentes y cadenas EVM, la plataforma Phalcon para el desarrollo de seguridad y el bloqueo proactivo de amenazas, la plataforma MetaSleuth para el rastreo e investigación de fondos, y la extensión MetaSuites para que los desarrolladores de web3 naveguen eficientemente en el mundo cripto.

Hasta la fecha, la empresa ha atendido a más de 300 distinguidos clientes como MetaMask, Uniswap Foundation, Compound, Forta y PancakeSwap, y ha recibido decenas de millones de dólares estadounidenses en dos rondas de financiación de inversores prominentes, entre ellos Matrix Partners, Vitalbridge Capital y Fenbushi Capital.

Sitio web oficial: https://blocksec.com/

Cuenta oficial de Twitter: https://twitter.com/BlockSecTeam

Apúntate para recibir las novedades
Cómo la solución de seguridad de pagos de BlockSec ayuda a las instituciones a mantenerse seguras en la era de la IA
Security Services

Cómo la solución de seguridad de pagos de BlockSec ayuda a las instituciones a mantenerse seguras en la era de la IA

Descubra cómo BlockSec ayuda a las instituciones de pago a proteger los fondos y los datos, y a reforzar su resiliencia mediante evaluaciones de seguridad, pruebas y monitoreo continuo.

Pérdidas de ~5,57 millones USD: exploits de X100 Freezer y NEM | Resumen semanal de BlockSec
Security Insights

Pérdidas de ~5,57 millones USD: exploits de X100 Freezer y NEM | Resumen semanal de BlockSec

Este informe semanal cubre dos incidentes de seguridad ocurridos en Ethereum y BNB Chain durante el período 19/10/2026–25/10/2026. Examina una toma de control de la gobernanza de X100 Freezer financiada mediante un préstamo flash y un fallo en la liquidación de impuestos que afectó a un pool de liquidez de un token NEM en BNB Chain. Las pérdidas estimadas dentro del alcance del informe suman aproximadamente 5,57 millones USD: ~4,75 millones USD de la transacción representativa de X100 y ~820 mil USD del incidente del pool de NEM. El total excluye las extracciones posteriores de X100 reportadas por separado.

Pérdidas de aproximadamente 3,87 millones USD: hackeos de Autheo Network y UNCX | Resumen semanal de BlockSec
Security Insights

Pérdidas de aproximadamente 3,87 millones USD: hackeos de Autheo Network y UNCX | Resumen semanal de BlockSec

Este informe semanal cubre 3 incidentes de seguridad correspondientes al período 12/10/2026–18/10/2026, con pérdidas totales estimadas en aproximadamente 3,87 millones USD. Autheo Network sufrió una vulneración de clave privada con pérdidas de aproximadamente 1,76 millones USD; Qubic Wallet perdió aproximadamente 1,30 millones USD debido a la generación de una semilla débil; y los contratos Smart Token Vesting V2 de UNCX Network sufrieron un error contable con pérdidas de aproximadamente 807 mil USD en Ethereum, BNB Chain, Base y Robinhood Chain. El análisis destacado de UNCX explica cómo un cálculo agregado de participaciones defectuoso mezcló tramos de adquisición de derechos creados con distintas proporciones entre tokens y participaciones, lo que permitió retiros excesivos de pools compartidos.