Как лучше всего работать с паролями в git-репозиториях?

225

У меня есть небольшой скрипт Bash, который я использую для доступа к твиттеру и всплывающего уведомления Growl в определенных ситуациях. Каков наилучший способ хранения моего пароля с помощью скрипта?

Я хотел бы передать этот сценарий в репозиторий git и сделать его доступным на GitHub, но мне интересно, как лучше всего сохранить свой логин / пароль в секрете при этом. В настоящее время пароль хранится в самом скрипте. Я не могу удалить его прямо перед тем, как нажать, потому что все старые коммиты будут содержать пароль. Разработка без пароля не вариант. Я предполагаю, что я должен хранить пароль во внешнем конфигурационном файле, но я решил проверить, существует ли установленный способ справиться с этим, прежде чем пытаться что-то собрать.

куби
источник

Ответы:

256

Типичный способ сделать это - прочитать информацию о пароле из файла конфигурации. Если ваш файл конфигурации вызывается foobar.config, то вы должны зафиксировать файл, вызываемый foobar.config.exampleв хранилище, содержащий примеры данных. Чтобы запустить вашу программу, вы должны создать локальный (не отслеживаемый) файл, который будет называться foobar.configс данными вашего реального пароля.

Чтобы отфильтровать существующий пароль из предыдущих коммитов, см. Страницу справки GitHub в разделе Удаление конфиденциальных данных .

Грег Хьюгилл
источник
4
Кстати, вы можете добавить пример foobar.config в репозиторий, а затем добавить foobar.config в файл .ignore. Таким образом, пример foobar.config будет отображаться при клонировании, и ваши действительные пароли не будут добавлены в репозиторий.
Mr_Chimp
16
@Mr_Chimp: .gitignoreфайл не применяется к отслеживаемым файлам, которые уже находятся в хранилище. Например, git add -uдобавит измененный файл, даже если он уже находится в .gitignore.
Грег Хьюгилл
1
В качестве дополнения, вот интересная ссылка на случай, если вы случайно добавили файл конфигурации и хотите удалить его из истории git: help.github.com/articles/remove-sensitive-data
Loïc Lopes
16
Как бы вы поделились этими паролями со своей командой? Одно дело иметь локальную копию (не привязанную к репо), другое - поделиться ею с большой командой, даже с автоматическими инструментами (для развертывания и т. Д.)
blueFast
2
У меня тот же вопрос, что и у @dangonfast. Это не кажется практичным для большой команды.
Джейкоб
25

Подход может состоять в том, чтобы установить пароль (или ключ API), используя переменную среды. Таким образом, этот пароль находится вне контроля версий.

С помощью Bash вы можете установить переменную окружения, используя

export your_env_variable='your_password'

Этот подход можно использовать с такими службами непрерывной интеграции, как Travis , при этом ваш код (без пароля), хранящийся в репозитории GitHub, может быть выполнен Travis (с вашим паролем, установленным с помощью переменной среды).

С помощью Bash вы можете получить значение переменной окружения, используя:

echo "$your_env_variable"

С помощью Python вы можете получить значение переменной окружения, используя:

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

PS: имейте в виду, что это, вероятно, немного рискованно (но это довольно распространенная практика) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-stealing-environment-variables/

PS2: эта dev.toстатья под названием «Как безопасно хранить ключи API» может быть интересной для чтения.

SCLS
источник
1
Как предотвратить потенциальную «небезопасную» сборку кода beeing от чтения содержимого переменной среды?
gorootde
16

Что сказал Грег, но я бы добавил, что это хорошая идея, чтобы проверить файл foobar.config-TEMPLATE.

Он должен содержать примеры имен, паролей или другой информации о конфигурации. Тогда совершенно очевидно, что должен содержать настоящий foobar.config, без необходимости искать во всем коде, в каких значениях должны присутствовать foobar.configи в каком формате они должны иметь.

Часто значения конфигурации могут быть неочевидными, например строки подключения к базе данных и тому подобное.

Проф. Фалькен
источник
7

Работа с паролями в репозиториях будет обрабатываться различными способами в зависимости от вашей конкретной проблемы.

1. Не делай этого.

И способы избежать этого описаны в некоторых ответах - .gitignore, config.example и т. Д.

или 2. Сделать репозиторий доступным только для авторизованных людей.

Т.е. люди, которым разрешено знать пароль. chmodи группы пользователей приходят на ум; также такие проблемы, как следует ли разрешать сотрудникам Github или AWS видеть вещи, если вы размещаете свои репозитории или серверы извне?

или 3. зашифровать конфиденциальные данные (цель этого ответа)

