Яка найкраща практика роботи з паролями в сховищах git?


225

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

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

Відповіді:


256

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

Щоб відфільтрувати існуючий пароль з попередніх комісій, див. Сторінку довідки GitHub на сторінці Видалення конфіденційних даних .


4
До речі, ви можете додати приклад foobar.config до репо, а потім додати foobar.config у файл .ignore. Таким чином приклад foobar.config з’явиться при клонуванні, а ваші фактичні паролі не будуть додані до репо.
Mr_Cimimp

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" може бути цікавою для прочитання.


1
Як запобігти побудові потенційного "небезпечного" коду від читання вмісту змінної вашого середовища?
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 Так це правда сумно. Але нам потрібно згадати таку можливість. На мою думку, для завдань, які складніші, ніж зберігання декількох паролів для однокористувацького середовища, краще використовувати спеціалізовані рішення з самого початку.
El Ruso

2
Щоб допомогти майбутнім читачам: Vault and 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 дуже хороший, простий та надійний. Він має низький фактор головного болю і у виробничих умовах.


4
Чи можете ви дати детальну інформацію про те, що робить цей самоцвіт? Таким чином, його можна (потенційно) розглядати як "практику", застосовну для багатьох мов.
mattumotu

medium.com/@MinimalGhost/… має огляд, в основному, здається, можна керувати перетягуванням матеріалів з конфігураційного файлу
tripleee

0

Довіряйте, але перевіряйте.

У .gitignoreцьому виключається "захищений" каталог з репо:

secure/

Але я поділяюсь параноїєю @ Майкла Поттера . Отже, для перевірки .gitignore, ось тест модуля Python, який би підняв klaxon, якщо цей "захищений" каталог коли-небудь потрапляє.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.