我有一个小的Bash脚本,用于访问twitter并在某些情况下弹出Growl通知。什么是处理脚本存储密码的最佳方法?
我想将此脚本提交给git repo并在GitHub上提供,但我想知道在执行此操作时保持登录/密码私密性的最佳方法是什么。目前,密码存储在脚本本身中。我无法在推送之前将其删除,因为所有旧提交都将包含密码。没有密码的开发不是一种选择。我想我应该将密码存储在外部配置文件中,但我想我会在尝试将某些内容放在一起之前检查是否有一种既定的方法来处理它。
答案 0 :(得分:223)
执行此操作的典型方法是从配置文件中读取密码信息。如果您的配置文件名为foobar.config
,那么您将向存储库提交一个名为foobar.config.example
的文件,其中包含示例数据。要运行程序,您可以使用真实的密码数据创建名为foobar.config
的本地(未跟踪)文件。
要过滤掉之前提交的现有密码,请参阅Removing sensitive data上的GitHub帮助页面。
答案 1 :(得分:23)
方法可以是使用环境变量设置密码(或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:这篇标题为"How to securely store API keys"的dev.to
文章可能会让您感兴趣。
答案 2 :(得分:16)
Greg said是什么,但我想补充一点,检查文件foobar.config-TEMPLATE
是个好主意。
它应包含示例名称,密码或其他配置信息。然后很明显真正的foobar.config应该包含什么,而不必查看foobar.config
中必须存在哪些值的所有代码以及它们应该具有的格式。
配置值通常不明显,例如数据库连接字符串和类似的东西。
答案 3 :(得分:2)
可以使用Vault来保护,存储和控制对令牌,密码,证书,API密钥等的访问权限。例如,Ansible使用处理密码的Ansible Vault或在剧本中使用的证书
答案 4 :(得分:1)
这是我使用的一种技术:
我在主文件夹中创建一个名为:
.config
在该文件夹中,我放置了配置文件,以容纳要外部化密码和密钥的任何数量的内容。
我通常使用反向域名语法,例如:
com.example.databaseconfig
然后在bash脚本中执行以下操作:
#!/bin/bash
source $HOME/.config/com.example.databaseconfig ||exit 1
如果|| exit 1
无法加载配置文件,则会导致脚本退出。
我在bash,python和ant脚本中使用了该技术。
我非常偏执,不认为.gitignore文件足够健壮,可以防止无意中签入。另外,没有任何东西可以监视它,因此,如果签到确实发生了,那么没人会找到解决方法。
如果特定的应用程序需要多个文件,则我创建子文件夹而不是单个文件。
答案 5 :(得分:1)
根据您的确切问题,以不同的方式处理存储库中的密码。
在某些回复中介绍了避免这样做的方法-.gitignore,config.example等
即被允许知道密码的人。 chmod
和用户组浮现在脑海;还存在一些问题,例如如果您在外部托管存储库或服务器,是否应该允许Github或AWS员工看到东西?
如果要将包含敏感信息(例如密码)的配置文件存储在公共位置,则需要对其进行加密。从存储库中恢复文件后,可以对其进行解密,甚至可以从其加密形式直接使用这些文件。
使用加密的配置数据的示例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也将能够更好地管理合并和樱桃精选等。
但是,如果您想对敏感信息进行版本控制的更改越多,您就越倾向于SAFE REPOSITORY解决方案(2)而远离ENCRYPTED INFO(3)解决方案。
答案 6 :(得分:0)
如果你在铁轨上使用红宝石,那么费加罗宝石非常好,简单,可靠。它对生产环境的影响也很小。
答案 7 :(得分:0)
信任,但请验证。
在.gitignore
中,这将从存储库中排除“安全”目录:
secure/
但是我分享@ Michael Potter的偏执狂。因此,要验证.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)