如果开源算法类、工具类、库这样的项目,自然不需要用到数据库;如果开源网站应用,数据库配置信息就不得不包含在代码里面了。
目前Github已经免费开放了私有代码仓库,把代码放在私有代码库里是最安全的,但那样不够开放,无法让更多人看到。
把数据库配置信息明文写在代码里是一件很危险的事情:别人可以远程连接你的服务器的数据库。然而程序员热爱开源、喜欢装逼,代码是不可能严密守护的,应该永远认为代码是可能被别人看到的。
那么如何隐藏数据库配置信息呢?
集中配置
不要让程序的配置信息散落在很多地方,要把整个系统的配置放在一个地方集中管理。比如把配置信息写在config.json里面,我们只需要保护好config.json这一个文件就够了。
使用gitignore
使用gitignore把config.json隐藏掉、不提交到开源代码库。这样做会带来如下问题:
- 别人不了解config.json的内容格式,无法直接运行代码
- 自己删除本地仓库之后,真正的config.json就成了永远的秘密,谁也不知道曾经的config.json长什么样子
因此直接隐藏config.json是不好的,需要进一步补充:把config.json加密之后提交到开源代码库。
提交加密的config-encoded.json,不提交config.json
把config.json加密之后得到config-encoded.json,把config-encoded.json提交到代码库。config-encoded.json文件中字段名不加密,只加密字段的取值,这样文件格式保存完好,别人可以自己修改配置。
如果自己删除了项目的本地仓库,从远程的config-encoded.json可以解密得到config.json。这样就可以很容易复现自己过去的代码了。
在这种加密情景下,随意使用一种对称加密算法(如DES、AES)即可,非对称加密没有意义。
加密就必然要用到密码,密码不能写在程序里,否则跟明文没有区别了。密码应该放在只有自己能看到的地方,如:
- 放在环境变量里
- 放在本地某个文件里
放在环境变量里比较简单易行,密码尽量设置的长一些。
最终实现
以nodejs后端代码为例,简要说明一下代码实现。
最终conf目录下包含如下6个文件:
- config.json主要的配置
{
- gitignore忽略config.json
- config-encoded.json
加密后的config.json
- encode.js
把config.json加密为config-encoded.json
const
- decode.js
把config-encoded.js解密为config.json
var fs = require("fs")
var path = require("path")
var crypto = require("crypto")
var privateKey = process.env["PRIVATEKEY"]
if (!privateKey) throw new Error("lack private key")
var config = JSON.parse(fs.readFileSync(path.join(__dirname, "config-encoded.json")).toString("utf8"))
for (var i in config) {
if (typeof config[i] == "string") {
var decoder = crypto.createDecipher("des",privateKey)
config[i] = decoder.update(config[i], "hex", "utf8") + decoder.final("utf8")
}
}
fs.writeFileSync(path.join(__dirname,"config.json"),JSON.stringify(config))
console.log("解密成功,请查看config.json")
- config.js
在config.json中最好存放简单属性(单层,没有层次化结构),这样便于解析配置。有层次的结构在config.js逻辑代码中实现,这样便于程序其它部分访问配置。
读取config-encoded.json并解密......
module.exports = {
port: conf.PORT,
cnblogFolder: path.normalize(path.join(__dirname, "../cnblog")),
githubAuth: {
clientId: conf.GithubAuthClientId,
clientSecret: conf.GithubAuthClientSecret
},
mysql: {
host: conf["mysql.host"],
user: conf["mysql.user"],
password: conf["mysql.password"],
database: conf["mysql.database"]
}
}
在整个过程中,唯一麻烦的地方在于:开发时,每次修改了config.json,都需要手动运行encode.js生成config-encoded.json。
总结
任何秘密都可以“压缩”成一个字符串。
- 最初秘密是整个源码库,我们把秘密放在一个文件里面
- 把文件加密之后,秘密存放在一个字符串里面
- 字符串可以作为密码存放在自己的脑袋里面,从此这份代码可以随意部署在任意一台机器上(只需要设置一下这台机器的环境变量就可以了)。
密码学领域有一个法则:柯克霍夫斯原则。它的内容是:加密体系的安全性应该植根于密码串本身,而不能依赖算法。为啥要有柯克霍夫斯原则呢?因为算法是有限的,是人写死的,是有逻辑意义的;而密码串是无限的,是随机生成的,是没有意义的。如果一个加密体系的可靠性源于算法,那么算法一旦泄露,真个加密体系就崩溃了。而依靠密码串的加密体系有无数个密码串来保证它的安全性,它所需要做的事情就是频繁更换密码串。