Jaka jest najlepsza praktyka postępowania z hasłami w repozytoriach git?

225

Mam mały skrypt Bash, którego używam do uzyskania dostępu do Twittera i wyskakiwania powiadomienia Growl w niektórych sytuacjach. Jaki jest najlepszy sposób na przechowywanie mojego hasła w skrypcie?

Chciałbym zatwierdzić ten skrypt w repozytorium git i udostępnić go w GitHub, ale zastanawiam się, jaki jest najlepszy sposób na zachowanie mojego loginu / hasła w tajemnicy podczas tej operacji. Obecnie hasło jest przechowywane w samym skrypcie. Nie mogę go usunąć tuż przed wypchnięciem, ponieważ wszystkie stare zmiany zawierają hasło. Opracowanie bez hasła nie jest opcją. Wyobrażam sobie, że powinienem przechowywać hasło w zewnętrznym pliku konfiguracyjnym, ale pomyślałem, że sprawdzę, czy istnieje ustalony sposób, aby sobie z tym poradzić, zanim spróbuję coś połączyć.

kubi
źródło

Odpowiedzi:

256

Typowym sposobem na to jest odczytanie informacji o haśle z pliku konfiguracyjnego. Jeśli plik konfiguracyjny zostanie wywołany foobar.config, wówczas zatwierdzisz plik wywołany foobar.config.exampledo repozytorium, zawierający przykładowe dane. Aby uruchomić program, należy utworzyć lokalny (nieśledzony) plik o nazwie foobar.configz danymi rzeczywistego hasła.

Aby odfiltrować istniejące hasło z poprzednich zatwierdzeń, zobacz stronę pomocy GitHub na stronie Usuwanie wrażliwych danych .

Greg Hewgill
źródło
4
Przy okazji możesz dodać przykładowy plik foobar.config do repozytorium, a następnie dodać plik foobar.config do pliku .ignore. W ten sposób po sklonowaniu pojawi się przykładowy plik foobar.config, a rzeczywiste hasła nie zostaną dodane do repozytorium.
Mr_Chimp
16
@Mr_Chimp: .gitignorePlik nie dotyczy śledzonych plików, które są już w repozytorium. Na przykład git add -udoda zmieniony plik, nawet jeśli jest już w .gitignore.
Greg Hewgill
1
Jako uzupełnienie, oto ciekawy link na wypadek, gdyby przypadkowo dodałeś plik konfiguracyjny i chcesz go usunąć z historii git: help.github.com/articles/remove-sensitive-data
Loïc Lopes
16
Jak poszedłbyś na dzielenie się tymi hasłami ze swoim zespołem? Jedną rzeczą jest posiadanie lokalnej kopii (niepowiązanej z repozytorium), drugą jest udostępnienie jej większej grupie nawet za pomocą automatycznych narzędzi (do wdrażania itp.)
blueFast
2
Mam to samo pytanie co @dangonfast. To nie wydaje się praktyczne dla dużego zespołu.
Jacob Stamm,
25

Podejściem może być ustawienie hasła (lub klucza API) przy użyciu zmiennej środowiskowej. To hasło jest poza kontrolą wersji.

W Bash możesz ustawić zmienną środowiskową za pomocą

export your_env_variable='your_password'

Tego podejścia można używać z usługami ciągłej integracji, takimi jak Travis , kod (bez hasła) przechowywany w repozytorium GitHub może być wykonywany przez Travis (z ustawionym hasłem przy użyciu zmiennej środowiskowej).

Dzięki Bash możesz uzyskać wartość zmiennej środowiskowej, używając:

echo "$your_env_variable"

Za pomocą Pythona możesz uzyskać wartość zmiennej środowiskowej, używając:

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

PS: pamiętaj, że jest to nieco ryzykowne (ale jest to dość powszechna praktyka) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-stealing-environment-variables/

PS2: ten dev.toartykuł zatytułowany „Jak bezpiecznie przechowywać klucze API” może być interesujący do przeczytania.

scls
źródło
1
Jak zapobiec potencjalnemu „niebezpiecznemu” kodowi, który jest w trakcie kompilacji, odczytać zawartość zmiennej środowiskowej?
gorootde
16

Co powiedział Greg, ale dodam, że dobrym pomysłem jest sprawdzenie pliku foobar.config-TEMPLATE.

Powinien zawierać przykładowe nazwy, hasła lub inne informacje o konfiguracji. Wtedy jest bardzo oczywiste, co powinien zawierać prawdziwy plik foobar.config, bez konieczności sprawdzania całego kodu, w którym wartości muszą być obecne foobar.configi w jakim formacie powinny mieć.

Często wartości konfiguracji mogą być nieoczywiste, takie jak parametry połączenia z bazą danych i podobne rzeczy.

Prof. Falken
źródło
7

Radzenie sobie z hasłami w repozytoriach byłoby obsługiwane na różne sposoby, w zależności od konkretnego problemu.

1. Nie rób tego.

Sposoby unikania tego są opisane w niektórych odpowiedziach - .gitignore, config.example itp

lub 2. Udostępnij repozytorium tylko upoważnionym osobom

To znaczy ludzie, którzy mogą znać hasło. chmodi przychodzą na myśl grupy użytkowników; także problemy, takie jak czy pracownicy Github lub AWS powinni mieć możliwość przeglądania rzeczy, jeśli przechowujesz swoje repozytoria lub serwery na zewnątrz?

lub 3. Zaszyfruj wrażliwe dane (cel tej odpowiedzi)

