下 做好依赖管理的十五条准则( 三 )


Go 开发者社区有一句谚语:“小的复制比小的依赖更好” 。
4、依赖的更新策略
关于软件的传统观点是「如果没有问题,就不要动它」 。因为,依赖包的升级可能会引入新的 bug ,而且升级也不像增加新功能那样带来多少好处,何苦要冒这个险呢?
但上述观点忽略了两种成本,它们分别是:
升级的成本 。最终还是要升级,因此而带来的成本(如 Log4j 的漏洞) 。在软件中,修改代码的难度并不是线性递增的 。10 次小改动比 1 次大改动更少工作量,也更不容易出问题 。
问题跟踪的成本 。很难发现之前新版本中已被修复过的 bug 。尤其是在与安全相关的环境中,外面已知的漏洞都会被人利用,你每天都有可能被攻击者入侵 。
例如,(一家跨国征信公司)的高管在 2017 年国会证词中的陈述 。
当年 3 月 7 日,爆出一个新漏洞,并发布了修复版本 。
3 月 8 日, 收到 US-CERT 应更新的通知 。
3 月 9 日、3 月 15 日, 分别进行了代码和网络扫描,并未发现有涉及漏洞的外网服务器 。
5 月 13 日,攻击者发现了( 的安全团队未发现)依然有存在漏洞的服务器 。攻击者利用漏洞入侵了的网络,并在接下来的两个月内盗取了约 1.48亿人的详细个人信息和财务信息 。
公司最终在 7 月 29 日发现被入侵,
并在 9 月 4 日进行了公开说明 。
同年 9 月, 的 CEO、CIO、CSO 已全部辞职,并且国会开始介入调查 。
的经验告诉我们,虽然依赖包管理器平台知道构建代码时所使用的版本,但你还是需要另外的工作来跟踪线上部署过程的信息 。
对于 Go 语言,我们正尝试在每个二进制文件中自动包含版本清单,以便在部署过程中可找到所需升级的依赖项 。Go 还可以在运行时提供这些信息,这样服务器就可以通过查询依赖库中的已知 bug ,并在需要升级时自动向监控服务发送报告 。
及时升级依赖固然重要,但升级就意味着向项目添加新代码,这时,我们仍旧需要去评估新版本依赖中可能带来的风险 。
至少,你应稍看一下版本间的代码差异,或看一下版本发布说明,来确定关键位置的升级代码 。
如果实在有太多的差异代码,导致难以通过差异信息来发现问题,那么,你应把这个问题也作为一个升级风险来对待 。
并且,版本升级不应完全自动化 。
在升级前,你应预先验证新版本是否能在你的环境运行 。
安全相关的关键升级窗口期特别短 。
如果你的升级过程包括运行以前所写的整合测试用例与合规测试用例,那你已有能力在上线前预先发现问题了 。在这种情况下,你升级越快,风险越低 。
在公司的入侵事件发生后,法院的安全团队发现证据表明攻击者(可能是不同的攻击者)在漏洞爆出后仅第 3 天(即 3 月 10 日)就已经入侵了的服务器,但他们当时只运行了一个命令 。
5、对依赖的监控
即使你已经做到上述所说的,工作仍旧没有完成 。
目前大部分依赖包管理器可以轻松做到(甚至自动地记录)某个版本代码的加密哈希值 。在其他电脑或测试环境中重新下载依赖包后,可验证哈希值是否一致 。
这样可以保证你的代码构建是基于你曾检查过、测试过的、完全相同的依赖代码以从而避免类似event-那样的攻击(偷偷地在已发布的版本 3.3.5 中插入了恶意代码,因为没有做哈希校验) 。若有哈希校验,则攻击者必须创建一个新的 3.3.6 版本,并等人们升级(且是没留意是否有修改的前提下)才能攻击成功 。
另外,还需注意是否有新的间接依赖被加入进来了 。版本升级也很容易在你升级目前的依赖时引入新的间接依赖 。