Imagem de capa por Bruno Martins @ Unsplash
Aviso
- Nenhuma medida por si só tornará nossas aplicações seguras. A segurança dum app depende duma combinação de medidas, e até mesmo as sofisticadas podem ser quebradas;
- Dito isso, destaco que este tutorial não consiste numa bala de prata para todos os nossos problemas de segurança, mas numa medida que pode deixar nossos apps menos sujeitos a vazamentos de dados.
Não li, nem lerei
- Chaves de API são utilizadas para autenticação de clientes em serviços específicos;
- A revelação de informações sensíveis, como as chaves de API, pode acarretar usos e consequências não planejados;
-
BuildConfig
do Gradle permite ao nosso projeto ler dados sensíveis contidos num arquivo local, na forma de código gerado.
Contexto
A divulgação de dados sensíveis tem o potencial de causar danos tanto aos nossos próprios interesses quanto aos das pessoas impactadas por nossos projetos.
Embora tenha focado em chaves de API, outros dados sensíveis como endpoints e credenciais também podem ser guardados localmente com a abordagem descrita aqui.
Os quês
Antes de tudo, é melhor termos a certeza de que estamos falando das mesmas coisas:
Cliente refere-se ao software ou hardware que faz uso dos serviços disponíveis num servidor, no modelo cliente-servidor <1>;
API significa Interface de Programação de Aplicativos, que, em termos gerais, é um conjunto de regras a serem observadas na comunicação entre programas, geralmente um cliente e um serviço <2>;
Segredo, token ou chave de API é um identificador único utilizado na autenticação de um cliente num serviço<3>;
Geração de código é o processo pelo qual um compilador converte representações de código em instruções que podem ser prontamente executadas pelo dispositivo <4>.
Os porquês
- Chaves de API permitem identificar clientes e são geradas para fins específicos. Por isso, é comum que a conta do cliente incorra em custos de acordo com as quotas de uso do serviço. - Google Maps Platform é um bom exemplo disso, já que os preços variam conforme a utilização;
- Evitar a revelação dessas chaves é fundamental na prevenção de usos indevidos.
Apesar disso, o versionamento de chaves de API é um erro que não apenas eu mesmo cometi no passado, mas que também vi colegas de profissão cometerem, independentemente da experiência.
A verdade é que, infelizmente, práticas de Segurança da Informação ainda são pouquíssimo divulgadas nas nossas comunidades de desenvolvimento.
O como
- A classe
BuildConfig
do Gradle permite a leitura de valores a partir de arquivos locais e a disponibilização deles na forma de código gerado; - Contanto que indiquemos corretamente os arquivos no
.gitignore
, a divulgação das nossas chaves de API no GitHub, GitLab e afins não deverá ser um problema.
O código
São oito as etapas:
1. Criar keys.properties
na raiz
- Na raiz do projeto, crie um arquivo chamado
keys.properties
e adicione a ele a chave da API desta forma:
api.key=API_KEY_HERE
2. Adicionar o caminho do arquivo .gitignore
- Muita atenção a esta etapa, pois o
.gitignore
é que indica quais arquivos ficam fora do versionamento! - Para prevenir o versionamento do
keys.properties
, adicione o caminho do arquivo ao.gitignore
desta forma:
/keys.properties
- Rode no terminal o comando
git status
. O arquivokeys.properties
não deverá estar listado para versionamento. Caso esteja, significa que a configuração do.gitignore
está errada! Repita a etapa.
3. Criar keys.gradle
na raiz
- Para recuperar os valores do
keys.properties
, adicione à raiz do projeto um arquivo chamadokeys.gradle
, onde o seguinte códigoGroovy
será adicionado:
// Define como carregar as informações do arquivo local
def getStringFromFile(fileName, property) {
Properties props = new Properties()
props.load(project.rootProject.file(fileName).newDataInputStream())
return props[property].toString()
}
// Indica keys.properties como o arquivo para recuperação dos valores
def getStringFromKeysFile(property) {
return getStringFromFile('keys.properties', property).toString()
}
// Expõe a chave definida no keys.properties como uma variável para uso em todo o projeto
ext.keys = ["apiKey": getStringFromKeysFile('api.key'),]
4. Importe keys.gradle
dentro do project/build.gradle
- Para utilizar os métodos definidos no
keys.gradle
, importe o arquivo dentro dobuild.gradle
do projeto:
buildscript {
apply from: project.rootProject.file("keys.gradle")
...
}
- A esta altura, a
BuildConfig
já consegue automaticamente converter nossa chave em código compilado -- mas só faremos isso na etapa 6.
5. Definir uma constante para a chave dentro do app/build.gradle
- Defina uma constante chamada
API_KEY
nobuild.gradle
do app desta forma:
android {
...
buildTypes {
// Embora esteja configurado para 'debug', certifique-se de quais build variants (debug, staging, release etc) precisarão da chave:
debug {
...
it.buildConfigField "String", "API_KEY", keys.apiKey
}
}
6. Limpar e recompilar o projeto
- Para que o código seja efetivamente gerado, selecione Build > Clean Project e depois Build > Rebuild Project no menu superior do Android Studio.
7. Chamar a variável via BuildConfig
dentro do projeto
- Chame a constante via
BuildConfig.API_KEY
, conforme etapa 5, onde for necessário no código. Por exemplo:
class CustomApplication : Application() {
override fun onCreate() {
super.onCreate()
Firebase.init(this, BuildConfig.API_KEY)
}
}
8. Compartilhar o arquivo keys.properties
de forma segura dentro do time
- Já que evitamos o versionamento da chave da API, é necessário utilizar uma maneira segura de compartilhar o
keys.properties
entre as pessoas da equipe; - Caso seu trabalho conte com um time de Segurança da Informação, verifique com ele qual a melhor forma de compartilhar o arquivo;
- Do contrário, ferramentas como 1Password podem ajudar, já que permitem a gestão de direitos de acesso aos arquivos.
Considerações finais
- Outras informações sensíveis como endpoint e credenciais também pode sem mantidas localmente com essa abordagem;
- Nomes como
keys.properties
,api.key
,keys.gradle
eAPI_KEY
são arbitrários -- apenas mantenha as extensões (.properties
,.key
,.gradle
) ao renomear; -
NOTE QUE
BuildConfig
gera código compilado, o que significa que engenharia reversa do.apk
ainda pode expor chaves de API e outros segredos; - Apenas a combinação de medidas de segurança pode nos ajudar a manter nossos apps seguros. Busque também ferramentas de otimização e ofuscação de código como R8 para reduzir as chances de vazamento de dados.
Agradecimentos especiais ao Victor Vieira por me ensinar essa abordagem há um ano; e ao Walmyr Carvalho por me estimular a escrever aqui no dev.to!
Top comments (0)