在写这篇文章的时候,系统管理员们正忙于确保自己的网络足以应对 CVE-2014-6271,也就是所谓的 “Shellshock” 漏洞。攻击报告表明通过漏洞可以获得 bash
shell 的权限,从而使攻击者可以控制其运行某些功能。而 bash shell 广泛存在在大多数 Linux 以及 OS X 甚至是 iOS 系统中(虽然大部分人都不太可能使用它)。实际上,人们从上世纪 60 年代开始设计相容分时系统(CTTS)时就已经开始考虑安全性了,但为何到现在这个问题都没有解决?为什么软件系统仍有安全问题?又是为什么我们应用开发者被告诫必须在安全上有所行动?
CTSS 所面临的最大挑战是允许多用户访问同一台计算机而互不干扰,这也影响了之后系统的架构设计,其中也包括衍生了 OS X 和 iOS 的 UNIX 系统。解决的办法是,每个用户在一个仿佛是真实电脑的环境中独立运行任务,但实际上却使用的是共享计算机生成的一个沙箱。
当一个用户需要大量资源,这一解决方案应避免对其他用户的资源产生不利影响。而这个方案也导致了拥有配额资源的多用户账户以及账户管理系统的出现。这种想法颇为有效:在 2006 年,我使用了这个系统,让超过 1000 名用户访问了共享的 OS X 电脑。
不幸的是,当初对这个问题的看法并不完整。那些防止某个账号去使用分配给其他账号的资源的技术手段,往往并不能防止某个用户使用分配给其他用户的资源。如果一个用户可以获得其他账户的权限,或者能说服其他用户运行自己的程序的话,那账户和权限系统将可以被规避。这也是 Shellshock 漏洞所暴露的问题的根源:攻击者可以获得受害者的权限和资源来运行自己的程序。
从 UNIX 设计的时代开始,计算机就开始变得更小、更快和更容易连接。越来越多的情况是当初软件创建时所没有预见到的。当电子邮件的所有用户都在同一所大学,且所有终端都归大学所有时,作为一个有效且公开的纯文本系统,它能良好的运行。但是当它要支持来自不同组织、不同地点甚至不同网络的人进行沟通时,就需要不一样的解决方案了。
在某种程度上,iOS 仍然算是一个多用户系统。与 UNIX 设计的环境不同,iOS 所有用户访问的账号和手机持有者所操作的账号是同一个。所有这些用户都是手机账号的持有者,他们包括:你、我、手机上所装应用的开发者以及苹果。
这的确有点过于简单粗暴,毕竟许多应用的功能不止局限于开发者提交到应用商店的那些。SDK、远程分析服务以及开源组件意味着许多应用中实际上包含了来自多个机构的源码,并且必须通过网络进行通信就有潜在被监视的风险。游戏规则已经不再是保护不同的人彼此使用同一台计算机,而是保证同一个人的不同任务不会互相干扰。
这些听起来挺糟糕的,就好像有点轻微的偏执狂症状。现实情况是,安全能够成为推动力量,因为它减少了在新的场景和过程中的风险,为人们进入更广阔的空间提供了可能。想象一下如果没有使用密码学,移动银行业务将会增加多少风险,而又会有多少人(甚至包括银行)愿意开展这项业务。
UNIX 仍然与现代的软件安全讨论息息相关的唯一原因是,我们仍然没有摆脱这个系统,这主要是因为我们从来都没有尝试过。计算的历史充满了这样的例子,明明有些系统已经暴露出重大的安全问题,但它依然在使用,因为这个行业在清理自己烂摊子方面集体都表现得很差。即使是最新版本的 iOS,拥有最新的工具和最新的编程语言,我们在其中所调用的方法,例如 C 语言的字符串库,也被认为在几十年前就需要被重构。
在软件系统的演变史中,补丁一直存在。接受不完整的技术有利于定位已发现的问题。虽然我们喜欢声称我们发明了未来,而实际上,我们花费了大量时间和资源来坚守过去。当然,也许更换这些系统也会引入许多我们已经修复的问题。
苹果告诉我们每一个版本的 iOS 都比过去更安全,甚至公布了一份系统安全特性细则的白皮书。在其中苹果介绍它如何使用了不断更新的(希望是更加先进的)加密算法和协议,更健壮的鉴定表单以及其他技术。为什么这样还是不够?
操作系统只能提供适用任意应用的安全特性;但不能提供你的应用所需的一切。虽然苹果能告诉你,你的 app 连接到了一个提供了一些有效身份证明的服务器,但它不能告诉你这是否是一个值得你信任的身份。
苹果可以提供文件保护来加密你的数据,并且在收到请求时解锁。但它不会告诉你什么时候可以进行这样的请求。
苹果可以限制应用之间的沟通方式,使得数据只能在受控的方式如 URL scheme 中进行。苹果既不能决定哪个级别的控制适合你的应用,也不能告诉你你的应用应该接受什么形式的数据,以及什么形式的数据是不合适的。
和操作系统的特性相似,移动应用程序漏洞排行榜能告诉你哪些问题是许多应用都遇到的,但却不能告诉你具体哪些和你的应用相关,以及它们是如何暴露的。它们也绝对不会告诉你当你的用户使用你的应用程序时漏洞被利用的详细情况---漏洞从执行的任务、上下文和环境中产生并且随着用户操作的过程中浮现出来。
安全是你应用程序架构的一部分:它是一个约束集合,你必须像顾虑 app 的响应时间、客户群的规模或者是外部系统的兼容性那样来考虑安全的问题。这意味着你必须将安全模块设计到你的应用中,就像你设计应用时遵循的其他约束条件一样。
一个评估应用安全常用的设计技巧是风险建模 :找到黑客们想要攻击你系统的原因,查出他们在使用现有的资源下的攻击方式,以及探索在系统设计中的可能被成功攻击的漏洞。
即便你已经确定了漏洞,还是要使用许多方法来应对。拿个现实生活中的例子打比方,假设你正打算确定假期行程,但在这周你老板仍有需要你保持联系甚至随时准备出现在办公室以应对紧急情况的可能性。你可以做如下应对:
所有这些可能性也都可以在软件安全中使用。你可以选择一个或者结合多个方法。我们的目标通常是不是规避风险,而是减轻风险。风险减轻到多少是合理的?这取决于你、你的公司、你的客户和你的合作伙伴能够接受剩余多少风险。
你缓解风险的技术同样取决于你的目标:当你可以引入任何安全对策时你想要达到什么目标?你们是要保护客户隐私、保证服务的持续可用性还是只要遵循相应的法律?如果这些目标冲突,你需要比较它们的优先级。你的决定可能取决于具体情况,这未必是你能提前设计出来的。建立充足的应急预案能让大家在再次发生不好的事的时候知道应该如何处理就好。
尽管软件安全技术和系统(比如 iOS)的安全保障能力一直在进步和创新,但风险分析和设计安全对策依然是应用开发者们的责任。那些使用我们应用时所产生的风险是不可能通过操作系统供应商或者框架开发人员解决的。这些风险是由应用所提供给用户的功能,或者在应用部署的环境或系统中所产生的。
苹果用 iOS SDK 中的安全和加密功能帮助我们找到了水源。而喝水的事情,得靠我们自己。