Qual é a melhor prática para lidar com senhas em repositórios git?

225

Eu tenho um pequeno script do Bash que eu uso para acessar o twitter e exibir uma notificação do Growl em determinadas situações. Qual é a melhor maneira de lidar com o armazenamento da minha senha com o script?

Gostaria de confirmar esse script no repositório git e disponibilizá-lo no GitHub, mas estou me perguntando qual a melhor maneira de manter meu login / senha privado enquanto o faço. Atualmente, a senha é armazenada no próprio script. Não consigo removê-lo logo antes de enviar porque todos os commits antigos conterão a senha. Desenvolver sem uma senha não é uma opção. Eu imagino que deveria estar armazenando a senha em um arquivo de configuração externo, mas pensei em verificar se havia uma maneira estabelecida de lidar com isso antes de tentar montar algo.

kubi
fonte

Respostas:

256

A maneira típica de fazer isso é ler as informações da senha de um arquivo de configuração. Se seu arquivo de configuração for chamado foobar.config, você confirmará um arquivo chamado foobar.config.examplepara o repositório, contendo dados de amostra. Para executar seu programa, você criaria um arquivo local (não rastreado) chamado foobar.configcom seus dados de senha reais .

Para filtrar sua senha existente das confirmações anteriores, consulte a página de ajuda do GitHub em Removendo dados confidenciais .

Greg Hewgill
fonte
4
Aliás, você pode adicionar um exemplo foobar.config ao repositório e, em seguida, adicionar foobar.config ao arquivo .ignore. Dessa forma, o exemplo foobar.config aparecerá quando clonado e suas senhas reais não serão adicionadas ao repositório.
precisa saber é o seguinte
16
@ Mr_Chimp: O .gitignorearquivo não se aplica aos arquivos rastreados que estão no repositório. Por exemplo, git add -uirá adicionar um arquivo alterado mesmo se ele já está em .gitignore.
Greg Hewgill
1
Como complemento, aqui está um link interessante, caso você tenha adicionado o arquivo de configuração por acidente e deseja excluí-lo do histórico do git: help.github.com/articles/remove-sensitive-data
Loïc Lopes
16
Como você compartilharia essas senhas com sua equipe? Uma coisa é ter uma cópia local (não comprometidos com o acordo de recompra), o outro é para compartilhá-lo com uma grande equipe de mesmo com ferramentas automáticas (para implantação, etc)
blueFast
2
Tenho a mesma pergunta que @dangonfast. Isso não parece prático para uma equipe grande.
Jacob Stamm
25

Uma abordagem pode ser definir a senha (ou chave da API) usando uma variável de ambiente. Portanto, essa senha está fora de controle de revisão.

Com o Bash, você pode definir variáveis ​​de ambiente usando

export your_env_variable='your_password'

Essa abordagem pode ser usada com serviços de integração contínua como o Travis , seu código (sem senha) armazenado em um repositório do GitHub pode ser executado pelo Travis (com sua senha configurada usando a variável de ambiente).

Com o Bash, você pode obter o valor de uma variável de ambiente usando:

echo "$your_env_variable"

Com o Python, você pode obter o valor de uma variável de ambiente usando:

import os
print(os.environ['your_env_variable'])

PS: saiba que é provavelmente um pouco arriscado (mas é uma prática bastante comum) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-stealing-environment-variables/

PS2: este dev.toartigo intitulado "Como armazenar com segurança chaves de API" pode ser interessante de ler.

scls
fonte
1
Como impedir que o código "inseguro" em potencial que está sendo construído leia o conteúdo da variável de ambiente?
gorootde
16

O que Greg disse, mas eu acrescentaria que é uma boa ideia fazer check-in em um arquivo foobar.config-TEMPLATE.

Ele deve conter nomes de exemplo, senhas ou outras informações de configuração. Então é muito óbvio o que o verdadeiro foobar.config deve conter, sem precisar procurar em todo o código quais valores devem estar presentes foobar.confige qual formato eles devem ter.

