Thực hành tốt nhất để xử lý mật khẩu trong kho git là gì?


225

Tôi đã có một đoạn script Bash nhỏ mà tôi sử dụng để truy cập twitter và bật lên thông báo Growl trong một số tình huống. Cách tốt nhất để xử lý lưu trữ mật khẩu của tôi với tập lệnh là gì?

Tôi muốn cam kết tập lệnh này với repo git và cung cấp nó trên GitHub, nhưng tôi tự hỏi cách tốt nhất để giữ thông tin đăng nhập / mật khẩu của tôi trong khi thực hiện việc này là gì. Hiện tại, mật khẩu được lưu trữ trong chính tập lệnh. Tôi không thể xóa nó ngay trước khi tôi đẩy vì tất cả các xác nhận cũ sẽ chứa mật khẩu. Phát triển mà không có mật khẩu không phải là một lựa chọn. Tôi tưởng tượng rằng tôi nên lưu trữ mật khẩu trong một tệp cấu hình bên ngoài, nhưng tôi nghĩ tôi sẽ kiểm tra xem liệu có cách nào được thiết lập để xử lý việc này trước khi tôi thử và đặt một cái gì đó lại với nhau không.

Câu trả lời:


256

Cách điển hình để làm điều này là đọc thông tin mật khẩu từ một tệp cấu hình. Nếu tệp cấu hình của bạn được gọi foobar.config, thì bạn sẽ cam kết một tệp được gọi foobar.config.exampleđến kho lưu trữ, chứa dữ liệu mẫu. Để chạy chương trình của bạn, bạn sẽ tạo một tệp cục bộ (không được theo dõi) được gọi foobar.configbằng dữ liệu mật khẩu thực của bạn .

Để lọc mật khẩu hiện tại của bạn khỏi các cam kết trước đó, hãy xem trang trợ giúp GitHub về Xóa dữ liệu nhạy cảm .


4
Btw, bạn có thể thêm một ví dụ foobar.config vào repo và sau đó thêm foobar.config vào tệp .ignore. Bằng cách này, ví dụ foobar.config sẽ xuất hiện khi được nhân bản và mật khẩu thực tế của bạn sẽ không được thêm vào repo.
Mr_Chimp

16
@Mr_Chimp: .gitignoreTệp không áp dụng cho các tệp được theo dõi đã có trong kho lưu trữ. Ví dụ: git add -usẽ thêm một tệp đã thay đổi ngay cả khi nó đã ở trong đó .gitignore.
Greg Hewgill

1
Ngoài ra, đây là một liên kết thú vị trong trường hợp bạn vô tình thêm tệp cấu hình và bạn muốn xóa nó khỏi lịch sử git: help.github.com/articles/remove-sensitive-data
Loïc Lopes

16
Làm thế nào bạn sẽ đi về việc chia sẻ những mật khẩu với nhóm của bạn? Một điều là có một bản sao cục bộ (không cam kết với repo), điều khác là chia sẻ nó với một nhóm lớn hơn ngay cả với các công cụ tự động (để triển khai, v.v.)
blueFast

2
Tôi có câu hỏi tương tự như @dangonfast. Điều này dường như không thực tế đối với một đội ngũ lớn.
Jacob Stamm

25

Một cách tiếp cận có thể là đặt mật khẩu (hoặc khóa API) bằng biến môi trường. Vì vậy, mật khẩu này nằm ngoài sự kiểm soát sửa đổi.

Với Bash, bạn có thể đặt biến môi trường bằng cách sử dụng

export your_env_variable='your_password'

Cách tiếp cận này có thể được sử dụng với các dịch vụ tích hợp liên tục như Travis , mã của bạn (không có mật khẩu) được lưu trữ trong kho GitHub có thể được Travis thực thi (với mật khẩu của bạn được đặt bằng biến môi trường).

Với Bash, bạn có thể nhận giá trị của biến môi trường bằng cách sử dụng:

echo "$your_env_variable"

