Quelle est la meilleure pratique pour gérer les mots de passe dans les référentiels git?

225

J'ai un petit script Bash que j'utilise pour accéder à Twitter et afficher une notification Growl dans certaines situations. Quelle est la meilleure façon de gérer le stockage de mon mot de passe avec le script?

Je voudrais valider ce script dans le dépôt git et le rendre disponible sur GitHub, mais je me demande quelle est la meilleure façon de garder mon identifiant / mot de passe privé en faisant cela. Actuellement, le mot de passe est stocké dans le script lui-même. Je ne peux pas le supprimer juste avant de pousser car tous les anciens commits contiendront le mot de passe. Développer sans mot de passe n'est pas une option. J'imagine que je devrais stocker le mot de passe dans un fichier de configuration externe, mais j'ai pensé que je vérifierais s'il y avait un moyen établi de gérer cela avant d'essayer de mettre quelque chose en place.

kubi
la source

Réponses:

256

La façon typique de le faire est de lire les informations de mot de passe à partir d'un fichier de configuration. Si votre fichier de configuration est appelé foobar.config, vous devez valider un fichier appelé foobar.config.exampledans le référentiel, contenant des exemples de données. Pour exécuter votre programme, vous devez créer un fichier local (non suivi) appelé foobar.configavec votre réel des données de mot de passe.

Pour filtrer votre mot de passe existant des validations précédentes, consultez la page d'aide de GitHub sur la suppression des données sensibles .

Greg Hewgill
la source
4
Btw, vous pouvez ajouter un exemple foobar.config au référentiel, puis ajouter foobar.config au fichier .ignore. De cette façon, l'exemple foobar.config apparaîtra lors du clonage et vos mots de passe réels ne seront pas ajoutés au dépôt.
Mr_Chimp
16
@Mr_Chimp: le .gitignorefichier ne s'applique pas aux fichiers suivis qui sont déjà dans le référentiel. Par exemple, git add -uajoutera un fichier modifié même s'il est déjà dans .gitignore.
Greg Hewgill
1
En complément, voici un lien intéressant au cas où vous auriez ajouté le fichier de configuration par accident et que vous voudriez le supprimer de l'historique git: help.github.com/articles/remove-sensitive-data
Loïc Lopes
16
Comment feriez-vous pour partager ces mots de passe avec votre équipe? Une chose est d'avoir une copie locale (non engagée dans le repo), l'autre est de la partager avec une plus grande équipe de même avec des outils automatiques (pour le déploiement, etc.)
blueFast
2
J'ai la même question que @dangonfast. Cela ne semble pas pratique pour une grande équipe.
Jacob Stamm
25

Une approche peut être de définir un mot de passe (ou une clé API) à l'aide d'une variable d'environnement. Ce mot de passe est donc hors contrôle de révision.

Avec Bash, vous pouvez définir une variable d'environnement en utilisant

export your_env_variable='your_password'

Cette approche peut être utilisée avec des services d'intégration continue comme Travis , votre code (sans mot de passe) stocké dans un référentiel GitHub peut être exécuté par Travis (avec votre mot de passe défini à l'aide d'une variable d'environnement).

Avec Bash, vous pouvez obtenir la valeur d'une variable d'environnement en utilisant:

echo "$your_env_variable"

Avec Python, vous pouvez obtenir la valeur d'une variable d'environnement en utilisant:

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

PS: sachez que c'est probablement un peu risqué (mais c'est une pratique assez courante) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-stealing-environment-variables/

PS2: cet dev.toarticle intitulé "Comment stocker en toute sécurité les clés d'API" peut être intéressant à lire.

scls
la source
1
Comment empêcher le code potentiel "dangereux" d'être construit de lire le contenu de votre variable d'environnement?
gorootde
16

Ce que Greg a dit mais j'ajouterais que c'est une bonne idée d'archiver un fichier foobar.config-TEMPLATE.

Il doit contenir des exemples de noms, mots de passe ou autres informations de configuration. Ensuite, il est très évident ce que le vrai foobar.config doit contenir, sans avoir à chercher dans tout le code les valeurs qui doivent être présentes foobar.configet le format qu'elles doivent avoir.

Souvent, les valeurs de configuration peuvent être non évidentes, comme les chaînes de connexion à la base de données et des choses similaires.

Prof. Falken
la source
7

Le traitement des mots de passe dans les référentiels serait géré de différentes manières en fonction de votre problème exact.

1. Ne le faites pas.

Et les moyens d'éviter de le faire sont traités dans certaines réponses - .gitignore, config.example, etc.

ou 2. Rendre le référentiel accessible uniquement aux personnes autorisées

C'est-à-dire les personnes autorisées à connaître le mot de passe. chmodet les groupes d'utilisateurs me viennent à l'esprit; des problèmes tels que les employés de Github ou AWS devraient-ils être autorisés à voir les choses si vous hébergez vos référentiels ou vos serveurs en externe?

ou 3. Chiffrer les données sensibles (objet de cette réponse)