Frequentemente, os valores de configuração podem não ser óbvios, como cadeias de conexão de banco de dados e coisas semelhantes.

Prof. Falken
fonte
7

Lidar com senhas em repositórios seria tratado de maneiras diferentes, dependendo do seu problema exato.

1. Não faça isso.

E maneiras de evitar fazer isso são cobertas em algumas respostas - .gitignore, config.example, etc

ou 2. Tornar o repositório acessível apenas a pessoas autorizadas

Ou seja, pessoas que têm permissão para saber a senha. chmodgrupos de usuários vem à mente; também problemas como os funcionários do Github ou da AWS podem ver coisas se você hospedar seus repositórios ou servidores externamente?

ou 3. Criptografar os dados confidenciais (objetivo desta resposta)

Se você deseja armazenar seus arquivos de configuração que contêm informações confidenciais (como senhas) em um local público, ele precisa ser criptografado. Os arquivos podem ser descriptografados quando recuperados do repositório, ou mesmo usados ​​diretamente de sua forma criptografada.

Um exemplo de solução javascript para usar dados de configuração criptografados é mostrado abaixo.

const fs = require('fs');
const NodeRSA = require('node-rsa');

let privatekey = new NodeRSA();
privatekey.importKey(fs.readFileSync('private.key', 'utf8'));
const config = privatekey.decrypt(fs.readFileSync('config.RSA', 'utf8'), 'json');

console.log('decrypted: ', config);

Arquivo de configuração descriptografado

Assim, você pode recuperar um arquivo de configuração criptografado escrevendo apenas algumas linhas de Javascript.

Observe que colocar um arquivo config.RSAem um repositório git o tornaria efetivamente um arquivo binário e, portanto, perderia muitos dos benefícios de algo como o Git, por exemplo, a capacidade de escolher as mudanças nele.

A solução para isso pode ser criptografar pares de valores-chave ou talvez apenas valores. Você pode criptografar todos os valores, por exemplo, se tiver um arquivo separado para informações confidenciais, ou criptografar apenas os valores confidenciais se tiver todos os valores em um arquivo. (ver abaixo)

Meu exemplo acima é um pouco inútil para quem deseja fazer um teste com ele, ou como um exemplo para começar, pois assume a existência de algumas chaves RSA e um arquivo de configuração criptografado config.RSA.

Então, aqui estão algumas linhas de código extras adicionadas para criar chaves RSA e um arquivo de configuração para brincar.

const fs = require('fs');
const NodeRSA = require('node-rsa');

/////////////////////////////
// Generate some keys for testing
/////////////////////////////

const examplekey = new NodeRSA({b: 2048});

fs.writeFileSync('private.key', examplekey.exportKey('pkcs8-private'));
fs.writeFileSync('public.key', examplekey.exportKey('pkcs8-public'));

/////////////////////////////
// Do this on the Machine creating the config file
/////////////////////////////

const configToStore = {Goodbye: 'Cruel world'};

let publickey = new NodeRSA();
publickey.importKey(fs.readFileSync('public.key', 'utf8'));

fs.writeFileSync('config.RSA', publickey.encrypt(configToStore, 'base64'), 'utf8');

/////////////////////////////
// Do this on the Machine consuming the config file
/////////////////////////////

let privatekey = new NodeRSA();
privatekey.importKey(fs.readFileSync('private.key', 'utf8'));

const config = privatekey.decrypt(fs.readFileSync('config.RSA', 'utf8'), 'json');
console.log('decrypted: ', config);

Criptografando apenas valores

fs.writeFileSync('config.RSA', JSON.stringify(config,null,2), 'utf8');

insira a descrição da imagem aqui

Você pode descriptografar um arquivo de configuração com valores criptografados usando algo como isto.

