《威胁建模:设计和交付更安全的软件》——2.4 软件模型

本节书摘来自华章计算机《威胁建模:设计和交付更安全的软件》一书中的第2章,第2.4节,作者:[美] 亚当·斯塔克 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.4 软件模型

建立清晰的软件模型有助于寻找威胁,否则你会陷入到软件功能正确与否的细节中。图表是软件建模的最佳方法。
正如你在第1章学习到的内容,在白板上绘制图表是开始威胁建模非常有效的方法,但是当一个系统特别复杂,在白板上绘制或重新绘制模型就变得不可行了。这时,你要么简化系统要么使用计算机化的方法。
本章,你能学习到各种图表,在威胁建模时如何使用各种图表、如何处理大型系统威胁建模的复杂性。你也能学到信任边界、有效标签,以及如何验证图表等更多细节内容。
2.4.1 图表类型
做图表有很多方法,不同的图表在不同的情况下所发挥的作用不同。遇到的最多的图表类型可能就是数据流图(data flow diagram)。不过,你也能看到UML、泳道图(swim lane diagram)、状态图。你可以把这些图表看成乐高积木,仔细考虑哪个图表最适合给当前软件建模。这里的每一种图表类型都可以与第二部分的威胁模型一起使用。
通过图表,你可以了解到系统是如何运转的,这样参与威胁建模的每一个人都有同样的理解。如果你满意对于软件如何运转的图表,那么在大家逐渐达成一致的过程中,你很可能发现对系统的安全性理解错误。因此,使用图表有助于沟通,形成共同的理解。
2.4.2 数据流图
数据流模型经常是威胁建模最理想的模型。安全问题经常是在数据流中出现,而不是在控制流中。数据流模型通常存在于网络或者系统构架中,相比之下软件产品较少,但也可以创建。
数据流图的应用非常广泛,有时会被称为“威胁建模图”。1967年由Larry Constantine设计,数据流图(DFD)包括带编号的要素(数据存储和过程),通过数据流连接起来,并与外部实体相互作用(在开发者或者组织控制之外的实体)。
数据流图是以数据流命名,几乎总是向两个方向流动,也有例外,例如无线传输或UDP协议传输到互联网。尽管这样,数据流经常用单向箭头展现,因为威胁和它们的影响一般都是不可逆的。意思是说,如果到达web服务器的数据流被读取了,可能会暴露密码或其他数据,反之,从web服务器流出来的数据流可能会暴露你的银行账户余额。这一图表规则并没有阐明通道安全与消息安全的差别。(这个通道可能是SMTP,而消息是电子邮件消息。)如果通道与消息的区别很重要,那么采用泳道图可能更为合适。(泳道图会在本章后面讲述。)
数据流图的要素如表2-1所示。


《威胁建模:设计和交付更安全的软件》——2.4 软件模型

图2-3是基于表2-1中要素绘制的典型数据流图。图2-4显示的是同一个模型,只是做了一些变化,你可以用这个例子来改善自己的模型。

《威胁建模:设计和交付更安全的软件》——2.4 软件模型


《威胁建模:设计和交付更安全的软件》——2.4 软件模型

下面解释从经典数据流图到现代数据流图的变化:
进程变成圆角矩形,与圆圈相比可以有更好的文本内容。
利用直线,而不是曲线,因为直线更容易使用,适合应用在大型图表中。
过去,很多数据流图的描述中既包含“进程”因素,又包含“复杂进程”因素。一个进程被描绘成一个圆圈,复杂进程则是两个同心圆。但是什么时候用一般进程,什么时候用复杂进程,仍旧不够清晰。区分二者的原则可以定义为:有子图的就是复杂进程。“看起来有点圆”,听上去也是个像样的原则。
除了应用在软件产品外,数据流图还可以应用在多个案例上。例如,图2-5用数据流图展示了一个运营网络案例,这是中小型公司网络的典型模型,选取了典型系统和部门。附录E中会深入讨论。


