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

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

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

Ответ 1

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

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

Ответ 2

Подход может состоять в том, чтобы установить пароль (или ключ 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" может быть интересно читать.

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

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

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

com.example.databaseconfig

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

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

|| exit 1 || exit 1 приводит к завершению работы сценария, если он не может загрузить файл конфигурации.

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

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

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

Ответ 6

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

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

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

  • .gitignore: хранить на серверах и т.д., но не в Git (слабое решение)
  • Переменные среды: fooobar.com/questions/42820/...
  • Config.example: fooobar.com/questions/42820/...
  • Параметр командной строки: введите пароль вручную при запуске программы

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

Decrypted Config File

Таким образом, вы можете восстановить зашифрованный файл конфигурации, написав всего несколько строк 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');

enter image description here

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

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

Ответ 7

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

Ответ 8

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

В .gitignore это исключило бы "безопасный" каталог из репозитория:

secure/

Но я разделяю паранойю Майкла Поттера. Итак, чтобы проверить .gitignore, здесь unit тест 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)