读软件开发安全之道:概念、设计与实施09安全设计
1. 安全设计
1.1. 过载、混乱和迷惑性并不是信息的属性,而是设计的失败。
- 1.1.1. 爱德华·塔夫特
1.2. 不应该将系统的安全性留给审查员进行修补
-
1.2.1. 审查员应该在对设计进行审查时,将威胁和缓解作为安全评估的附加
-
1.2.2. 小型企业的运营通常不会那么正式,并且设计师和审查员可能是同一个人
2. 在设计中集成安全性
2.1. 设计阶段为将安全原则和模式应用到软件项目中提供了绝佳的机会
-
2.1.1. 在设计阶段,开发者应该创建开发文档,以便明确一个软件项目的高层级特征,类似于一个架构蓝图
-
2.1.2. 设计文档中通常包含功能描述(从外部审查软件是如何工作的)和技术规范(从内部审查软件是如何工作的)
2.2. 经验丰富的设计师会从一开始就将安全性纳入设计方案之中
-
2.2.1. 在开始编码之前,你可以先进行威胁建模、识别攻击面、绘制数据流等
-
2.2.2. 在设计过程的早期最容易进行重大更改,这样能够避免事后重做
-
2.2.3. 尽早探索新的架构,并满足基本的要求,越早着手越容易完成
2.3. 明确设计中的假定规则
-
2.3.1. 预测严重的误解,从而使你永远不会听到任何人说:“但我原本以为……”
-
2.3.2. 一些对文档很重要,但在设计中很容易忽略的常见假定规则
-
2.3.2.1. 会对设计空间带来限制的预算、资源和时间
-
2.3.2.2. 系统是否可能成为攻击目标
-
2.3.2.3. 非协商性要求,如与旧系统的兼容性
-
2.3.2.4. 对于系统必须执行的安全级别的期望
-
2.3.2.5. 数据的敏感性,以及保护数据安全的重要性
-
2.3.2.6. 对系统未来变更的预期需求
-
2.3.2.7. 系统必须达到的特定性能或效率基准
-
2.3.3. 澄清假定规则对于安全性至关重要,因为误解通常是一些问题产生的根本原因
-
2.3.3.1. 薄弱的接口设计或组件之间交互不匹配,而攻击者恰恰能够利用上述情况
2.4. 定义范围
-
2.4.1. 如果一个设计的范围很模糊,审查员可能会认为一些重要的安全内容不在范围之内,而设计师也不会意识到有问题
-
2.4.2. 不要因为那些被遗漏在设计生态系统之外的部分,导致整个设计的失败
-
2.4.3. 当你接管一个旧系统时,你首先要努力理解它,要关注它最敏感的部分,以及对于维护安全性最基础的部分,或者最明显的攻击目标
-
2.4.4. 你可以通过定义一个狭窄的范围,使其对应需要重新设计的部分,来处理现有系统的设计迭代、冲刺(sprint)和主要修订
-
2.4.5. 在重新设计的过程中,工作超出预期范围是很常见的,并且这通常是一件好事,当超出预期范围时,你应该根据需要对范围进行调整
-
2.4.6. 很少有软件设计是存在于“真空”中的,它们需要依赖于现有的系统、流程和组件
-
2.4.6.1. 确保设计能够与它所依赖的事务很好地协作至关重要
2.5. 设置安全要求
-
2.5.1. CIA三元组是一个很好的起点
-
2.5.1.1. 描述保护私人数据免遭未经授权披露的需求(机密性)
-
2.5.1.2. 描述保护和备份数据的重要性(完整性)
-
2.5.1.3. 描述系统所需要的稳健和可靠程度(可用性)
-
2.5.2. 许多软件系统的安全要求很简单,但为了确保完整性并传达优先事项,仍然值得对其进行详细的说明
-
2.5.3. 要特别关注对于安全性要求很高的软件,我们要仔细列举它的安全要求
-
2.5.4. 安全性无关紧要
-
2.5.4.1. 由多个研究小组共享的天气数据收集程序:温度和其他大气条件都可供任何人免费测量,并且披露这些信息也是无害的
-
2.5.5. 一般性准则
-
2.5.5.1. 将安全要求表达为最终目标,而不规定“如何做”
-
2.5.5.2. 考虑所有利益相关者的需求
> 2.5.5.2.1. 在这些需求可能发生冲突的情况下,有必要找到一个良好的平衡点
-
2.5.5.3. 了解关键缓解措施的可接受成本和权衡
-
2.5.5.4. 当有不寻常的要求时,解释这些要求的动机以及它们的目标
-
2.5.5.5. 设定可以实现的安全目标,无须要求完美
2.6. 威胁建模
-
2.6.1. 提高软件架构安全性的最佳方法之一是将威胁建模纳入设计过程之中
-
2.6.2. 先炮制一系列潜在的设计,依次对每一个设计进行威胁建模,通过某种汇总评估对它们进行评分,然后选择最好的一个
-
2.6.3. 威胁建模会突出风险,进而促使你评估替代方案
-
2.6.4. 使用基准威胁模型来指导设计的另一种方式是突出设计决策带来的额外风险的来源
-
2.6.4.1. 为敏感数据添加缓存层,以提高响应时间
-
2.6.5. 好的软件设计最终取决于主观判断
-
2.6.6. 策略
-
2.6.6.1. 进行灵活的设计,以便未来轻松地添加安全保护
> 2.6.6.1.1. 不要把自己置于不安全的境地
-
2.6.6.2. 如果有需要特别关注的特定攻击,则对系统进行检测,以推动对企图滥用实例行为的监控
-
2.6.6.3. 当可用性与安全性发生冲突时,寻求用户接口的替代方案
-
2.6.6.4. 使用一些能够说明设计中可能存在的主要缺点的潜在场景(源自威胁模型),来解释安全风险,并使用这些场景来证明不实施缓解措施的成本
3. 建立缓解措施
3.1. 设计接口
-
3.1.1. 接口定义了一个系统的边界,描绘了设计或其组件的局限性
-
3.1.2. 可能包括系统调用、库、网络(客户端/服务器或点对点)、进程间和进程内 API、公共数据存储中的共享数据结构等
-
3.1.3. 复杂的接口通常都有自己的设计,比如安全通信协议
-
3.1.4. 要记录输入的数据是否经过可靠验证,或应该被视为不受信任的数据
-
3.1.5. 连接外部组件(设计范围之外的组件)的接口应该符合这些组件的现有设计规范
-
3.1.6. 要想设计安全的接口,首先要对它们的工作方式做出可靠的描述,包括必要的安全属性(即CIA、黄金标准或隐私要求)
-
3.1.7. 审查接口的安全性相当于验证它们的功能是否能够正常运行,以及是否能够在潜在威胁面前保持强健
-
3.1.8. 在某些情况下,另一种选择是封装接口以添加安全保护
-
3.1.8.1. 设计一个额外的软件层来提供加密和解密,以确保该组件仅存储加密后的数据,这样即便数据泄露了也不要紧
3.2. 设计数据处理
-
3.2.1. 数据处理是几乎所有设计的核心,因此保护它是很重要的一步
-
3.2.2. 减少对敏感数据的移动
-
3.2.2.1. 在设计级别上显著降低风险的关键机会
-
3.2.2.2. 以后的实施中通常是不可能做到的
-
3.2.2.3. 要想减少传递数据的需要,一种方法是将数据与不透明的标识符相关联,然后使用该标识符作为句柄,在必要时,你可以将其转换为实际的数据
-
3.2.2.4. 标识公共信息或数据是否具有保密要求
> 3.2.2.4.1. 这在数据处理的要求中是一个重要的例外,让你能够在合理的地方放松保护
- 3.2.2.5. 在没有明确规定的情况下,始终将个人信息视为敏感信息,并且仅在有特定用途时才收集此类数据
> 3.2.2.5.1. 无限期地存储敏感数据会带来无尽的保护义务
> 3.2.2.5.2. 要想避免这种情况,你可以在可能的情况下(比如在其变得不活跃的多年后)销毁不使用的信息
3.3. 将隐私融入设计
-
3.3.1. 个人信息的泄露经常会成为头条新闻,我相信公司可以通过将隐私融入软件设计中来获得更好的结果
-
3.3.2. 隐私问题关乎数据保护给人带来的影响,不仅涉及法律和监管问题,还涉及客户期望和未经授权即披露所带来的潜在影响
-
3.3.3. 当人们或流程对政策中的承诺产生了误解,或者根本没有考虑上述内容时,往往就会发生隐私问题
-
3.3.4. 审计是隐私管理的重要工具,即使我们只使用它来记录对敏感数据的合法访问
-
3.3.4.1. 通过仔细追踪访问记录,我们可以及早发现并纠正有问题的访问和利用
-
3.3.4.2. 在隐私数据泄露之后,如果没有任何记录可以指明谁可以访问相关数据,那么人们将很难有效地做出响应
-
3.3.5. 设计师要尽可能设计出明确的隐私保护措施
-
3.3.5.1. 如果你无法判断隐私的合规性,请让隐私政策的负责人在设计上签字
-
3.3.6. 常用技术
-
3.3.6.1. 识别收集到的新类型数据,并确保隐私政策合规
-
3.3.6.2. 确认政策允许你将数据用于你想要的目的
-
3.3.6.3. 如果该设计可能不会对数据的使用施加限制,请考虑仅将访问权限赋予熟悉隐私政策规范以及了解如何审计合规性的员工
-
3.3.6.4. 如果政策限制了数据的保留期限,请设计一个系统来确保及时删除相关数据
-
3.3.6.5. 随着设计的发展,如果数据库中的某个字段被废弃,请考虑将其删除以降低泄露的风险
-
3.3.6.6. 考虑建立一个数据共享的审批流程,以确保接收方能够获得管理层的批准
4. 规划整个软件生命周期
4.1. 太多的软件设计都默默地假定了这个系统会永远存在下去,而忽略了一个现实,即所有软件的生命周期都是有限的
4.2. 从首次发布和部署,到更新和维护,再到最终退役,系统生命周期中的许多方面都存在重大的安全隐患,而且这些隐患随着时间的推移很容易被忽略
4.3. 至少任何设计都应该考虑数据的长期处理
5. 权衡取舍
5.1. 在无法简单地做出选择的情况下,权衡取舍需要进行大量的工程判断,同时还要考虑很多其他因素
5.2. 大多数设计工作都是在企业或项目社区中进行的,它们所需的安全级别在很大范围内都是统一的
5.3. 设计阶段是在软件的各项竞争需求之间取得适当平衡的最佳机会
5.4. 当考虑到预定的截止日期、预算和人数限制、遗留的兼容性问题,以及冗长的功能列表时,很少会(甚至完全不会)将安全作为重中之重
5.5. 安全软件设计的核心是在这些理想化的原则与现实世界系统的实用需求之间取得适当的平衡
-
5.5.1. 完美的安全性从来不是我们的目标,额外的缓解措施也只能带来有限的好处
-
5.5.2. 最佳设计从来都不容易确定,但在软件设计中明确这些权衡,更有可能找到合理的折中方案
6. 设计的简洁性
6.1. 如果安全性需要依赖于正确地做出一些复杂的决定或设计一些复杂的机制,我们都要小心:要看看是否有更简单的方法来实现相同的目标
6.2. 在软件中,我们却很容易在不经意间绕过外部保护层,打开通往内部的通道