《威胁建模:设计和交付更安全的软件》——2.4 软件模型Https://yqfile.alicdn.com/b44dbe76d5952c418f9343b5226f0c3ea5c12ec6.png
" >

统一建模语言UML
UML为统一建模语言的缩写。如果你在软件开发过程中使用UML,那么你也可以用UML图表进行威胁建模,而不需要重新画图。利用UML绘制威胁建模图表最重要的是添加信任边界。
UML相当复杂。举例来说,Visio的UML模板用了80个左右符号,而用数据流图的话只需要6个。也正是由于这种复杂性,使人们画结构图、行为图、交互图时能体现出大量微妙之处,且表达能力更强。如果威胁建模相关人员不熟悉所有UML符号,或者对于这些符号的意义理解有误,图表工具的有效性就会大打折扣。从理论上讲,任何有困惑的人都可以提问,但是前提是他们知道自己有困惑(他们可能会假定代表鱼的符号不包括鲨鱼)。应该允许人们愿意提一个“简单”而暴露自己无知的问题。对于使用UML开展威胁建模的团队来说,给UML图添加信任边界要比重新创建新图表更容易些。
泳道图
泳道图通常用于表达不同参与者之间的数据流。用长线绘制,每条线代表一个参与者,每个参与者都对应有一条线,每条道的边上标识每个参与者的身份。每条信息都由参与者之间的一条线表示;时间是通过每条泳道向下的流向来表示。图表有些像游泳道,故而得名。每条信息应以内容来标识,如果内容太复杂,那么就提取一些内容细节作为图标的关键词,使标识更有意义。参与者计算的数值或状态应该记录在表示参与者的线旁。通常该协议中的参与者是如计算机这样的实体,泳道图通常在每个参与者之间有暗含的信任边界。密码学家、协议设计者Carl Ellison将泳道图扩展至包括作为参与者的人,以此来研讨人们期待知道什么、期待做什么。他称这种扩展为“仪式”,这将在第15章中更详细地讨论。
图2-6显示一个泳道图范例。
状态图
状态图显示一个系统可能的不同状态以及这些状态间的转换。一个计算机系统基于它所收到的有效信息和存储的数据,可被建模成包含状态、存储、状态转换规则的机器(计算机测试收到的信息,根据规则进行验证)。每个方框标示一种状态,之间的线标上标示状态转换条件。威胁建模中你可以使用状态图,检查每次状态转换是否经过恰当安全验证。


《威胁建模:设计和交付更安全的软件》——2.4 软件模型https://yqfile.alicdn.com/fc225ec8be8034271eb887ecbe96a07ee5298663.png
" >
 

