开发教室
程序设计|Delphi|Java|C++|VB|.NET|Css|Js|PHP|ASP|MySQL|数据库|WEB开发|网页特效|视频
首页 > 开发教室 > 程序设计 > .NET > VC.NET > 正文

复审它:在你的代码中寻找安全缺陷的专家技巧

2007-03-15 源自: 网友评论 0 进入视频教程

原文出处:MSDN Magazine November 2003


本文假定你熟悉安全,C#,和Visual Basic.NET

摘要
  因为安全缺陷而复审代码是软件制作过程中的一个关键部分,贯穿于计划编制、设计和测试。本文是作者反省其多年来的代码安全复审工作而总结出的所有开发人员在追踪潜在安全漏洞时可以遵循的一些识别模式和准则。该过程开始于检查代码的运行环境,考虑到将要运行代码的用户的角色,并研究该代码可能存在安全问题的历史。在对这些 背景问题有一个了解之后,特定弱点就可以被找到,包括SQL注入攻击,跨站点脚本,和缓冲区溢出。另外,某些危险信号,例如象 "password"、 "secret" 之类的变量名以及其它一些明显而普遍的安全失误都可以被查出并纠正。


  我的大部分工作涉及到复审别人的代码,寻找安全方面的错误。诚然,这并不是我的首要任务——往往是设计复审和威胁模型分析[编者:threat modeling 即威胁模型分析]—但是我确实要看到大量的代码。
  但愿你能理解对别人代码的复审工作,虽然是做了件好事,它确不是创造一个安全的软件的方法 。 你要通过设计、编写、测试并证明安全系统的过程,并通过在进度表中考虑安全复审、培训和使用工具所用的时间来产生安全的软件。只简单地设计、编写、测试和 编制项目文档,然后再寻找安全错误并不能创造出安全的软件。代码复审仅仅只是这个过程的一部分,但是它本身并不能创造出安全代码。
  在本文中我将不会讨论代码弱点的本性,比如整数溢出攻击、SQL注入和缓冲区溢出;你可以在一些书本中进一步了解这些问题(比如我的书 《Writing Secure Code》,Microsoft Press®,2002)。而是采用在一个高层次的观点来考虑复审代码过程中的问题。然而,在开始之前我想指出的是这仅仅是我复审代码 查找安全错误的方法;它不一定是你应该使用的复审代码方法,我也不能保证它的形式完全适合于某些特定种类的漏洞。我想证明的是我在看代码时脑中想到的同样对你 也的确是有帮助的。
  在我看来,复审代码有三种方法:详细分析,快速分析和混合方法。我倾向于采用混合方法,因为它有迅速覆盖很大范围的优点;如果我觉得某些东西需要更 深入的分析我会将它标出来以便将来进行代码复审,可能涉及到其他专家关注的领域。但是现在,我只讨论最初的快速代码复审,正如我喜欢这样称呼它,这种扫描''n''标记方法—迅速地扫描代码,并标出需要进一步复审的代码。 下面是我实现这个过程的要点。