Если вы хотите хранить свои файлы конфигурации, содержащие конфиденциальную информацию (например, пароли), в общедоступном месте, то их необходимо зашифровать. Файлы могут быть расшифрованы при восстановлении из хранилища или даже использованы прямо из зашифрованной формы.

Пример решения javascript для использования зашифрованных данных конфигурации показан ниже.

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

Расшифрованный файл конфигурации

Таким образом, вы можете восстановить зашифрованный файл конфигурации, написав всего несколько строк Javascript.

Обратите внимание, что помещение файла config.RSAв репозиторий git фактически сделает его бинарным файлом, и поэтому оно утратит многие преимущества чего-то вроде Git, например, возможность выбирать изменения в нем.

Решением этой проблемы может быть шифрование пар ключ-значение или, возможно, просто значений. Вы можете зашифровать все значения, например, если у вас есть отдельный файл для конфиденциальной информации, или зашифровать только конфиденциальные значения, если у вас есть все значения в одном файле. (увидеть ниже)

Мой пример, приведенный выше, немного бесполезен для тех, кто хочет провести с ним тест или в качестве примера для начала, поскольку он предполагает наличие некоторых ключей RSA и зашифрованного файла конфигурации config.RSA.

Итак, вот несколько дополнительных строк кода, добавленных для создания ключей RSA и файла конфигурации для воспроизведения.

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

Только шифрование значений

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

введите описание изображения здесь

Вы можете расшифровать файл конфигурации с зашифрованными значениями, используя что-то вроде этого.

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

С каждым элементом конфигурации в отдельной строке (например, Helloи Goodbyeвыше), Git будет лучше распознавать, что происходит в файле, и будет сохранять изменения в элементах информации как различия, а не полные файлы. Git также сможет лучше управлять слияниями, выбором вишни и т.д.

Однако чем больше вы хотите, чтобы управление версиями изменяло конфиденциальную информацию, тем больше вы переходите к решению БЕЗОПАСНОЕ ХРАНИЛИЩЕ (2) и от решения ЗАКРЫТЫЙ ИНФО (3).

Иван
источник
3

Можно использовать Vault, который защищает, хранит и контролирует доступ к токенам, паролям, сертификатам, ключам API и т. Д. Например, Ansible использует Ansible Vault, который имеет дело с паролями или сертификатами, используемыми в книгах воспроизведения.

Эль Русо
источник
Я считаю, что Ansible Vault слишком сложен, по сравнению с простым созданием примера файла конфигурации.
icc97
@ icc97 Да, это правда грустно. Но нам нужно упомянуть эту возможность. На мой взгляд, для задач более сложных, чем хранение нескольких паролей для однопользовательской среды, лучше использовать специализированные решения с самого начала.
Эль Русо
2
Чтобы помочь будущим читателям: Vault и Ansible Vault - совершенно разные несвязанные проекты с похожими названиями
bltavares,
2

Вот техника, которую я использую:

Я создаю папку в моей домашней папке с именем: .config

В эту папку я помещаю файлы конфигурации для любого количества вещей, которые я хочу экстернализировать пароли и ключи.

Я обычно использую обратный синтаксис доменного имени, такой как:

com.example.databaseconfig

Затем в скрипте bash я делаю это:

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

|| exit 1Вызывает сценарий на выход , если он не в состоянии загрузить конфигурационный файл.

Я использовал эту технику для скриптов bash, python и ant.

Я довольно параноик и не думаю, что файл .gitignore достаточно надежен, чтобы предотвратить случайную регистрацию. Кроме того, ничего не отслеживается, поэтому, если регистрация все-таки произошла, никто не узнает, как с ней справиться.

Если конкретное приложение требует более одного файла, я создаю подпапку, а не один файл.

Майкл Поттер
источник
1

Если вы используете рубин на рельсах, драгоценность Figaro очень хорошая, простая и надежная. У него низкий коэффициент головной боли и в производственной среде.

ahnbizcad
источник
4
Можете ли вы рассказать подробнее о том, что делает этот камень? Таким образом, его можно (потенциально) рассматривать как «практику», применимую ко многим языкам.
Mattumotu
medium.com/@MinimalGhost/… имеет обзор, в принципе, кажется, что он справляется с
извлечением информации
0

Доверяй, но проверяй.

В .gitignoreэтом случае исключить «безопасный» каталог из репо:

secure/

Но я разделяю паранойю Майкла Поттера . Итак, чтобы проверить .gitignore, вот модульный тест Python , который вызовет клаксон, если этот «безопасный» каталог когда-нибудь будет проверен. И чтобы проверить проверку, также проверяется допустимый каталог:

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)
Боб Стейн
источник