# Análise estática no Android – parte 2: segredos no binário

> Para onde vai o .env quando o app é publicado, onde cada plataforma guarda esse valor e a linha entre configuração pública e segredo de verdade.

- Autor: Marcelo Silva
- Publicado em: 2026-10-10
- Tema: Segurança
- Série: Segurança Mobile, parte 5 de 11 (https://marcelxsilva.dev/series/seguranca-mobile/)
- URL: https://marcelxsilva.dev/artigos/segredos-no-binario/

Todo app precisa de alguma configuração que muda entre ambientes: o endereço da API, a chave pública de um serviço de terceiro, o identificador do projeto no Firebase. Esses valores costumam viver num arquivo `.env` durante o desenvolvimento, e a pergunta deste artigo é simples: para onde eles vão quando o app vira um binário publicado na loja?

A resposta curta é: para dentro do binário, lado a lado com o código. E "dentro do binário" significa que qualquer pessoa que baixe o APK ou o IPA pode lê-los.

## Como o valor sai do `.env` e entra no app

Em projetos React Native, é comum usar uma biblioteca como o [`react-native-config`](https://github.com/lugg/react-native-config) para ler variáveis de um arquivo `.env` em tempo de build e torná-las acessíveis ao código JavaScript e nativo. O problema de segurança não está na biblioteca em si, está no que ela faz por definição: ela copia **todas** as variáveis do `.env` para o binário, sem distinguir o que é público do que é secreto. Se uma chave estiver no `.env` no momento do build, ela vai para o APK e para o IPA, do mesmo jeito.

Isso significa que a responsabilidade de separar "configuração pública" de "segredo" não é da ferramenta, é de quem decide o que entra no `.env` de produção.

## Onde esse valor aparece no Android

No Android, cada variável de ambiente lida em tempo de build normalmente vira uma constante na classe `BuildConfig`, gerada automaticamente pelo Gradle para cada módulo do app. Essa classe, depois de decompilada (veja o [artigo anterior](https://marcelxsilva.dev/artigos/abrindo-um-apk/)), fica dentro de `jadx-out/sources`, no caminho que espelha o nome do pacote do app:

```bash
find jadx-out -name BuildConfig.java
grep 'public static final' jadx-out/sources/com/exemplo/app/BuildConfig.java
```

A saída é uma lista simples de campos `public static final`, com nome e valor em texto puro. Para conferir só o nome de cada campo e o começo do valor, sem imprimir o segredo inteiro na tela:

```bash
grep 'public static final String' jadx-out/sources/com/exemplo/app/BuildConfig.java \
  | sed -E 's/= "(.{0,6})[^"]*"/= "\1…"/'
```

O mesmo valor também pode aparecer duplicado nos recursos de string, se for referenciado por algum SDK nativo que espera configuração via `meta-data` no `AndroidManifest.xml`, prática comum em SDKs do Google (Firebase, Maps) e de terceiros:

```bash
grep -oE '<string name="[A-Z_]{4,}">' apktool-out/res/values/strings.xml
grep -i 'meta-data' apktool-out/AndroidManifest.xml
ls apk-out/assets
```

## Onde esse valor aparece no iOS

No iOS a mecânica muda: as variáveis de ambiente não ficam expostas num arquivo de configuração separado como o `Info.plist`. Elas são compiladas diretamente dentro do binário executável do app, misturadas ao código de máquina. Isso não as esconde, só muda a ferramenta usada para encontrá-las: em vez de ler uma classe decompilada, basta procurar o texto dentro do binário com uma ferramenta como o `strings`, do Unix:

```bash
strings Payload/Example.app/Example | grep -iE 'secret|client_id|api_key|https://' | sort -u | head -50
```

Na prática, isso quer dizer que um valor que vaza no Android (visível no `BuildConfig`) normalmente também está presente no binário iOS, só que num formato menos óbvio de inspecionar. Dá até para confirmar isso sem nunca imprimir o valor em texto puro: basta ler os nomes e valores do lado Android e procurar cada valor, em bytes, dentro do binário iOS.

```python
import re
bc = open('jadx-out/sources/com/exemplo/app/BuildConfig.java').read()
vals = dict(re.findall(r'String (\w+) = "([^"]{8,})"', bc))
blob = open('Payload/Example.app/Example', 'rb').read()
for k, v in vals.items():
    print(f'{k:32} {"SIM" if v.encode() in blob else "-"}')
```

## O que é aceitável e o que não é

Nem todo valor encontrado dentro de um binário é um problema. A linha entre "aceitável" e "vazamento" depende do que aquele valor permite fazer sozinho:

| Tipo | Exemplo | Pode ficar no app? |
|---|---|---|
| Identificador público | URL base da API, chave de API com restrição de domínio/app, DSN de um serviço de monitoramento de erros | Sim, foi feito para isso |
| Segredo de cliente confidencial | Client secret de OAuth, chave privada, token de serviço com privilégio amplo | Não. Quem extrai o valor consegue se passar pelo próprio app perante o backend |
| Metadado de build | Caminho interno de CI, identificador de time de assinatura | Idealmente não. Não compromete o app sozinho, mas revela detalhes do pipeline interno para quem for mapear a infraestrutura |

A categoria mais arriscada é a do meio: um "client secret" existe, por definição, para autenticar um cliente de software perante um servidor, mas um app distribuído numa loja pública não consegue guardar segredo de verdade, porque o próprio usuário (ou quem analisa o APK) tem acesso físico ao binário inteiro. É esse paradoxo que leva protocolos como o [OAuth 2.0 para apps nativos](https://datatracker.ietf.org/doc/html/rfc8252) a recomendar fluxos que não dependem de um client secret fixo embutido no app, como o [PKCE](https://datatracker.ietf.org/doc/html/rfc7636).

## Fechando o ciclo: da string até a linha de código

Encontrar um valor suspeito no `BuildConfig` ou no binário é só a primeira metade do trabalho. A segunda metade, quando há acesso ao código-fonte do app, é confirmar de onde aquele valor vem: uma busca pelo nome da variável no repositório costuma levar direto ao arquivo de configuração que a define, e aí dá para avaliar se aquilo foi intencional ou um descuido.

No próximo artigo: o `AndroidManifest.xml` depois de decodificado revela muito mais do que variáveis, ele define boa parte da superfície de ataque de um app, dos componentes exportados até a configuração de rede.