Jeśli chcesz przechowywać pliki konfiguracyjne zawierające poufne informacje (takie jak hasła) w miejscu publicznym, musisz je zaszyfrować. Pliki można odszyfrować po odzyskaniu z repozytorium, a nawet użyć bezpośrednio z ich zaszyfrowanej postaci.

Przykładowe rozwiązanie javascript do korzystania z zaszyfrowanych danych konfiguracyjnych pokazano poniżej.

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

Odszyfrowany plik konfiguracji

Możesz więc odzyskać zaszyfrowany plik konfiguracyjny zapisujący zaledwie kilka wierszy Javascript.

Zauważ, że umieszczenie pliku config.RSAw repozytorium git skutecznie uczyniłoby go plikiem binarnym, a zatem straciłoby wiele korzyści z czegoś takiego jak Git, np. Zdolność do wybierania w nim zmian.

Rozwiązaniem tego może być zaszyfrowanie par kluczy lub wartości. Możesz zaszyfrować wszystkie wartości, na przykład jeśli masz osobny plik dla poufnych informacji, lub zaszyfrować tylko wrażliwe wartości, jeśli masz wszystkie wartości w jednym pliku. (patrz poniżej)

Mój powyższy przykład jest nieco bezużyteczny dla każdego, kto chce z nim wykonać test lub jako przykład na początek, ponieważ zakłada istnienie niektórych kluczy RSA i zaszyfrowanego pliku konfiguracyjnego config.RSA.

Oto kilka dodatkowych wierszy kodu dodanych do tworzenia kluczy RSA i pliku konfiguracyjnego do zabawy.

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

Tylko szyfrowanie wartości

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

wprowadź opis zdjęcia tutaj

Możesz odszyfrować plik konfiguracyjny z zaszyfrowanymi wartościami, używając czegoś takiego.

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

Z każdym elementem konfiguracji w osobnym wierszu (np. HelloI Goodbyewyżej) Git lepiej rozpozna, co się dzieje w pliku, i zapisze zmiany w elementach informacji jako różnice, a nie pełne pliki. Git będzie również w stanie lepiej zarządzać połączeniami i typami wiśni itp.

Jednak im bardziej chcesz kontrolować zmiany wersji wrażliwych informacji, tym bardziej zbliżasz się do rozwiązania BEZPIECZNA REPOZYTORIUM (2) i odejdziesz od rozwiązania ZASZYFROWANE INFORMACJE (3).

Ivan
źródło
3

Można użyć Vault, który zabezpiecza, przechowuje i kontroluje dostęp do tokenów, haseł, certyfikatów, kluczy API itp. Na przykład Ansible używa Vault Ansible, który zajmuje się hasłami lub certyfikatami używanymi w podręcznikach

El Ruso
źródło
Uważam, że Ansible Vault jest zbyt skomplikowany z pewnością w porównaniu do zwykłego tworzenia przykładowego pliku konfiguracyjnego.
icc97
@ icc97 Tak, to smutna prawda. Ale musimy wspomnieć o tej możliwości. Moim zdaniem w przypadku zadań bardziej skomplikowanych niż przechowywanie kilku haseł do środowiska dla jednego użytkownika lepiej od początku używać specjalistycznych rozwiązań.
El Ruso
2
Aby pomóc przyszłym czytelnikom: Vault i Ansible Vault to zupełnie różne niepowiązane projekty o podobnych nazwach
bltavares
2

Oto technika, której używam:

W moim katalogu domowym tworzę folder o nazwie: .config

W tym folderze umieszczam pliki konfiguracyjne dla dowolnej liczby rzeczy, które chcę uzewnętrznić hasła i klucze.

Zazwyczaj używam składni odwrotnej nazwy domeny, takiej jak:

com.example.databaseconfig

Następnie w skrypcie bash robię to:

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

|| exit 1Wywołuje skrypt do wyjścia, jeśli nie jest w stanie załadować plik konfiguracyjny.

Użyłem tej techniki do skryptów bash, python i ant.

Jestem dość paranoikiem i nie sądzę, że plik .gitignore jest wystarczająco solidny, aby zapobiec niezamierzonemu zameldowaniu. Co więcej, nic nie monitoruje, więc jeśli nastąpiłaby odprawa, nikt nie dowiedziałby się, jak sobie z tym poradzić.

Jeśli określona aplikacja wymaga więcej niż jednego pliku, tworzę podfolder zamiast jednego pliku.

Michael Potter
źródło
1

Jeśli używasz rubinu na szynach, klejnot Figaro jest bardzo dobry, łatwy i niezawodny. Ma niski współczynnik bólu głowy również w środowisku produkcyjnym.

ahnbizcad
źródło
4
Czy możesz podać kilka szczegółów na temat tego, co robi ten klejnot? W ten sposób można (potencjalnie) uznać ją za „praktykę” mającą zastosowanie w wielu językach.
mattumotu,
medium.com/@MinimalGhost/… ma przegląd, w zasadzie wydaje się, że radzi sobie z
pobieraniem
0

Ufaj ale sprawdzaj.

W .gitignoreten sposób wykluczono by „bezpieczny” katalog z repozytorium:

secure/

Ale podzielam paranoję @ Michaela Pottera . Tak więc, aby zweryfikować .gitignore, oto test jednostkowy Pythona , który podniósłby klakson, gdyby ten „bezpieczny” katalog kiedykolwiek został wpisany. Aby sprawdzić, sprawdzany jest również legalny katalog:

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
źródło