Với Python, bạn có thể nhận giá trị của biến môi trường bằng cách sử dụng:

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

PS: lưu ý rằng có lẽ nó hơi rủi ro (nhưng đó là một thực tế khá phổ biến) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-steals-en môi-biến /

PS2: dev.tobài viết này có tiêu đề "Cách lưu trữ khóa API an toàn" có thể thú vị để đọc.


1
Làm cách nào để ngăn chặn việc xây dựng mã "không an toàn" tiềm năng khi đọc nội dung của biến môi trường của bạn?
gorootde


16

Những gì Greg nói nhưng tôi nói thêm rằng nên kiểm tra một tập tin foobar.config-TEMPLATE.

Nó nên chứa tên ví dụ, mật khẩu hoặc thông tin cấu hình khác. Sau đó, rất rõ ràng những gì foobar.config thực sự nên chứa, mà không cần phải tìm trong tất cả các mã cho các giá trị phải có trong foobar.configvà định dạng mà chúng nên có.

Thông thường giá trị cấu hình có thể không rõ ràng, như chuỗi kết nối cơ sở dữ liệu và những thứ tương tự.


7

Xử lý mật khẩu trong kho sẽ được xử lý theo nhiều cách khác nhau tùy thuộc vào vấn đề chính xác của bạn là gì.

1. Đừng làm điều đó.

Và các cách để tránh làm được đề cập trong một số câu trả lời - .gitignore, config.example, v.v.

hoặc 2. Làm cho kho lưu trữ chỉ có thể truy cập đối với người được ủy quyền

Tức là những người được phép biết mật khẩu. chmodvà các nhóm người dùng đến với tâm trí; còn các vấn đề như nhân viên Github hoặc AWS có được phép xem mọi thứ nếu bạn lưu trữ bên ngoài kho hoặc máy chủ của mình không?

hoặc 3. Mã hóa dữ liệu nhạy cảm (mục đích của phản hồi này)

Nếu bạn muốn lưu trữ các tệp cấu hình của mình có chứa thông tin nhạy cảm (như mật khẩu) ở một vị trí công cộng thì nó cần phải được mã hóa. Các tập tin có thể được giải mã khi được phục hồi từ kho lưu trữ, hoặc thậm chí được sử dụng trực tiếp từ dạng được mã hóa của chúng.

Một ví dụ giải pháp javascript để sử dụng dữ liệu cấu hình được mã hóa được hiển thị bên dưới.

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

Tập tin cấu hình được giải mã

Vì vậy, bạn có thể khôi phục tệp cấu hình được mã hóa chỉ bằng một vài dòng Javascript.

Lưu ý rằng việc đặt một tệp config.RSAvào kho lưu trữ git sẽ khiến nó trở thành một tệp nhị phân một cách hiệu quả và do đó nó sẽ mất nhiều lợi ích của một cái gì đó như Git, ví dụ như khả năng chọn cherry thay đổi cho nó.

Giải pháp cho vấn đề đó có thể là mã hóa các cặp giá trị khóa hoặc có lẽ chỉ là các giá trị. Bạn có thể mã hóa tất cả các giá trị, ví dụ nếu bạn có một tệp riêng cho thông tin nhạy cảm hoặc chỉ mã hóa các giá trị nhạy cảm nếu bạn có tất cả các giá trị trong một tệp. (xem bên dưới)

Ví dụ của tôi ở trên là một chút vô dụng đối với bất kỳ ai muốn thực hiện thử nghiệm với nó hoặc làm ví dụ để bắt đầu vì nó giả sử sự tồn tại của một số khóa RSA và tệp cấu hình được mã hóa config.RSA.

Vì vậy, đây là một số dòng mã bổ sung được thêm vào để tạo khóa RSA và tệp cấu hình để chơi.

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

Chỉ mã hóa giá trị

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

nhập mô tả hình ảnh ở đây

Bạn có thể giải mã một tập tin cấu hình với các giá trị được mã hóa bằng cách sử dụng cái gì đó như thế này.

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