图2-7表示一个关于门的简单的状态机(源自*)。一个门有三种状态:开启、关闭、锁住,通过状态转换进入每个状态。“门栓”系统比锁上的旋钮更好画上去,因为后者从两个状态都能被锁住,这就创造了复杂的图表和用户体验。显然,状态图很快就会变得复杂。你可以想象一个更为复杂的状态图,其中包括“半掩”,一个可以从关闭和开启都能到达的状态(我曾画这个图,但是填写标签时遇到了难处。显然,门半掩着没有明确指向,不该部署)。你并不只是为了做架构决策而把模型设计得更简单些,而是简单的模型更易于使用,更易于反映出设计。
2.4.3 信任边界
正如第1章中介绍的,信任边界是不同主体汇聚的位置,也就是说,是实体与不同权限实体之间交叉的位置。
画边界
建立软件模型之后,添加边界的方法有两种:添加你知道的边界并寻找更多的边界,或者列举主体并寻找边界。从边界开始,尽量加入你所能加入的任何信任边界。Unix系统的UID、Windows会话、机器、网段等之间的边界要用方框来画,每个方框里面的主体都应添加标签。
从主体入手,从特权范围(通常是root/admin或者匿名网络用户)的一端或者另一端开始,然后每当其与“别人”对话了就添加边界。
你总是能添加至少一个边界,所有运算都会在某些情况下发生(因此,你可能会批评图2-1中显示的Web客户端和SQL客户端没有标识出背景)。
如果你不知道在哪里画一个信任边界,你的图表可能都在一个信任边界里,或者是你漏掉了边界。问自己两个问题:首先,系统里所有要素是否都具有相同级别的权限,并且可以访问系统里所有其他东西?其次,软件里的所有沟通交流都是在同一边界内完成的吗?只要这两个问题里面有一个回答是否定的,那么你就要马上弄清楚,要么遗漏了边界,要么图中缺少了一个要素,要么两个问题都存在。如果两个问题的回答都是肯定的,那么你该在所有实体之外画一个信任边界,然后继续其他开发活动。(这种状态是不太可能的,除非是开发团队每一部分都创建了一个软件模型。“自下而上”的方法将在第7章中详细讨论。)
很多威胁建模的著作都宣称信任边界应该只涉及数据流,这对威胁建模中最详细级别的设计来说很有用。如果信任边界涉及数据存储(即数据库),这可能表示有不同信任级别的数据表或者存储过程;如果信任边界涉及一个既定主机,这可能表示比如每一个“软件安装程序”在“网络内容更新”时有不同的权限;如果你发现信任边界涉及数据流之外的其他要素,要么将该要素一分为二(在模型中,或在现实中,或者是两者),要么画子程序图来表示它们被分成多个实体。好的威胁模型最关键是划清楚边界在哪,以及如何保护边界。反之,没有清晰的边界划分和认识,就不能创建好的模型。
使用信任边界
威胁常常与信任边界是密切相关的。这一点是显然的:信任边界勾勒出各个主体之间的攻击界面。一些人认为威胁只出现在边界主体之间,或者出现在信任边界上。这种假设有时是不准确的。原因是,假设一台web服务器需要进行一系列复杂的订购过程。例如,戴尔网上商店要组装一台电脑,会加入几千个零件,但是只有一部分是经过检测,可以出售的。我们可以构造出图2-8所示的网站模型。


《威胁建模:设计和交付更安全的软件》——2.4 软件模型

如图2-8所示,尽管web服务器是通过可信的TCP/IP堆栈进行会话,显然它也有可能受到来自网络浏览器的攻击风险。同样,销售模块也存在风险;虽然订单处理模块中对HTML提交的数据进行校验,但攻击者仍能够在提交HTML中插入随机的零件编码。即使在销售模块和订购模块没有信任边界,即使数据在三个边界都进行校验,威胁仍然跟随着数据在流动。由于客户仅作为一个外部实体,所以在图中被表示为网络浏览器。当然,web浏览器还有许多其他组件,但是如果没有对它们做任何威胁分析,那为什么给它们做模型呢?
因此,更确切地说,威胁往往会在信任边界和复杂解析过程中汇聚,但是也可能出现在攻击者能够控制信息的任何地方。
2.4.4 图表中包含的内容
那么图表中应该包括哪些内容呢?以下是一些经验法则:

  • 显示推动系统的事件
  • 显示驱动的进程
  • 判定每个进程将产生和发送什么响应
  • 识别每个请求和响应的数据来源
  • 识别每个响应的接收者
  • 忽略内在运作机制,重点关注范围