Si vous souhaitez stocker vos fichiers de configuration contenant des informations sensibles (comme les mots de passe) dans un endroit public, ils doivent être cryptés. Les fichiers peuvent être décryptés lorsqu'ils sont récupérés à partir du référentiel, ou même utilisés directement à partir de leur forme cryptée.

Un exemple de solution javascript pour utiliser les données de configuration cryptées est illustré ci-dessous.

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);

Fichier de configuration déchiffré

Vous pouvez donc récupérer un fichier de configuration crypté en écrivant seulement quelques lignes de Javascript.

Notez que le fait de placer un fichier config.RSAdans un référentiel git en ferait effectivement un fichier binaire et qu'il perdrait ainsi de nombreux avantages de quelque chose comme Git, par exemple la possibilité de choisir les modifications.

La solution à cela pourrait être de chiffrer des paires de valeurs clés ou peut-être simplement des valeurs. Vous pouvez crypter toutes les valeurs, par exemple si vous avez un fichier séparé pour les informations sensibles, ou crypter uniquement les valeurs sensibles si vous avez toutes les valeurs dans un fichier. (voir ci-dessous)

Mon exemple ci-dessus est un peu inutile pour quiconque souhaite faire un test avec lui, ou comme exemple pour commencer car il suppose l'existence de certaines clés RSA et d'un fichier de configuration crypté config.RSA.

Voici donc quelques lignes de code supplémentaires ajoutées pour créer des clés RSA et un fichier de configuration avec lequel jouer.

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);

Chiffrement des valeurs uniquement

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

entrez la description de l'image ici

Vous pouvez déchiffrer un fichier de configuration avec des valeurs chiffrées en utilisant quelque chose comme ça.

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

Avec chaque élément de configuration sur une ligne distincte (par exemple Helloet Goodbyeau - dessus), Git reconnaîtra mieux ce qui se passe dans un fichier et stockera les modifications des éléments d'information sous forme de différences plutôt que de fichiers complets. Git sera également en mesure de mieux gérer les fusions et les choix de cerises, etc.

Cependant, plus vous souhaitez contrôler les versions des informations sensibles, plus vous vous déplacez vers une solution SAFE REPOSITORY (2) et loin d'une solution ENCRYPTED INFO (3).

Ivan
la source
3

On peut utiliser Vault qui sécurise, stocke et contrôle l'accès aux jetons, mots de passe, certificats, clés API, etc. Par exemple, Ansible utilise le coffre-fort Ansible qui traite des mots de passe ou des certificats utilisés dans les playbooks

El Ruso
la source
Je trouve Ansible Vault trop complexe certainement par rapport à la simple création d'un exemple de fichier de configuration.
icc97
@ icc97 Oui, c'est triste vrai. Mais nous devons mentionner cette possibilité. À mon avis, pour des tâches plus complexes, le stockage de quelques mots de passe pour un environnement mono-utilisateur est préférable d'utiliser des solutions spécialisées dès le début.
El Ruso
2
Pour aider les futurs lecteurs: Vault et Ansible Vault sont des projets sans rapport assez différents avec des noms similaires
bltavares
2

Voici une technique que j'utilise:

Je crée un dossier dans mon dossier d'accueil appelé: .config

Dans ce dossier, je place les fichiers de configuration pour un certain nombre de choses que je veux externaliser les mots de passe et les clés.

J'utilise généralement une syntaxe de nom de domaine inversée telle que:

com.example.databaseconfig

Ensuite, dans le script bash, je fais ceci:

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

Le || exit 1provoque la fermeture du script s'il n'est pas en mesure de charger le fichier de configuration.

J'ai utilisé cette technique pour les scripts bash, python et ant.

Je suis assez paranoïaque et je ne pense pas qu'un fichier .gitignore soit suffisamment robuste pour empêcher un enregistrement par inadvertance. De plus, il n'y a rien de surveillant, donc si un enregistrement a lieu, personne ne découvrira comment y faire face.

Si une application particulière nécessite plus d'un fichier, je crée un sous-dossier plutôt qu'un seul fichier.

Michael Potter
la source
1

Si vous utilisez du rubis sur des rails, le bijou Figaro est très bon, facile et fiable. Il présente également un faible facteur de maux de tête avec l'environnement de production.

ahnbizcad
la source
4
Pouvez-vous donner des détails sur ce que fait ce joyau? De cette façon, il pourrait (potentiellement) être considéré comme une «pratique» applicable dans de nombreuses langues.
mattumotu
medium.com/@MinimalGhost/… a une vue d'ensemble, il semble essentiellement gérer la récupération de trucs à partir d'un fichier de configuration
tripleee
0

Faites confiance mais vérifiez.

Dans .gitignorece cas, un répertoire "sécurisé" du référentiel serait exclu:

secure/

Mais je partage la paranoïa de @ Michael Potter . Donc, pour vérifier .gitignore, voici un test unitaire Python qui déclencherait un klaxon si ce répertoire "sécurisé" était jamais enregistré. Et pour vérifier la vérification, un répertoire légitime est également testé:

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
la source