Back to Blog

Protegendo o Ecossistema Solana (4) — Validação de Contas

April 1, 2022
6 min read

0. Revisão

1. Visão Geral

No artigo anterior, discutimos como atualizar um programa. Nesta publicação, apresentaremos os problemas relacionados ao controle de acesso, que é um dos tópicos de segurança mais comuns e fundamentais na área de DeFi.

2. Instruções

No Solana, cada programa exporta um único entrypoint, que é definido com entrypoint!. Diferente do Ethereum, os clientes só podem invocar uma única função definida como o entrypoint, que normalmente é chamada de process_instruction. A função entrypoint recebe três parâmetros: o ID do programa do contrato inteligente, as contas nas quais o programa irá operar e os dados de instrução. Os dados de instrução especificam qual instrução será invocada. A figura abaixo mostra um exemplo. Ao desempacotar os dados de instrução, diferentes instruções (por exemplo, Lock, Unlock) são selecionadas. Assim, as instruções acessíveis a partir do entrypoint são públicas para todos e podem ser executadas com dados de instrução especificados.

3. Validação de Conta

Conforme mencionado, o programa recebe as contas que precisa ler ou gravar. Esse design levanta duas questões. Para as contas a serem lidas, como garantir que os dados armazenados nelas são confiáveis. Para as contas a serem gravadas, como garantir que apenas usuários privilegiados possam invocar as instruções para gravar nessas contas. A seguir, ilustramos o problema de controle de acesso. Todos os códigos de teste podem ser encontrados aqui.

3.1 Revisão de Código (PrivilegeOwner)

Primeiro definimos duas structs: Door e Config. Apenas a conta chave (linha 17) especificada na struct door pode abrir a door criada. No entanto, a porta não pode ser aberta quando o estado do sistema está bloqueado, conforme especificado na estrutura Config (linha 81).

Conforme mencionado, a conta Config especifica se a porta pode ser aberta. Nesse caso, deve haver apenas uma conta Config no programa. Para isso, usamos PDA para armazenar os dados da Config. Após inicializar a conta Config, definimos o atributo is_initialized como true para que ela não possa ser inicializada novamente por atacantes (linha 108 - linha 110).

A instrução Open() é usada para abrir as portas. A instrução recebe várias contas, incluindo a conta da porta a ser aberta, a conta config e a conta owner que pretende abrir a door. Para garantir que a porta pertence ao programa e que a configuração é válida, verificamos o proprietário da conta door e da conta config (linha 204 - linha 205). Isso impede que usuários maliciosos forneçam contas falsas. Isso responde à nossa primeira pergunta: para garantir que a conta a ser lida seja confiável, precisamos verificar o proprietário da conta! Observe que apenas o proprietário da conta door pode abrir a porta. Nesse caso, verificamos se a conta owner é o verdadeiro owner da door e, mais importante, se a instrução está autorizada pelo proprietário (linha 217 - linha 219).

Na função validate_owner(), primeiro verificamos se as chaves públicas das duas contas são iguais e, em seguida, verificamos a assinatura do proprietário. Isso responde à segunda pergunta: para garantir que apenas usuários privilegiados possam invocar a instrução open, precisamos verificar o proprietário e o assinante da conta. A instrução close é semelhante à open e os detalhes podem ser encontrados no código.

Implantamos o programa na testnet e ele pode ser encontrado no seguinte link.

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

Todas as transações de teste estão listadas abaixo. O processo completo desta transação é 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 Transação de Ataque

Para demonstrar a importância da verificação de proprietário e da verificação de assinante, usamos dois cenários de ataque como exemplos.

Primeiro Cenário

O primeiro é que o proprietário da 'door' tenta abrir a porta quando a config está bloqueada. Para isso, criamos uma conta config falsa em outro programa e atribuímos ao atributo is_lock o valor false. O código do programa personalizado é mostrado abaixo.

Enviamos a transação para criar a conta config falsa; a chave pública da conta config falsa é: 2MtSrbWp24VjPZQcSUkiWrvNro7qqKemVCsh3Yxc8LTy.

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

Uma vez criada a conta config falsa, a inserimos no programa (linha 423).

O resultado é mostrado abaixo; o log imprime incorrect program id for instruction, o que significa que o proprietário da conta config deve ser o programa. Portanto, o atacante não consegue contornar essa verificação.

Segundo Cenário

O segundo cenário é que um usuário malicioso tenta abrir a porta quando ela está desbloqueada.

Nesse caso, inserimos a conta do proprietário real no programa (linha 419) e enviamos a transação. O resultado é mostrado abaixo.

É impresso Signature verification failed, o que significa que o proprietário real deve assinar a transação para abrir a porta, portanto nosso segundo ataque também falha.

4. Resumo

No Solana, as instruções implementam lógicas específicas com base em diferentes contas, que são fornecidas por clientes ou por outros programas. Portanto, a verificação adequada das contas é extremamente importante.

Neste artigo, apresentamos como verificar corretamente as contas e usamos dois cenários de ataque para ilustrar a importância dessas verificações. Continue acompanhando e mais artigos serão compartilhados.

Leia outros artigos desta série:


Sobre a BlockSec

A BlockSec é uma empresa pioneira em segurança blockchain, fundada em 2021 por um grupo de especialistas em segurança de renome mundial. A empresa está comprometida em aprimorar a segurança e a usabilidade para o emergente mundo Web3, a fim de facilitar sua adoção em massa. Para isso, a BlockSec oferece serviços de auditoria de segurança de contratos inteligentes e chains EVM, a plataforma Phalcon para desenvolvimento seguro e bloqueio proativo de ameaças, a plataforma MetaSleuth para rastreamento e investigação de fundos, e a extensão MetaSuites para construtores web3 que navegam com eficiência no mundo cripto.

Até o momento, a empresa atendeu mais de 300 clientes de prestígio, como MetaMask, Uniswap Foundation, Compound, Forta e PancakeSwap, e recebeu dezenas de milhões de dólares americanos em duas rodadas de financiamento de investidores proeminentes, incluindo Matrix Partners, Vitalbridge Capital e Fenbushi Capital.

Site oficial: https://blocksec.com/

Conta oficial no Twitter: https://twitter.com/BlockSecTeam