回答是否有什么能辅助你思考哪里出错了,或者有什么可以帮助你找到威胁
这个列表来自Howard和LeBlanc的《编写安全代码(第2版)》(Writing Secure Code,Second Edition)(Microsoft Press,2009)。
2.4.5 复杂图
当你构建一个复杂系统时,图会很复杂。系统变得复杂,绘制图表(或者理解整个系统)就变得很困难。
一条原则是“不要画成视力检查表”。一个真实的软件开发项目包含很多细节,而在模型中也需要包含这些细节,做好二者之间的平衡是很重要的。正如第1章提到的,为了实现平衡,可以利用子图来显示一个特定区域的细节。你需要采用很多合适的方法来拆解和描述你的项目中各个详细的技术领域。例如,假设有一个非常复杂的过程,可能这一过程本身构成一个图,过程以外的东西构成另外的图。假设有一个调度员或者排队系统,这可能是进行拆解的好地方。或许你的数据库或者故障转移系统是拆解的好地方。或许有一些要素确实需要更多细节,这些都是可以拆解的好方法。
做子图时要注意,子图的数量不要多于上一级主图的数量;另一种方法是利用不同的图来表现不同的场景。
有时,简化图表也是有用的。当图中的两个要素从安全角度来看是等效的,那么你可以让这二者合并成一个。等效是指在同一个信任边界内部,且依赖于同一技术,处理相同数据的一组要素。
切记,图表是用来帮助我们理解系统和展开讨论的。正如本书开始时说的“一切模型都是错误的,只有一些是有用的”。因此,在你添加额外的图时,不要想:“这么做是正确的吗?”而是问:“这对我们思考哪里出错有帮助吗?”
2.4.6 图的标签
图的标签要简洁明了。因为你想要用这些标签的名字去讲故事,所以首先从驱动系统的外部实体开始。那些都是名词,如“客户”或“振动传感器”。它们通过数据流传输信息,这些用名词或者名词短语标注,例如“要买的书”或“振动频率”。数据流从来不用动词来标注。虽然会比较难,但是你应尽量找些有意义的词作为标签,并用箭头方向来表示“读”或“写”。换句话说,数据流将信息(名词)传递到过程,过程是动态的:动词、动词短语或者动名词短语。
很多人发现用序号标注数据流非常有用,可帮助追踪事件发生的顺序。将图中的要素标注数字可以帮助保持图的完整性或易于交流。你可以给每个东西分别标注数字(数据流1、过程1等等)或者你可以在图表里统一标注数字,例如外部实体1与过程4对话,是通过数据流2和数据流3。通常,所有事物统一标注数字会比较清晰。你可以说“1号”而非“数据流1号,不是过程1号”。
2.4.7 图中的颜色
颜色可以添加大量信息,例如,微软的Peter Torr使用绿色表示可信任的,红色为不可信任的,蓝色表示正被建模的(Torr,2005)。然而完全依赖于颜色是有问题的,因为约有1/12的人是色盲,他们大部分还是红绿色盲(Heitgerd,2008)。即使有彩色打印机,还有一部分人不能完全获得颜色想要表达的信息。用带有标签的边框可以解决这个问题。有了带边框的信任边界,就没有理由使用颜色了。
2.4.8 入口点
威胁建模的一种早期方法是“资产/入口点”法,在对操作系统进行建模时非常有效。该方法可以分解成以下几个步骤:
1.绘制数据流图。
2.找到数据流中跨越信任边界的点。
3.将数据流和信任边界的交叉点标为“入口点”。
还有很多其他步骤和方法,但是我们作为一个团体已经学习了很多,重新完整解释会令人乏味和注意力分散。
在Acme/SQL示例中(如图2-1所示),入口点是“前端”和“数据库管理”控制过程。“数据库”也是一个入口点,因为名义上,其他软件可以在数据库改变数据,可以使用数据解析故障来获得对系统的控制。对于财务来说,入口点是“对外输出报表”、“财务规划和分析”、“核心财务软件”、“销售”和“应收账款”。
2.4.9 表验证
验证得到的表是否是软件最恰当的模型的目的有两个:保证准确性和结果完美。第一个目标较为容易,因为你可以对该图是否反映了实际状况征求意见。如果图表缺失了重要部件,或者图显示的东西并不存在,那么可以知道没有反映事实。如果图中缺少重要的数据流,或出现了并不存在的数据流,那也没有反映事实。如果不编辑图你无法讲出软件的故事,那么也是不准确的。
当然,“重要性”也引出第二个标准:结果完美。重要的是能帮你找到问题,寻找问题时可以问自己这样的问题:“这个要素有没有安全影响”,“是不是有时发生或者在特定情况下才发生”。获得问题答案是个经验问题,就像构建软件的多个方面。一个好的、经验丰富的架构师能快速评估需求并解决它们,一个好的威胁建模者可以很快了解哪些要素是最重要的。达到这种经验水平很大程度上要靠多练习。本书第二部分涉及的系统的寻找威胁方法,旨在帮助你识别哪些要素是重要的。
如何验证图
为了更好地验证图,让最了解这个系统的人员聚在一起,要有人站在图前,排查重要的用例,确保以下几点:
他们能充分讨论图要描述的故事。
在目前情况下,他们无须改动图表。
他们无须提到图中没有出现的事物。
当你更新图表、积累经验时,这些经验法则会很有用:

  • 任何时候听到有人说“有时”或“也”时,都应考虑加入更多细节来拆解诸多场景。例如,如果你说:“有时我们通过SSL连接到web服务器,有时需要回归使用HTTP。”这两种数据流你都要画上(并思考攻击者是否会让你那样回退)。
  • 每当你需要更多细节来解释安全相关行为时,都需要画到图上。
  • 每个信任边界边框都必须有一个标签。
  • 每当你对系统设计或者构建有不同意见时,将那些细节画到图上。为了让所有人保持进度一致,这是很重要的一步,对于不是所有人都能聚在一起对威胁建模进行讨论的大型团队尤为重要。任何人看到得到的图与他们的想法相矛盾的时候,他们都可以要么接受它,要么挑战这个假设,不管是哪一个,清晰准确的图表可以让每个人保持相同进度。
  • 不要有数据池:你写的数据是要有原因的,要表示出谁在使用这些数据。
  • 数据不能从一个存储区转移到另一个存储区,要表示出其移动的过程。
  • 所有数据到达的方式都应表示出来。
  • 如果有控制数据流的机制(例如防火墙、访问权限)也要表示出来。
  • 所有过程必须至少有一个入口数据流和一个出口数据流。
  • 如前面讲过,不要画成视力检查表。

