利用威胁模型改进安全流程
2008-12-01 13:35:19 来源:WEB开发网目录
威胁建模
代码审阅和模糊测试优先级
模糊测试
减少攻击面
降低权限
这一切意味着什么?
本专栏将使用威胁模型来改善安全工程流程,以从更全面的角度提出一种安全设计考虑模式,可主要用于帮助您为代码审阅、模糊测试和攻击面分析任务来排定优先级。
鉴于本专栏的设置,您可能希望先阅读 Jeremy Dallman 的帖子“向 SDL '匍匐前进'”,它位于安全性开发生命周期 (SDL) 博客中。您会发现,本专栏提出的概念与 Jeremy 的想法非常吻合,您可以在循序渐进学习使用 SDL 的过程中改进安全工作的结构性和精确度。
我要介绍的是使用威胁模型来帮助推进其他 SDL 安全要求,主要是代码审阅优先级、模糊测试优先级和减少攻击面。这就是全部工作。当然我需要对此进行解释,因此让我们稍微详细地介绍一下每项工作。我首先介绍威胁建模。
威胁建模
许多《MSDN 杂志》读者对威胁模型的概念都很熟悉。Adam Shostack 在 2008 年 7 月刊中撰写了一篇有关这一主题的非常不错的文章(“重振您的威胁建模过程”),并在 SDL 博客中更深入地介绍了未来威胁建模过程概览。
在我看来,当人们考虑安全漏洞时,大部分人想到的是实现错误。有人可能会认为 SDL 有点过于关注实现错误了,因为从历史上看,Microsoft 修复的大多数漏洞都是由实现错误引起的。但在过去的几年中,我们已将更多的资源投入到了安全设计方面,部分原因是由于有了 SDL,实现错误现在已经很少出现了。
威胁建模是 SDL 的基础,因为它允许开发团队以结构化的方式来考虑安全设计。威胁建模过程可以精简为以下任务:
绘制软件数据流的概观。
使用“依据元素的 STRIDE”方法查找针对设计的威胁。
解决每个威胁。
验证您已建模了足够多的软件,已考虑到了每种威胁并解决了已发现的所有威胁。
威胁模型的核心元素是应用程序入口点的描述。威胁模型会在“绘图”阶段捕获入口点作为信任边界。其示例包括联网入口点、文件和注册表入口点等。
良好的威胁模型还应该可以捕获网络可访问性和接口的身份验证/授权要求。这包括基于 IP 地址的网络可访问性(本地和远程、本地子网以及仅本地访问)。它还包括身份验证和授权级别、匿名访问、用户访问以及仅管理员访问。对于 Windows 访问控制列表 (ACL),授权级别更加精细,但在此不需要做太深入的讲述(有关 ACL 的更深入讨论,请查看 John Michener 在本期发表的文章“理解 Windows 文件和注册表权限”)。这里只是简单提一下。
威胁模型捕获的另一个关键数据段是进程标识。入口点只是在进程中进入运行的代码段的接口,如果遭到破坏,高权限进程将非常危险。在 Windows 中,最高权限进程是指以 SYSTEM 或管理员身份运行的进程。在 Linux 或 Mac OS X 中,以根身份运行的进程具有最高权限。
每个应用程序入口点应记录以下数据:
入口点名称
网络可访问性
身份验证和授权
进程名称和进程标识
注释(如果需要)
图 1 显示了一些示例。很显然,第一个网络端口的公开度最高,因为它对地球上的所有人都是公开的。最公开的入口点是面临最大攻击风险的入口点,因为对于可远程访问的内容,任何人都可以使用同一网络(在本例中,可能是 Internet)中的计算机进行访问。允许用户匿名访问的内容也可由未经身份验证或授权的任何人访问。因此,可远程匿名访问的入口点几乎任何人都可以访问,其中也包括居心叵测者。
图 1 进程入口点
入口点 | 网络可访问性 | 身份验证和授权 | 进程 | 注释 |
TCP/1234 | 远程 | 匿名 | App.exe (SYSTEM) | 主请求/响应 |
.pipeadmin | 远程 | 管理员 | App.exe (SYSTEM) | 管理命名管道 |
ncacn_ip_tcp 609b954b-4fb6-11d1-9971-00c04fbbb345 | 远程 | 通过身份验证的用户 | User.exe(网络服务) | 用户的帐户信息 RPC 接口 |
Config.xml | 本地 | 管理员 | App.exe (SYSTEM) | 应用程序配置文件 |
管理命名管道只能由远程(和本地)管理员访问,这可使用 ACL 来适当地强制实施。对所有 Windows 命名对象(包括命名管道)都进行 ACL 验证至关重要。
还应注意,许多入口点位于以 SYSTEM 身份运行的代码中。可远程访问的未经身份验证的入口点和以 SYSTEM 身份运行的进程这二者的组合是 Windows 中存在的公开度最高的组合。如果此代码中存在安全性错误,那么任何人在地球上的任何角落都可以使其失败。被利用的代码将以 SYSTEM 身份运行,这意味着这种攻击可以完全控制计算机。我将在本专栏的后面部分对此主题做详细的介绍。
公开度有助于驱动代码审阅和模糊测试优先级。下面将对此进行讨论。
代码审阅和模糊测试优先级
就此先考虑片刻;您有许多代码需要进行审阅和测试。但您不能将未来 10 年中的所有时间都花在代码审阅上,因为有些时候您必须向客户发布产品。这意味着您必须为各项安全性工作排定优先级,因此,必须为最危险的环节赋予最多的、最深入的关注(有关找出最危险环节的一些提示,建议您阅读“谁是坏家伙”边栏)。这表明将会首先审阅和模糊化处理最危险的代码。
手动审阅代码时,一定要先审阅公开度最高的代码。例如,TCP/1234 套接字示例的代码可能与图 2 类似。由于调用 recv 时会读取未经身份验证的远程网络端口,在经过验证之前 buf 应被视为有毒废物。在安全环中,此数据通常被视为已遭破坏,无论在准确性方面还是在结构方面都不再值得信赖。
图 2 潜在问题缓冲区
SOCKET remoteSocket = accept(listenSocket,NULL, NULL);
if (remoteSocket == INVALID_SOCKET) {
PRINTERROR("accept()");
closesocket(listenSocket);
return;
}
// Receive data from the client
char szBuf[256];
memset(buf, 0, sizeof(buf));
nRet = recv(remoteSocket,
buf, // Danger!
sizeof(buf),
0);
if (nRet == INVALID_SOCKET) {
PRINTERROR("recv()");
closesocket(listenSocket);
closesocket(remoteSocket);
return;
}
// process data in buf
为使事情在代码审阅过程中变得简单一些,应创建一个表格,其中包括源代码位置和入口点的 API 调用以及数据等(请参见图 3)。现在,跟随数据从入口点 API 一直到用来验证或净化传入数据的代码。如果您没有验证数据完整性的代码,而您的代码可能会对传入数据执行危险操作,则您可能会遇到编码错误(最好的情况)和严重的漏洞(最糟的情况)。无论代码行为如何,都绝不能假定数据构成没有问题。
图 3 分析入口点
入口点 | 源文件 | API 和数据 |
TCP/1234 | Readfromsocket.cpp | recv(,buf,) |
.pipeadmin | AdminConsole.cpp | ReadFile(,bCommand) |
ncacn_ip_tcp 609b954b-4fb6-11d1-9971-00c04fbbb345 | UserInfo_i.c 和 UserInfo.idl | Various |
Config.xml | Config.cpp | ReadFile(,bFileContents) |
模糊测试
回过头来看一看最后一行:“绝不能假定数据构成没有问题。”模糊测试的目标是确定您是否的确做出了假设,如果是这样,那么是否在有人违反这些假设时会导致您的应用程序失败。这里我不打算对模糊测试进行解释,因为 MSDN 杂志已多次介绍过,(例如,Dan Griffin 撰写的文章“模糊测试:为 Team System 创建自定义的测试接口提供程序”,但我将介绍如何使用模糊测试。
与代码审阅一样,您无法对每个可能的代码路径都进行模糊测试,因此需要按公开度排定优先顺序。为此,您应首先模糊化公开度最高的入口点。在我的小示例中,我将对 TCP/1234 入口点在早期进行严格的、经常性的模糊测试。实际上,我永远也不会停止模糊化该入口点!毕竟,CPU 时间非常廉价。
我会再前进一步并询问自己,为什么这个接口具有如此高的公开度?默认情况下我能减少公开度吗?这就是减少攻击面需要解决的问题。
减少攻击面
我之前已经说过无数次,但我还要重申一遍:减少攻击面与尝试彻底搞清代码同样重要,因为您永远无法彻底搞清代码。我曾在 2004 年 11 月发表的文章“攻击面:通过尽可能减少向未受信用户公开的代码降低安全风险”中详细介绍过这一原则。这里我将为您对此做一概述:您应减少攻击面,以减少对您的客户的潜在风险。毕竟,对所有攻击者都公开的接口要比仅对本地子网中的攻击者公开的接口面临的风险更大,原因很简单,后者出现潜在攻击者的可能性要小一些。
来看一看我的示例,默认情况下我将 TCP/1234 入口点作为经过身份验证的入口点,仅允许经过身份验证的用户对其进行访问。这可能是只使用了基本身份验证(是的,我也知道它是一种糟糕的身份验证方案),也可能是一些更为复杂的验证(如 Kerberos)。这是我的默认设置,如果系统管理员要将其改为匿名访问,也没有任何问题。管理员可自行对此做出决定。
必须要指出的是,如果我向应用程序添加了身份验证,那么威胁模型就需要进行更新,因为现在系统中的某处已经存在身份验证过程和身份验证凭据数据库。
谁是坏家伙?
以下是安全代码审阅的黄金规则:始终都要了解那些坏家伙所控制的内容。这带来的必然结果就是,谁是坏家伙?究竟谁是坏家伙要取决于入口点的身份验证和授权级别。
在图 1 中给出的第一个 TCP 端口示例中,坏家伙绝对是地球上的任何人。对于第二个入口点(命名管道),坏家伙实际上并不是坏人,因为他是本地管理员组的成员。如果您不信任您的管理员,那么您能相信谁呢?
如果您的时间有限,那么应最早、最深入地对哪个接口进行代码审阅呢?很显然,应该是第一个接口,因为地球上的任何人都可以对其进行访问。如果在这段代码中存在安全性错误,那么任何人都可以破坏它。要真正弄清这一点,请想一想蠕虫:蠕虫依靠的就是未经身份验证的远程网络接口。
降低权限
减少攻击面的另一方面是降低应用程序权限,这是我接下来要讨论的内容。还记得以 SYSTEM 身份运行的进程吗?嗯,您需要考虑使用较低权限的帐户以减少侵扰中的潜在损害。这是减少攻击面的另一个重要方面。
从公开度最高的入口点开始。在本例中,如果做出的决定是默认允许匿名访问 TCP/1234,那么您应弄清楚侦听该端口的进程为什么要以 SYSTEM 身份运行。是否可以将其降低为较低权限的帐户?
还要注意,两个网络入口点和一个文件入口点都在同一进程中运行。这样做真的合适吗?两个入口点是针对管理员,一个入口点是针对匿名用户。您应考虑将此进程分成两个不同的进程,以便能够充分利用较低权限的进程标识。
如果您看一看 IIS 6.0 和 IIS 7.0 体系结构,会注意到处理匿名和远程用户请求的进程 (w3wp.exe) 以网络服务身份运行,而主管理进程 (inetinfo.exe) 以 SYSTEM 身份运行。 后一个进程仅限于管理员。在 IIS 4.0 和 IIS 5.0 中,进程处理和管理访问通常由一个高权限进程执行。
有趣的是,减少攻击面的几个方面早已为人所熟知。它们是典型 Saltzer 和 Schroeder 安全设计原则的一部分,包括最低权限和最低公用机制。
对第二个概念最低公用机制需要稍加说明。它意味着您应减少被多个用户或用户类型所共享的代码量。在我的示例中,执行管理任务和匿名用户请求的单一进程可能会很危险。修正方法是遵循 IIS 的操作,将应用程序拆分成以独立标识执行不同任务的两个进程。
这一切意味着什么?
说实话,本专栏其实没有定义任何新内容。所有这些都包括在 SDL 中!但本专栏解释了我们在 SDL 中定义、描述和要求的许多安全性领域之间的隐含关系,并说明了如何利用良好的威胁模型来帮助简化向客户交付更安全产品时所需的其余大部分安全工作。
构建良好的威胁模型、确定您的公开度、使用入口点列表(按公开度排列)来帮助驱动代码审阅和模糊测试优先级,最后促使攻击面下降。这样才能得到更安全的软件。
您是否想知道您对安全性的了解程度?请接受本期提供的具有挑战性的“安全性 IQ 测试”。
请将您想询问的问题和提出的意见发送至 briefs@microsoft.com。
Michael Howard 是 Microsoft 的一名首席安全性项目经理,主要致力于安全流程改进和最佳实践。他参与编写了多本安全方面的著作,其中包括“Writing Secure Code for Windows Vista”(编写 Windows Vista 安全代码)、“The Security Development Lifecycle”(安全开发生命周期)、“Writing Secure Code”(编写安全代码)和“19 Deadly Sins of Software Security”(软件安全的 19 个致命错误)。
赞助商链接