是否将诸如API密钥之类的秘密信息保留在源代码控制之外的策略?


216

我正在一个网站上工作,该网站将允许用户使用来自Twitter,Google等的OAuth凭据登录。为此,我必须在这些提供商处注册并获得我拥有的超级秘密API密钥用誓言保护身体的各个部位。如果我的钥匙结了,那部分就被拉了。

API密钥必须随我的源一起传送,因为它在运行时用于执行身份验证请求。就我而言,密钥必须存在于应用程序中的配置文件中或代码本身中。当我在一台计算机上进行构建和发布时,这不是问题。但是,当我们将源代码管理混为一谈时,事情就会变得更加复杂。

因为我是个贱人,所以我更喜欢在云或GitHub中使用免费的源代码控制服务,例如TFS。这给我带来了一个小难题:

当我的API密钥在代码中并且我的代码可在公共存储库中使用时,如何保持身体完整?

我可以想到许多方法来解决此问题,但是没有一种方法令人满意。

  • 我可以从代码中删除所有私人信息,并在部署后重新编辑。实施起来会很痛苦(我不会在很多方面详述),并且不是一种选择。
  • 我可以加密它。但是,由于我必须解密它,因此任何有消息来源的人都可以弄清楚该怎么做。无意义。
  • 我可以支付私人来源控制的费用。哈哈j / k花钱了吗?请。
  • 我可以使用语言功能将敏感信息与我的其余源代码隔离开,从而使它不受源代码控制。这就是我现在正在做的事情,但是如果错误地检查机密文件,很容易搞砸。

我真的在寻找一种有保证的方式,以确保我不会与世界共享我的私人信息(除了在手套上),这将通过开发,调试和部署而流畅地运行,并且也非常安全。这是完全不现实的。 那我该怎么办呢?

技术细节:VS2012,C#4.5,源代码管理将是TF服务或GitHub。当前使用分部类将敏感键拆分到一个单独的.cs文件中,该文件不会添加到源代码管理中。我认为GitHub可能具有优势,因为.gitignore可以用于确保不检入部分类文件,但是我之前已经搞砸了。我希望有一个“哦,常见的问题,这就是你的做法”,但我可能不得不满足于“没有那么多的吸引力”,:/


6
您可以确保包含您的API密钥的配置文件不在源代码控制的目录中,这将使它无法首先检入。
David Sergey

22
BitBucket.org有无限的私人存储库。自由。和gitHub仓库导入器(保留历史记录)
Rob van der Veer

4
@Dainius我不信任我的开发人员,因为我认识他们。亲密地。实际上,我至少对自己很亲密……不,我会让那个人撒谎。但是我知道,搞砸是多么容易,要清理所说搞砸的历史将有多么困难。
威尔

15
@Dainius:是的。我查看团队代码中的每个字符。说真的 我别无选择。我不能被蒙住眼睛。至少不可靠。但是我愿意,因为我是我的团队。我是TEAM中的I。有一个开发人员,是我。我就是他 是。如果他做得不好,我就是要解决这个问题的人。我。
2013年

3
为什么首先要尝试将密钥编译到代码中?通常将此类内容放入配置文件中。
多纳研究员

Answers:


127

不要将您的秘密信息放入代码中。将其放入配置文件中,该文件在启动时会被您的代码读取。配置文件不应置于版本控制中,除非它们是“出厂默认设置”,然后它们不应具有任何私有信息。

另请参阅问题版本控制和个人配置文件,以了解如何做到这一点。


8
@RobertHarvey只是不将其放在版本控制上,而是在必要时添加了忽略规则。使用该软件的任何人都必须使用自己的API密钥来构建自己的配置文件。
菲利普

10
因此,当您构建和创建软件的发行版时,如何确定该软件随附配置文件?除非您有一些具有合理默认值的文件,否则通常不合理的期望您的用户经历制作配置文件的过程。
Thomas Owens

4
好吧,工厂默认设置是其中一部分,“安装程序”或“首次运行向导”是另一部分
johannes