图表要能打印出来。
《撰写安全代码》(Writing Secure Code)的作者David LeBlanc表示:“一个过程没有输入时是个奇迹,而没有输出就是黑洞。要么你遗漏了东西,要么已经把过程误以为是被允许成为黑洞或是奇迹的人。”
什么时候验证图表
对于软件产品,有两个主要的验证图表时间:创建的时候和准备发布测试版的时候。还有第三个触发事件(不那么常见),就是你添加安全边界时。
对于可操作的软件图表,在图表创建的时候进行验证,然后在效果与最新的输出之间做权衡来确定是否需要再次验证。这种权衡会根据系统的成熟度、规模、组件间的紧密程度、首次展示的节奏、新产品本质等有所不同。在此提供一些指南:

  • 相比成熟的系统,新系统会经历更多次的图表改变。
  • 相比小型系统,大型系统会经历更多次的图表改变。
  • 相比松耦合系统,紧耦合系统会经历更多次的图表改变。

系统上线便尽快做出修改将在以后每次展示时经历较少次的图表改变。
关注重构或技术的上线或冲刺可能会经历更多的图表改变。不论哪种情况,创建一个合适的追踪项目,确保在合适的时间对你的图表进行重新检查。适当的追踪项目是尽量地延迟软件的发布或上线,例如缺陷、任务管理软件或检查列表。如果没有办法延迟软件发布的方法,那么在担心是否需要检查威胁建模之前,你可能要关注是否有明确规定的发布过程。该过程描述不在本书讨论范围内。

上一篇:欧盟下周将有条件批准微软260亿美元收购领英


下一篇:nginx启动、重启、关闭