软件开发安全之道:概念、设计与实施
上QQ阅读APP看书,第一时间看更新

1.2 信任

信任在数字世界也同样重要,但是在数字世界中,人们常常认为信任是理所当然的。软件安全从根本上都要依赖于信任,因为没有人可以控制一个系统的所有组件,没有人可以自己编写所有的软件,也没有人可以对自己合作的所有供应商进行审查。如今的数字系统全都非常复杂,复杂到哪怕是全球顶尖科技“巨头”也不可能从零开始搭建一个完整的科技栈,从硅材料到操作系统、网络、外设,再到把它们结合起来,让它们形成各司其职的软件体系。我们日常使用的系统无不是大规模、复杂的卓绝技术成就。因为没有人可以自己从头搭建所有这些系统,所以企业都会根据功能或者价格来选择自己的软硬件产品——这里值得留意的是,所有选择本质上都是建立在信任基础上的决策。

安全性要求我们认真地分析信任关系——哪怕没人有时间、有资源来对所有资源进行彻底的调查和验证。如果无法做到充分信任,就意味着企业必须完成大量不必要的工作去保护一个很可能不会面临任何实质威胁的系统。反之,如果无条件地信任,未来则有可能会措手不及。说白了,如果你完全信任一个实体,它们也就基本上不需要为失败承担任何后果了。违背信任有下面两种完全不同的形式:恶意行为(如欺骗、谎言、诡计等)和失职行为(如错误、误解、疏忽等)。

在信息缺失的情况下做出重要决策,是人们最需要信任关系的场景。不过,我们与生俱来的信任感依赖的是微妙的感官信号,而这种本能不适用于数字世界。在下文中我们会首先探讨“信任感”这个概念,剖析我们日常产生的信任感到底为何物,然后把信任感的概念推广到软件领域。随着阅读的深入,读者应该尝试把自己对软件的看法和自己建立信任感的本能联系起来。利用好自己的信任本能是一种强有力的手段。久而久之,我们就可以对软件安全运用类似的信任本能,这比多少技术分析都更加有效。

1.2.1 信任感

理解信任感的最好方法就是在我们依靠信任来做出判断的时候,仔细品味那种感受。读者可以进行一个思想实验,也可以找一位自己能够绝对信任的人在现实生活中进行尝试。想象和一位朋友走过一条繁华的大街,前面不远处就是熙熙攘攘的车流,街道中有一条人行横道,告诉这位朋友你想让他/她引导你走过这条人行横道。在这个过程中,你完全依靠这位朋友来安全地走过这条人行横道,你会闭上双眼,绝对按照他/她的指示行动。你们两人手拉手走过这条人行横道,你任凭这位朋友帮你转过身,让你面对这条人行横道;在前方出现危险时挡住你,不让你继续前进。你听到耳边汽车呼啸而过,知道自己的朋友在等待安全的时刻才会让你继续前行——这一刻你的朋友已经化身为你的保镖。这时,你的心跳可能会明显加速,你可能会警惕地倾听着身边一切预示着有可能出现危险情况的声音。

现在,这位朋友准确无误地指引你前行。如果你打算就这样闭着眼走在马路上,你的这种感受就是真正的信任——当然也有可能还达不到完全的信任。你的意识可以敏锐地察觉到显而易见的危险,你的感官高度紧张从而快速确认周遭的安全,你的内心深处则有个声音在不断警告你不要再走了。你与生俱来的内部安全监控系统无法收集足够的信息,因此希望你先把眼睛睁开再继续前行。如果这位朋友判断出现了失误怎么办?如果这位朋友口蜜腹剑,想要置你于死地又当如何?归根结底,是你对这位朋友的信任让你忽略这些本能,走过面前的人行横道。

我们应该提升自己对数字信任方面的决策的认识,这样才能帮助别人看清这些决策给安全带来的影响。在理想情况下,当我们给一项重要的服务选择组件或者厂商时,在刚才那种练习中我们用来指导自己做出信任决策的直觉同样能够发挥作用。

1.2.2 比特不是肉眼可见的