6
如果许多用户有自己的安装,那么他们不应该创建和使用自己的API密钥吗?使用同一密钥的多个站点/安装可能不是一个好主意。如果只是一次安装,那么使用配置文件并不麻烦。
Mike Weller

10
@Will,如果由于实现细节的不切实际而不能执行此操作,那么我会说您只是没有适当的部署工具。使用未提交的秘密配置文件进行部署应该是完全轻松的。由于我生活在Ruby生态系统而不是C#中,因此无法为您提供具体建议。但是Ruby人们倾向于使用Capistrano进行自动部署。我确信C#也具有用于自动部署的工具,这将使过程变得容易。
本李

29

您可以将所有私有/受保护的密钥作为系统环境变量。您的配置文件将如下所示:

private.key=#{systemEnvironment['PRIVATE_KEY']}

这就是我们处理这些情况的方式,没有任何代码。与不同的属性文件和配置文件结合使用,效果很好。我们针对不同的环境使用不同的属性文件。在我们的本地开发环境中,我们将开发密钥放在属性文件中以简化本地设置:

private.key=A_DEVELOPMENT_LONG_KEY

如果我可以使其与托管选项一起使用,这将是一个合理的解决方案。不会是环境变量,但也许某些键/值配置对在发布后不会消失……
2013年

在将这些环境变量传送到实时环境之前,如何将它们放入构建服务器中?这样,您就可以准备生产资源/配置文件了。
Ioannis Tzikas

构建服务器是开发机器,这就是为什么我担心此信息可能会意外地检查到源代码管理的原因。
2013年

这样的问题可能是服务器上的任何人都可以读取环境。
JasonG 2014年

用户的envvar仅可由用户或root读取。(但是,古老的Linux和AIX并未这样做)
Neil McGuigan 2014年

27

纯Git方式

  • .gitignore 包含私人数据的文件
  • 使用本地分支,在其中替换TEMPLATEDATA
  • 使用污迹/清洁过滤器,其中(本地)过滤器的脚本执行双向替换TEMPLATE<->DATA

水星的方式

  • 伪代码之上的MQ补丁,替换TEMPLATEDATA(变更集是公共的,补丁是私有的)
  • 带有特殊设计的关键字的关键字扩展名(仅在您的工作目录中扩展)

与SCM无关的方式

  • 在构建/部署过程中替换关键字

嗯... git建议很好,您的不可知论建议给了我一个好主意...我可以使用构建事件将文件引入发布过程,然后再将其删除,从而有助于确保它不会被意外地添加到源控制..

7
不,不,再一次-不!忽略文件对于添加一些非常特定的自定义来构建流程或某些东西很有好处,但是它永远不应该用于存储任何安全数据。即使忽略安全数据,也不要将其存储在回购中。
shabunc

11
@shabunc-RTFM!忽略的文件未存储在回购中
Lazy Badger 2013年

9
@LazyBadger-我很清楚它被忽略了。我也知道,在回购协议中,总是有机会毫无疑问地有人将其添加到回购协议中。一些外部配置路径会更好。
shabunc

4
@shabunc-将配置保留在SCM路径之外的好地方。例如,这就是为什么Postgres允许您通过将密码放入文件中来绕过密码检查。但是他们要求将密码文件放在〜/ .pgpass中-大概这不是很方便检查源代码控制的位置。他们知道,自动化,他们有给你一把枪,但他们努力工作,让你从拍摄自己的脚吧..
史蒂夫米奇利

14

我将机密放入加密的文件中,然后提交。系统启动时会提供密码,或者将密码存储在我不提交的小文件中。Emacs将很乐意管理这些加密文件,这是很好的。例如,emacs初始化文件包括:(加载“ secrets.el.gpg”),该文件可以正常工作-启动编辑器时提示我输入那些罕见的密码。我不担心有人破坏加密。


3
这是一个很好的解决方案-很惊讶您没有更多的赞成票。我与一家处理学生数据的公司合作,该数据在美国受到联邦监管,因此他们在使用凭据和机密时必须格外小心。他们也是一家大公司,因此他们需要使用SCM来获取凭据,以便IT可以在构建工程师后找到/管理它们。您的解决方案正是他们所做的。他们有解密密钥文件,其中包含用于dev / staging / prod / etc的解密密钥(每个文件一个)。然后将所有机密加密并检入文件。解密文件用于在每个环境中获取它们。
Steve Midgley 2013年