Với mỗi mục cấu hình trên một dòng riêng biệt (ví dụ HelloGoodbyeở trên), Git sẽ nhận ra tốt hơn những gì đang diễn ra trong một tệp và sẽ lưu trữ các thay đổi đối với các mục thông tin là khác biệt thay vì các tệp hoàn chỉnh. Git cũng sẽ có thể quản lý sáp nhập và chọn cherry tốt hơn.

Tuy nhiên, bạn càng muốn kiểm soát phiên bản thay đổi thành thông tin nhạy cảm, bạn càng hướng tới giải pháp SAFE REPOSITORY (2) và tránh xa giải pháp ENCRYPTED INFO (3).


3

Người ta có thể sử dụng Vault để bảo mật, lưu trữ và kiểm soát quyền truy cập vào mã thông báo, mật khẩu, chứng chỉ, khóa API, v.v. Ví dụ: Ansible sử dụng Ansible Vault xử lý mật khẩu hoặc chứng chỉ được sử dụng trong Playbook


Tôi thấy Ansible Vault quá phức tạp chắc chắn so với việc chỉ tạo một tệp cấu hình ví dụ.
icc97

@ icc97 Có thật là buồn. Nhưng chúng ta cần đề cập đến khả năng này. Theo tôi, đối với các tác vụ phức tạp hơn thì lưu trữ một vài mật khẩu cho môi trường người dùng đơn lẻ là tốt hơn để sử dụng các giải pháp chuyên dụng ngay từ đầu.
El Ruso

2
Để giúp độc giả tương lai: Vault và Ansible Vault là những dự án không liên quan đến nhau có cùng tên
bltavares

2

Đây là một kỹ thuật tôi sử dụng:

Tôi tạo một thư mục trong thư mục nhà của tôi được gọi là: .config

Trong thư mục đó, tôi đặt các tập tin cấu hình cho bất kỳ số lượng nào mà tôi muốn ngoại lệ hóa mật khẩu và khóa.

Tôi thường sử dụng cú pháp tên miền ngược như:

com.example.databaseconfig

Sau đó, trong tập lệnh bash tôi làm điều này:

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

Các || exit 1nguyên nhân kịch bản để thoát nếu nó không phải là khả năng tải các tập tin cấu hình.

Tôi đã sử dụng kỹ thuật đó cho các tập lệnh bash, python và ant.

Tôi khá hoang tưởng và không nghĩ rằng một tập tin .gitignore đủ mạnh để ngăn chặn việc đăng ký vô ý. Thêm vào đó, không có gì giám sát nó, vì vậy nếu việc đăng ký đã xảy ra, không ai sẽ tìm ra cách đối phó với nó.

Nếu một ứng dụng cụ thể yêu cầu nhiều hơn một tệp, tôi tạo thư mục con thay vì một tệp.


1

Nếu bạn đang sử dụng ruby ​​trên đường ray, đá quý Figaro rất tốt, dễ dàng và đáng tin cậy. Nó có một yếu tố đau đầu thấp với môi trường sản xuất quá.


4
Bạn có thể cho một số chi tiết về những gì đá quý đó không? Bằng cách đó, nó có thể (có khả năng) được coi là một 'thông lệ' áp dụng trên nhiều ngôn ngữ.
mattumotu

Medium.com/@MinimalGhost/ Có một cái nhìn tổng quan, về cơ bản nó dường như quản lý việc lấy nội dung từ một tệp cấu hình
tripleee

0

Tin tưởng nhưng xác minh.

Trong trường hợp .gitignorenày sẽ loại trừ một thư mục "an toàn" khỏi repo:

secure/

Nhưng tôi chia sẻ sự hoang tưởng của Michael Potter . Vì vậy, để xác minh .gitignore, đây là một bài kiểm tra đơn vị Python sẽ tăng klaxon nếu thư mục "an toàn" này được kiểm tra. Và để kiểm tra kiểm tra, một thư mục hợp pháp cũng được kiểm tra:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.