
0. Revisão
- Protegendo o Ecossistema Solana (1) — Olá, Solana
- Protegendo o Ecossistema Solana (2) — Chamadas Entre Programas
- Protegendo o Ecossistema Solana (3) — Atualização de Programa
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:
- Protegendo o Ecossistema Solana (1) — Olá, Solana
- Protegendo o Ecossistema Solana (2) — Chamadas Entre Programas
- Protegendo o Ecossistema Solana (3) — Atualização de Programa
- Protegendo o Ecossistema Solana (5) — Multi-Sig
- Protegendo o Ecossistema Solana (6) — Multi-Sig2
- Protegendo o Ecossistema Solana (7) — Confusão de Tipos
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