7
好吧,从某种意义上说,对秘密(在这种情况下为API密钥)进行加密只会将问题从不提交秘密数据转移到不提交密码短语(现在变成密码)。但是,当然,在系统启动时要求它是一个不错的选择。
siegi 2014年

我喜欢这个解决方案。您提交的加密文件的类型可以是KeePass文件。每个notes字段都有一个条目,使用该字段存储.env文件的内容。几个月前,我写了一个可以读取keepass文件并使用notes条目字段创建.env文件的工具。我正在考虑添加一个功能,以便可以require('switchenv').env()在Node.js程序的顶部执行操作,并根据与NODE_ENV或类似内容匹配的条目创建process.env变量。-> github.com/christiaanwesterbeek/switchenv
Christiaan Westerbeek

14

这是特定于Android / Gradle的,但您可以在的全局gradle.properties文件中定义键user home/.gradle/。这也很有用,因为您可以根据buildType或flavor使用不同的属性,例如,用于开发的API和用于发布的不同属性。

gradle.properties

MY_PRIVATE_API_KEY=12356abcefg

build.gradle

buildTypes {
        debug{
            buildConfigField("String", "GOOGLE_VERIFICATION_API_KEY", "\"" + MY_PRIVATE_API_KEY +"\"")
            minifyEnabled false
            applicationIdSuffix ".debug"
            }
        }

在代码中,您将像这样引用

String myAPI = BuildConfig.GOOGLE_VERIFICATION_API_KEY;

BuildConfig转化为相应的源文件,在您的APK这么简单的逆向工程将揭示你所投入的BuildConfig密钥和秘密
德米特里Livotov

1
的确是有道理的。但是问题是关于如何将api键保持在源代码而非二进制文件之外。
scottyab '16

11

您不打算随应用程序一起分发该密钥或将其存储在源代码存储库中。这个问题问的是如何做到这一点,而这并不是通常要做的。

移动网络应用

对于Android / iPhone,设备应在首次运行时从您自己的Web服务中请求密钥。然后将密钥存储在安全的位置。发布者是否应更改或撤消密钥。您的Web服务可以发布新密钥。

托管的Web应用程序

使用软件许可证的客户在首次配置软件时必须手动输入密钥。您可以给每个人相同的密钥,不同的密钥,也可以让他们拥有自己的密钥。

发布的源代码

您将源代码存储在公共存储库中,而不存储在KEY中。在文件的配置中,在* 添加行* 放置键 *。当开发人员使用您的源代码时,他们将复制该sample.cfg文件并添加自己的密钥。

您不会将config.cfg文件用于开发或生产。


4
这个问题在问如何做到这一点,绝对没有。事实是这些密钥需要由代码使用,因此可以由代码访问,这通常意味着通过代码或配置文件,如果它们不在源代码中,则它们至少在附近,并且可能会意外地终止于资源。不幸的是,托管的Web应用程序是荒谬的。您无需通过您的(假设的)facebook帐户申请api密钥即可登录StackOverflow。 这里关键的地方是一个巨大的过度简单化作为Q.描述了将无法正常工作在dev亡>酒吧环境
请问

与许多其他问题一样,我已经正确回答了这个问题。您没有接受其中之一的事实意味着您不了解如何使用这些键。
Reactgular 2013年

7
那么我们如何保护密钥发布Web服务?使用另一个键?
Jiangge张

同上什么@JianggeZhang说-这是危险的建议
大卫K.赫斯

5

将环境变量用于每个服务器都会更改的秘密内容。

http://en.wikipedia.org/wiki/Environment_variable

如何使用它们取决于语言。


3
对于许多人来说,建议不要通过模糊处理来确保安全。您是否愿意详细说明您的答案?

2
这并不是很模糊,环境变量仅对您添加了它们的用户可用,因此所有凭据都对应用程序运行的用户上下文具有相同的保护。我更新了答案,以包括环境变量的概念。这更清楚吗?
Filipe Giusti

