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;
-
BuildConfigdo 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
BuildConfigdo 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.propertiese 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.gitignoredesta forma:
/keys.properties
- Rode no terminal o comando
git status. O arquivokeys.propertiesnão deverá estar listado para versionamento. Caso esteja, significa que a configuração do.gitignoreestá 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ódigoGroovyserá 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.gradledo projeto:
buildscript {
apply from: project.rootProject.file("keys.gradle")
...
}
- A esta altura, a
BuildConfigjá 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_KEYnobuild.gradledo 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.propertiesentre 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.gradleeAPI_KEYsão arbitrários -- apenas mantenha as extensões (.properties,.key,.gradle) ao renomear; -
NOTE QUE
BuildConfiggera código compilado, o que significa que engenharia reversa do.apkainda 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)