分配时间和努力

  我有一套等级评定体系,我用它来确定复审代码所需的相对时间。这个体系基于如果弱点被利用后存在的潜在危害以及可能受到的潜在攻击。 具体针对的范围基于以下几个特点:

  • 代码以缺省值运行吗?
  • 代码以高优先级运行吗?
  • 代码是否侦听某个网络接口?
  • 网络接口是不可靠的吗?
  • 代码是用C/C++写的吗?
  • 该代码以前是否有历史弱点?
  • 该组件是否由安全研究员做过最终详细审查?
  • 该代码处理敏感或隐私数据吗?
  • 代码是可复用的吗(例如,某个 DLL、C++ 类头文件、库、或程序集)?
  • 根据威胁模型分析,该组件处于高风险环境或遭受高风险威胁吗?

  如果该列表中同时有三或四个以上项目被言中,这时我将在更深的层次上复审代码。事实上,如果代码是侦听传输控制协议(TCP)或用户数据报协议(UDP)的 socket 并以缺省值运行,那么就要准备大量时间来复审该代码。
  在查找安全错误时,我倾向于复审三个主要类别的代码:C/C++代码,网络服务器应用代码,(比如 ASP、ASP.NET、CGI、和 Perl) 以及托管代码(主要是C#,和一些 Visual Basic .NET)。
  你应该意识到每一种语言都存在一些细微的差别。首先,C 和 C++ 的首要问题是缓冲区溢出。诚然,还有一些其它问题,但是当你在同一个句子里听到单词“缓冲区”和“超限”时 ,你几乎可以确信这肯定涉及到了C 或 C++。更高级的语言如 C#、Visual Basic.NET 和 Perl 应该没有缓冲区溢出问题。如果有,那么这个缺陷可能出在运行时环境而非正在被复审的代码中。然而,这类语言常被用于 编写网络服务器应用软件代码,并遭遇其它类型的缺陷。缓冲区超限是令人讨厌的,因为攻击者可以将代码注入正在运行的进程中并取得控制权。所以让我们首先看看缓冲区溢出。

C和C++的缓冲区溢出

  缓冲区超限是软件行业的祸根,你应该竭尽全力将它们从代码里清除出去。但是,最好是首先不要让它们进入代码。在我复审缓冲区超限的代码有两个办法。第一是识别出这个应用软件 的所有进入点,尤其是网络进入点,跟踪数据在代码中的移动并质问数据是如何被处理的。我假定所有数据都是畸形的。当看到接触(读出或写入)该数据的任何代码时,我 便问,“有没有导致该代码失败的数据版本?”。这种方法虽然彻底但非常耗时。另一个技术是寻找已知的和潜在的危险结构并跟踪数据回到进入点。 以下面的代码为例:

void fuction(char *p) {        char buff[16];        …        strcpy(buff,p);        …        }
  如果我看到像这样的代码,我将跟踪变量 p 到它的源头,并且如果它来源于某个我并不信任的地方,或在接近它被拷贝的地方没有进行合法性检查,这时我知道已经找到了一个安全缺陷。值得注意的是,并不是说 strcpy 本身是危险的或者说是不安全的。应该说,恰恰是这个数据使得这类函数惊慌失措。如果你检查的数据具有良好的格式,那么 strcpy 可能就是安全的。当然,如果你犯错,那么你的代码就有一个安全错误。我也检查“n”版本字符串处理函数,比如 strncpy,因为你也要检查那些缓冲区大小的计算是正确的。
  我谨慎对待那些处理标注文件格式的代码。通过标注那些由块组成的文件,这里每个块都有一个头描述下一个数据块。MIDI音乐格式就是一个很好的例子。一个严重的安全缺陷被发现后,在一个处理 MIDI 文件的名为 quartz.dll 的 Windows 组件中被修复。有个畸形的 MIDI 结构导致了处理文件的代码失败,或更糟。你可以在 Unchecked Buffer in DirectX Could Enable System Compromise 得到更多关于这个缺陷的内容。

另一个我留心的结构是:
        while (*s != ''//'')        *d++ = *s++;		
这个循环囿与源中的某个字符;它不受目的地大小的限制。我主要用下面的正则表达式扫描 *x++ = *y++。
        /*/w+/+/+/s?=/s?/*/w+/+/+        

  当然,人们也可能用 *++x = *++y,因此你也需要对此进行扫描。我想在这里再次强调的是这个结构并不危险,除非数据源是不可信的,因此你需要确定数据源是否可信赖。

下面是另一类你应该注意的与缓冲区超限有关的问题:整数溢出弱点。

C和C++的整数溢出

  如果你不知道什么是整数溢出攻击和怎样修复它们,你应该先阅读我的:
Development Impacts of Security Changes in Windows Server 2003 这篇文章。真正的安全缺口发生 于计算某个缓冲区大小的算法以及计算导致的上溢或下溢。看下面的例子:

        void func(char *b1, size_t c1, char *b2, size_t c2) 	{
const size_t MAX = 48;
if (c1 + c2 > MAX) return;
char *pBuff = new char[MAX];
memcpy(pBuff,b1,c1);
memcpy(pBuff+c1,b2,c2);
}
  这段代码看起来挺好,但是,如果你将 c1 和 c2 相加并且结果超过 232-1。你便会认识到有问题,举个例子,0xFFFFFFF0 和 0x40 相加的结果是 0x30 (十进制数为 48)。当它们被用来做 c1 和 c2 的值时,这个加法通过了检查,此时这个代码将拷贝近 4GB 的内容到一个48字节的缓冲区。你正好遭遇到缓冲区超现!许多像这样的缺陷是可被人利用的,它允许攻击者将代码注入到你的进程中。
  当复审C 和 C++代码的整数溢出时,我查找所有 new 操作符以及动态内存分配函数(alloca, malloc, calloc, HeapAlloc等等)的实例,然

上一篇: 实例解析C++/CLI线程之多任务
下一篇:使用.NET 向浏览器写入二进制文件

评论  点击查看
 
开发频道推荐
开发热点文章