4

我认为这是每个人在某些时候都遇到麻烦的问题。

这是我使用的工作流程,可能对您有用。它使用.gitignore:

  1. 所有配置文件都放在一个特殊文件夹中(带有示例配置文件-可选)
  2. 所有配置文件都包含在.gitignore中,因此它们不会公开
  3. 在私人盒子上设置一个gitolite服务器(或您最喜欢的git服务器)
  4. 在私有服务器中添加包含所有配置文件的仓库
  5. 添加脚本以将配置文件复制到主仓库中的特殊文件夹中(可选)

现在,您可以将配置存储库克隆到任何开发和部署系统。只需运行脚本将文件复制到正确的文件夹即可。

您仍然可以获得所有GitHub糖果,可以与世界共享您的代码,敏感数据永远不在主存储库中,因此它们不会公开。它们仍然只是任何部署系统的一招和副本。

我为私人git服务器使用15美元/年的价格,但您也可以根据Cheapskate的要求在家里设置一个;-)

PS:您也可以使用git子模块(http://git-scm.com/docs/git-submodule),但我总是忘记命令,因为规则又如此肮脏!


2

使用加密,但在启动时提供一个主密钥,作为控制台上的密码,仅在进程的用户可以读取的文件中,或从系统提供的密钥存储区(如Mac OS密钥链或Windows密钥存储区)中提供。

为了连续交付,您需要在某个位置记录各种密钥。应该从代码中区分配置,但是将其保持在版本控制之下是很有意义的。


1

3策略,尚未提及(?)

在检入时或在VCS中进行预检钩

  • 搜索具有高熵的字符串,例如,secret-secrets
  • regex搜索众所周知的API密钥模式。AWS的AKIA *密钥就是一个示例,git-secrets是基于此的一种工具。同样,变量名称(如“ password”)具有不变的赋值。
  • 搜索已知机密-您知道自己的机密,然后在其中搜索文字。还是使用工具,我写了这个概念证明

已经提到的策略

  • 存储在源树之外的文件中
  • 在源代码树中有它,但是告诉VCS忽略它
  • 环境变量是在源树之外存储数据的一种变体
  • 只是不要将宝贵的秘密告诉开发人员

0

将私人信息置于您的源代码管理之外。创建一个未加载的默认分发版本,并使您的VCS忽略真正的默认版本。您的安装过程(无论是手动,配置/构建还是向导)应处理创建和填充新文件的过程。(可选)修改文件的权限,以确保只有所需的用户(Web服务器?)才能读取它。

好处:

  • 不假设开发实体==生产实体
  • 不假定所有协作者/代码审阅者都是受信任的
  • 通过将其排除在版本控制之外来防止简单的错误
  • 通过QA /内部版本的自定义配置,易于自动化安装

如果您已经在执行此操作,并且不小心将其签入,请将其添加到项目的中.gitignore。这将使其无法再次执行。

这里很多免费的Git主持在提供私人仓库的。尽管您永远不应该对凭据进行版本控制,但是您可以便宜一些,也可以拥有私有存储库。^ _ ^


-2

除了将OAuth密钥存储为原始数据之外,为什么不通过某种加密算法运行字符串并将其存储为盐腌哈希呢?然后使用配置文件在运行时将其还原。这样,密钥就不会存储在任何地方,无论是存储在开发箱中还是服务器本身。

您甚至可以创建一个API,以便您的服务器根据每个请求自动生成一个新的加盐和哈希API密钥,这样,即使您的团队也无法看到OAuth源。

编辑:也许尝试斯坦福Javascript加密库,它允许一些相当安全的对称加密/解密。


1
哈希通常是争夺的一种方式。但是,可以按照您的建议执行对称加密算法。

3
杜德(Dude),您不能(轻松)解密哈希。这就是哈希的全部要点。这是为了让我使用其他人的API,他们在其中为我分配一个秘密密钥。我对它的哈希确保(除非我每次选择一个较差的算法并将其破解),否则我将无法使用其API。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.