const savedconfig = JSON.parse(fs.readFileSync('config.RSA', 'utf8'));
let config = {...savedconfig};
Object.keys(savedconfig).forEach(key => {
    config[key] = privatekey.decrypt(savedconfig[key], 'utf8');
});

Com cada item de configuração em uma linha separada (por exemplo, Helloe Goodbyeacima), Git irá reconhecer melhor o que está acontecendo em um arquivo e armazena alterações nos itens de informação como as diferenças ao invés de arquivos completos. O Git também será capaz de gerenciar fusões e escolhas de cereja, etc.

No entanto, quanto mais o controle de versão for alterado para informações confidenciais, mais você estará caminhando para uma solução de REPOSITÓRIO SEGURO (2) e se afastando de uma solução de INFO ENCRYPTED (3).

Ivan
fonte
3

Pode-se usar o Vault que protege, armazena e controla o acesso a tokens, senhas, certificados, chaves de API, etc. Por exemplo, o Ansible usa o Ansible Vault que lida com senhas ou certificados usados ​​em playbooks

El Ruso
fonte
Acho o Ansible Vault muito complexo, certamente comparado a apenas criar um arquivo de configuração de exemplo.
Icc97 7/05
@ icc97 Sim, é triste verdade. Mas precisamos mencionar essa possibilidade. Na minha opinião, para tarefas mais complexas do que armazenar poucas senhas para o ambiente de usuário único, é melhor usar soluções especializadas desde o início.
El Ruso
2
Para ajudar os futuros leitores: Vault e Ansible Vault são bastante diferentes projetos não relacionados com nomes semelhantes
bltavares
2

Aqui está uma técnica que eu uso:

Eu crio uma pasta na minha pasta pessoal chamada: .config

Nessa pasta, coloco os arquivos de configuração para qualquer número de coisas que eu queira externalizar senhas e chaves.

Eu normalmente uso sintaxe de nome de domínio reverso, como:

com.example.databaseconfig

Em seguida, no script bash, faço o seguinte:

#!/bin/bash
source $HOME/.config/com.example.databaseconfig ||exit 1

O || exit 1faz com que o script para saída se não é capaz de carregar o arquivo de configuração.

Eu usei essa técnica para scripts bash, python e ant.

Sou bastante paranóico e não acho que um arquivo .gitignore seja suficientemente robusto para impedir um check-in inadvertido. Além disso, não há nada monitorando, por isso, se um check-in acontecesse, ninguém descobriria como lidar com isso.

Se um aplicativo específico exigir mais de um arquivo, crio uma subpasta em vez de um único arquivo.

Michael Potter
fonte
1

Se você estiver usando rubi nos trilhos, a gema Figaro é muito boa, fácil e confiável. Também possui um baixo fator de dor de cabeça no ambiente de produção.

ahnbizcad
fonte
4
Você pode dar alguns detalhes sobre o que essa jóia faz? Dessa forma, poderia (potencialmente) ser considerada uma 'prática' aplicável em vários idiomas.
mattumotu 20/09/16
medium.com/@MinimalGhost/... tem uma visão geral, basicamente parece gerenciar puxando em material de um arquivo de configuração
tripleee
0

Confie mas verifique.

Em .gitignoreesta excluiria um diretório "seguro" do repo:

secure/

Mas eu compartilho a paranóia de @ Michael Potter . Portanto, para verificar .gitignore, aqui está um teste de unidade Python que geraria um klaxon se esse diretório "seguro" fosse verificado. E para verificar a verificação, um diretório legítimo também é testado:

def test_github_not_getting_credentials(self):
    safety_url = 'https://github.com/BobStein/fliki/tree/master/static'
    danger_url = 'https://github.com/BobStein/fliki/tree/master/secure'

    self.assertEqual(200, urllib.request.urlopen(safety_url).status)

    with self.assertRaises(urllib.error.HTTPError):
        urllib.request.urlopen(danger_url)
Bob Stein
fonte