上述讨论是为了强调一点,当我们自以为“眼睁睁地看着这些数据”的时候,我们其实看到的只是一种与数据本身距离十万八千里的数据展示方式。其实,我们看到的是屏幕上的一系列像素点,虽然我们认为这些像素点展示了数据,但是我们对这些数据的物理保存位置并不知情,这些数据很可能经历了数百万条命令才被转换为人类可以读懂的信息,并且最终显示在我们的显示器上。数字科技让信任这件事变得相当棘手,因为它如此抽象、迅捷,看不见、摸不着。当我们浏览数据时,一定要切记内存中的数据与我们阅读数据时看到的那些像素点之间隔着大量的软硬件操作。我们又怎么知道这里面有没有哪个环节恶意歪曲了数据的含义呢?数字信息的基本事实是极难直接进行分辨的。

网页浏览器上显示的锁形图标表示浏览器和网页站点之间建立的是安全的连接。这些图标出现与否,只是为了向用户表明一点,即连接是否安全。但是在这个图标的背后,包含一系列数据和大量的计算(这些内容我们会在第11章详细介绍),这些最终都被包含到一个二进制的“是/否”提示符中。哪怕是专家级别的开发人员,让他们手动确认一个实例的可靠性也是一个相当艰巨的任务。我们能做到的只有信任这些软件,我们当然也有理由信任这些软件。这里的关键在于,我们必须弄清楚这种信任的深度和广度,而不是认为所有的信任都是理所当然的。

1.2.3 能力与不足

大多数攻击始于软件的缺陷或者误配,这些问题可能都是那些诚信有加、心存善念的程序员和IT人员制造出来的——毕竟,是人就会犯错。因为软件使用许可都会包含免责声明,所以一切软件都是在用户知悉和认可风险的前提下使用的。如果诚如人们所言,“所有软件都有bug”,那么其中总有一些bug可以被利用,攻击者也总能找到一些bug并且加以利用。软件专家一般很少因为错信了恶意软件,而成为攻击者的目标。

好消息是,我们并不难判断哪些操作系统、编程语言比较可靠。大型企业在提供和支持高质量软硬件时,都有可供追溯的历史,因此信任这些企业就在情理之中。而对于那些难以追溯历史的软件供应商来说,信任它们则存在一定的风险。它们固然也有一些技术高超、动力十足的员工为之努力工作,但这个行业本身缺乏透明性,因此人们很难判断它们的产品是否安全。开源提供了这种透明性,但是开源软件的安全水平取决于项目甲方对开发者的监管是否严格到足以防止开发者在软件中有意无意地插入恶意代码。显然,没有一家软件公司会承诺在发生攻击事件时提供更高级别的安全性或者向用户提供赔偿,以此彰显自己在业内的特殊地位。因此,作为客户,我们也没有这样的安全选项。无论法律、规范还是商业协议,都提供了一些额外的方法来减轻人们执行信任决策时面临的不确定性。

信任决策的重要性固然不可小觑,但是也没人能够永远做出正确的决策。实际情况是,信任决策从来都是不完美的。这就像美国证券交易委员会警告人们的那句话一样:“过去的表现无法担保未来的结果。”好在人们已经学会了如何权衡信任感(虽然人们更擅长面对面,而不是通过数字媒体来判断谁更值得信任),而且在绝大多数情况下,我们的信任决策都是正确的——只要我们信息准确,目标清晰。

1.2.4 信任是一个频谱

信任永远是分不同程度的,在对信任的评估过程中也总是包含一定的不确定性。信任是一个频谱,在这个频谱的一端,比如在进行一场大手术的过程中,我们就是把自己的性命托付给了那些医疗从业者,我们不仅放弃了对自己身体的控制权,也放弃了监控手术操作的任何意识和能力。在最糟糕的情况下,一旦手术失败,我们其实也就没有了任何追诉的机会(关于遗产的合法权利除外)。日常的信任程度就会小得多:信用卡额度的上限是银行没有收到欠款也能承受的损失;汽车则有代客钥匙,我们可以用它限制人们打开后备箱。

鉴于信任是一个频谱,“信任但仍要验证”的策略就是一个强大的工具,可以让我们在绝对信任与绝对不信任之间建立起一座桥梁。在软件领域,我们可以通过授权和审计来达到这样的效果。一般来说,审计包括自动审计(准确地校验大量重复的活动日志)与手动审计(选择性校验,以处理那些比较罕见的情况,它把人工核查作为最终决策的依据)。本章后文还会对审计进行进一步的探讨。

