owasp-masvs · pentest · resilience
Como passar no item de resiliência de um pentest mobile: os quatro controles do MASVS-RESILIENCE
Relatórios de pentest de apps móveis costumam terminar com um achado de resiliência. Veja o que os quatro controles OWASP MASVS-RESILIENCE realmente pedem, que evidência o testador procura e como fechar as lacunas sem falsa confiança.
Por Equipe Moatgator9 min de leituraRead this article in English
Se a sua empresa vende um app móvel que lida com dinheiro, dados de saúde ou credenciais, mais cedo ou mais tarde um relatório de pentest vai chegar à sua mesa com uma linha mais ou menos assim: "o aplicativo não tem resiliência contra engenharia reversa e adulteração". Muita gente de desenvolvimento lê isso como algo vago. Não é. A frase corresponde a um grupo específico de controles do OWASP Mobile Application Security Verification Standard, e cada controle tem uma pergunta concreta que o testador responde olhando para o seu build.
Este artigo percorre esses quatro controles, explica o que o testador verifica em cada um e descreve formas honestas de fechar as lacunas. Ele é escrito para a pessoa que precisa responder ao relatório, e não para quem o escreveu. Tudo aqui é defensivo: descrevemos o que construir e como verificar, e nada que sirva como caminho para contornar uma proteção.
O que é o MASVS-RESILIENCE, e o que ele não é
O MASVS organiza os requisitos de segurança mobile em grupos como armazenamento, criptografia, autenticação, rede e plataforma. Um deles, o MASVS-RESILIENCE, é diferente dos demais. Os outros grupos perguntam se o seu app foi construído corretamente. Este pergunta o quanto é difícil para um atacante que já tem o seu app, em um aparelho que ele controla, entendê-lo e alterá-lo.
Esse enquadramento importa. Controles de resiliência não corrigem uma vulnerabilidade. A falta de uma checagem de autorização no seu servidor não vira segurança por causa de ofuscação. O que a resiliência faz é aumentar o custo de encontrar e explorar problemas e tornar visíveis, para você, algumas classes de fraude (um build modificado, uma sessão instrumentada, um aparelho comprometido) que de outro modo passariam em silêncio.
O próprio padrão diz isso: esses controles são pensados para apps cujo modelo de ameaça inclui um atacante local determinado, como bancos, pagamentos, jogos com economia de dinheiro real e apps que protegem propriedade intelectual. Se o seu app é um catálogo simples, somente leitura, um pentest que exige os quatro pode estar pedindo mais do que o seu risco justifica. Decida a partir do seu modelo de ameaça, e não a partir da lista.
Os quatro controles
São quatro controles, e uma boa forma de lembrar de cada um é pela pergunta que ele responde.
- RESILIENCE-1: o app pode confiar na plataforma em que roda? O app valida a integridade da plataforma: tenta perceber um aparelho com root ou jailbreak, uma imagem de sistema depurável ou um emulador, e pode pedir ao fabricante do sistema operacional uma declaração assinada sobre o aparelho.
- RESILIENCE-2: o app percebe que foi modificado? O app implementa mecanismos antiadulteração: confere se o próprio código e a identidade de assinatura são os que você publicou e reage quando não são.
- RESILIENCE-3: o código é difícil de ler? O app implementa mecanismos contra análise estática: identificadores são renomeados, strings sensíveis não ficam em texto claro e as informações de depuração são removidas.
- RESILIENCE-4: o app percebe um analista ao vivo? O app implementa técnicas contra análise dinâmica: responde a um debugger anexado, a frameworks de hooking e a um ambiente emulado.
Quem trabalha com o guia de testes vai exercitar cada um deles contra o seu build de release. Por isso o primeiro conselho prático é testar o build que de fato vai para a loja. Um build de debug, com símbolos e debuggable ligado, falha em todos os quatro, e o relatório vai dizer isso.
RESILIENCE-1: integridade da plataforma
O que o testador procura é se o app faz alguma coisa quando o aparelho não é confiável e se essa resposta é robusta ou facilmente ignorada. Existem duas camadas.
A primeira camada é a de heurísticas no aparelho: procurar sinais de root ou jailbreak, uma partição de sistema gravável, marcas de depuração na imagem do sistema ou um emulador. Essas verificações são baratas e úteis, e também são as mais fáceis de neutralizar em um aparelho que o atacante controla. Trate-as como sinal, nunca como portão.
A segunda camada é uma declaração assinada pelo fabricante da plataforma. No Android é a Play Integrity API, e no iOS é o App Attest. O valor delas está em o veredito ser produzido e assinado pelo fabricante e verificado no seu servidor, de modo que um app modificado não consegue simplesmente responder "tudo certo". O custo é que você precisa de um backend que confira o veredito e de uma política para o que fazer com cada resposta.
Para fechar a lacuna sem falsa confiança, combine as duas camadas e deixe o servidor tomar a decisão. Envie um token vinculado à requisição com as operações sensíveis, verifique no servidor e decida o que cada resultado significa para aquela operação. Mover dinheiro pode exigir um veredito forte; ler uma página pública pode não exigir nada.
RESILIENCE-2: antiadulteração
O testador reempacota o seu app, muda algo pequeno, assina com a chave dele e instala. A pergunta é se a cópia modificada continua funcionando e se alguém percebe. Para a maioria dos apps, a resposta honesta é que funciona perfeitamente e ninguém percebe.
Controles úteis aqui são a verificação da assinatura em tempo de execução, a checagem da origem da instalação e, de novo, a atestação da identidade do app no servidor. O Play Integrity informa se o binário e o certificado do app correspondem ao que o Google Play distribuiu, e o App Attest liga as requisições a uma chave que só existe na instância genuína do seu app, em um aparelho genuíno.
Cuidado com verificações de integridade puramente locais. Uma checagem que compara um hash e depois mostra uma caixa de erro é um único salto condicional que um atacante pode remover. Verificações locais são um quebra-molas, e funcionam melhor quando alimentam um valor que o servidor verifica, de modo que remover a checagem também remove a capacidade de concluir a operação.
RESILIENCE-3: resistência à análise estática
O testador abre o seu pacote e tenta ler. A ofuscação de nomes e o encolhimento de código, que as ferramentas de build do Android oferecem, tornam isso mais lento. O mesmo vale para remover símbolos de depuração e manter segredos fora do pacote. Em apps escritos com React Native ou Flutter, a lógica interessante costuma ficar em um bundle JavaScript ou em Dart compilado, então as opções de ofuscação dessas ferramentas importam tanto quanto as nativas.
Este é o controle em que a honestidade mais importa, porque a ofuscação é o item mais superestimado. Ela atrasa quem lê, mas não impede. A ação mais valiosa neste controle nem é ofuscar: é garantir que nenhum segredo de longa duração esteja no pacote. Uma chave de pagamento, uma credencial de nuvem ou um token de administrador embutido no app pode ser lido por qualquer pessoa, e nenhum esquema de renomeação muda isso. Leve os segredos para um servidor e dê ao app tokens de vida curta e escopo limitado.
RESILIENCE-4: resistência à análise dinâmica
O testador executa o seu app sob um debugger ou um framework de hooking e observa o que acontece. As respostas defensivas típicas incluem detectar um debugger anexado ao processo, perceber artefatos conhecidos de instrumentação e recusar operações sensíveis em um ambiente emulado. Como nas verificações de plataforma, todas são sinais executados em um aparelho que o atacante controla, então parta do princípio de que um analista determinado consegue neutralizar qualquer uma isoladamente.
O valor está em combinar camadas e em reagir com bom senso. Reportar o sinal ao seu backend, onde ele pode ser somado a outras evidências (histórico do aparelho, velocidade incomum, uma atestação que falha), é mais forte do que uma falha local. Uma falha também ensina ao analista exatamente qual verificação disparou.
Como um relatório costuma ser pontuado
Testadores raramente dão um simples passou ou falhou. Eles registram, para cada controle, se havia proteções, se elas foram eficazes diante de uma tentativa razoável e se estão em camadas. Isso significa que você pode mudar o resultado de duas formas: adicionar as proteções que faltam e tornar as existentes mais difíceis de remover, ligando-as a decisões do servidor.
Se você quer saber onde está antes de o testador chegar, uma análise estática do pacote de release dá uma primeira aproximação. O Scan gratuito da Moatgator lê um APK, AAB ou IPA e pontua os quatro controles com 25% cada, com uma letra de A a F. É importante entender o limite dessa abordagem: o Scan só lê bytes e nunca executa o app. Uma proteção escondida dentro de um empacotador comercial, ou carregada em tempo de execução, aparece como "não detectada", e "não detectada" não é "vulnerável". Use o resultado como lista de tarefas, não como veredito.
Uma ordem prática de trabalho
Se você tem um relatório aberto e um prazo, esta ordem tende a dar mais proteção por hora de trabalho.
- Garanta que o build de release não seja depurável, não use certificado de debug e tenha o encolhimento de código ligado.
- Remova do pacote os segredos de longa duração e troque por tokens de vida curta emitidos pelo seu servidor.
- Adicione atestação verificada no servidor (Play Integrity e App Attest) às operações que importam, com uma política escrita para cada veredito.
- Adicione sinais no aparelho para root, jailbreak, debugger e hooking e envie-os ao backend, em vez de derrubar o app.
- Teste de novo e guarde as evidências: o build, a configuração e os logs do servidor que mostram a política funcionando.
O que nenhum controle vai lhe dar
Nenhum desses controles torna um app inquebrável, e um fornecedor que promete isso está vendendo outra coisa. Um app resiliente é aquele em que derrotar as proteções exige uma pessoa habilidosa e tempo de verdade, em que isso deixa rastro no seu servidor e em que o dinheiro ou o dado em jogo não depende de uma única verificação no cliente. Se o seu relatório pede que você "passe" em resiliência, o alvo honesto são o segundo e o terceiro pontos: custo e visibilidade. Escreva isso na sua resposta ao relatório, inclua o seu modelo de ameaça e deixe o testador julgar o build em relação a ele.
Fontes
Cada afirmação acima pode ser conferida aqui. Se um link mudou, avise e corrigimos o artigo.
- OWASP MASVS: Mobile Application Security Verification Standard
- OWASP MASVS-RESILIENCE-1: platform integrity
- OWASP MASVS-RESILIENCE-2: anti-tampering
- OWASP MASVS-RESILIENCE-3: anti-static analysis
- OWASP MASVS-RESILIENCE-4: anti-dynamic analysis
- OWASP MASTG: Mobile Application Security Testing Guide
- Android developers: Play Integrity API
- Apple developer documentation: Establishing your app's integrity (App Attest)