1.2.5 信任决策

在软件领域,人们有两种选择:信任或不信任。有些系统对应用设置了各式各样的许可,人们需要手动来允许或者禁止它们。如果存在疑问,我们大可以选择不信任——只要我们有理由信任另一个候选的解决方案。如果你在评估的时候标准过高,导致没有任何一种产品可以获得你的信任,那么你就只能大费周章、自己动手开发组件了。

做出信任决策的过程就像给一棵“信任树”剪枝,这棵树如果不加修剪就会长出无穷的枝杈。如果我们相信某项服务或者某台计算机是安全的,我们就不需要花费大量精力进行深入分析了。如果我们无法做到信任,那么我们就需要对系统的更多组成部分(包括很多微组件)加以保护。图1-1演示了做出信任决策的过程。如果没有完全信任的云服务来保存我们的数据,那么我们就必须自己运维服务器,这又需要我们继续加以判断:到底是使用一项可靠的托管服务还是自己搭建Web服务器,到底是使用我们可以信任的现成数据库软件还是自己编写一个数据库软件?不难发现,如果我们不信任供应商,我们就需要继续做出信任决策,毕竟我们不可能完全靠自力更生。

对于那些显然不能信任的输入信息(尤其是从公共互联网或者客户端发来的输入信息),我们当然应该表示怀疑,怎么谨慎都不为过(详见本书第4章)。即使在处理那些可以信任的输入信息时,我们仍然不应以绝对可靠视之。如果我们只是希望降低整个系统的脆弱性,防止因软件问题导致错误传播,则可以在条件允许的情况下,尽可能增加一些安全校验。

图1-1 演示做出信任决策过程的决策树

1.隐式信任组件

每个软件项目都会依赖大量存在隐式信任(implicitly trusted)的技术,包括硬件、操作系统、开发工具、库和其他很难核查可靠性的技术,所以我们只能根据供应商的声誉选择相信这些工具。不过,读者还是应该对隐式信任的概念有所了解,并且给予适当的重视——尤其是准备大幅度扩展隐式信任的范围之前。

管理隐式信任没有简便方法,不过下面这种思路也许可以给读者带来启迪:把你认定为可靠的对象数量降至最低。如果你现在已经选择了使用微软或苹果等公司的操作系统,就继续使用它们提供的编译器、库、应用以及其他产品和服务,这样可以把信息暴露的风险降到最低。你可以这样理解个中逻辑,即每多信任一家公司,就给了这家公司一次让你失望的机会。另外,从实用的角度来看,同一家公司产品线中的产品往往兼容性更好,这些产品之间的互操作也经历过更多的测试。

2.值得信赖

最后,也不要忘了从另一个角度来思考信任决策,那就是在我们提供产品和服务时也需要提升客户的信任感。每个软件产品都必须让终端用户认为这个产品是值得信赖的。在大部分情况下,我们只需要展现出自己的专业性就可以了,但是如果这项产品提供的功能相当重要,就必须让客户有坚实的理由信任我们的产品。

下面是在工作中提升信任感的几种基本方式。

保持透明可以提升信任感。公开自己的工作可以让客户评估产品的安全性。

让第三方参与,利用第三方的独立性来提升信任感(比如,可以聘请独立的审计人员)。

有时,我们的产品就是需要和其他产品进行集成的第三方产品,因为独立交易的双方很难相互勾结,所以这项产品也可以提升客户的信任感。

在出现问题的情况下,要主动接受客户的反馈,然后果断采取行动,并且公开披露调查的结果,以及提出防止问题再次发生的措施。

有些特性或者设计要素可以把信任感具象化,让客户可以亲眼看到——比如,通过一种存档解决方案来实时显示在不同位置保存了多少份备份。

行动可以提升信任感,空洞的口号则会让那些精明的客户产生怀疑。我们可以提供一些有形的证据,最好是可以让客户自己进行验证的证据。虽然真有能力审查开源代码质量的人寥寥可数,但是开放代码给人们审查(让他们知道总有懂得如何审查代码的人会去审查这些代码)本身也能够提升信任感。