Board logo

标题: 安全编程 防止缓冲区溢出-解决方案续 [打印本页]

作者: look_w    时间: 2018-4-19 12:18     标题: 安全编程 防止缓冲区溢出-解决方案续

C/C++ 解决方案针对缓冲区溢出的一种简单解决办法就是转为使用能够防止缓冲区溢出的语言。毕竟,除了 C 和 C++ 外,几乎每种高级语言都具有有效防止缓冲区溢出的内置机制。但是许多开发人员因为种种原因还是选择使用 C 和 C++。那么您能做什么呢?
事实证明存在许多防止缓冲区溢出的不同技术,但它们都可划分为以下两种方法:静态分配的缓冲区和动态分配的缓冲区。首先,我们将讲述这两种方法分别是什么。然后,我们将讨论静态方法的两个例子(标准 C                         strncpy/strncat  和 OpenBSD 的                         strlcpy/strlcat ),接着讨论动态方法的两个例子(SafeStr 和 C++ 的                         std::string )。                  
重要选择:静态和动态分配的缓冲区缓冲区具有有限的空间。因此实际上存在处理缓冲区空间不足的两种可能方式。
静态方法具有一些缺点。事实上,静态方法有时可能会带来不同的缺陷。静态方法基本上就是丢弃“过多的”数据。如果程序无论如何还是使用了结果数据,那么攻击者会尝试填满缓冲区,以便在数据被截断时使用他希望的任何内容来填充缓冲区。如果使用静态方法,应该确保攻击者能够做的最糟糕的事情不会使得预先的假设无效,而且检查最终结果也是一个好主意。
动态方法具有许多优点:它们能够向上适用于更大的问题(而不是带来任意的限制),而且它们没有导致安全问题的字符数组截断问题。但它们也具有自身的问题:在接受任意大小的数据时,可能会遇到内存不足的情况 ―― 而这在输入时也许不会发生。任何内存分配都可能会失败,而编写真正很好地处理该问题的 C 或 C++ 程序是很困难的。甚至在内存真正用完之前,也可能导致计算机变得太忙而不可用。简而言之,动态方法通常使得攻击者发起拒绝服务攻击变得更加容易。因此仍然需要限制输入。此外,必须小心设计程序来处理任意位置的内存耗尽问题,而这不是一件容易的事情。
标准 C 库方法最简单的方法之一是简单地使用那些设计用于防止缓冲区溢出的标准 C 库函数(即使在使用 C ++,这也是可行的),特别是                          strncpy(3) 和                         strncat(3) 。这些标准 C 库函数一般支持静态分配方法,也就是在数据无法装入缓冲区时丢弃它。这种方法的最大优点在于,您可以肯定这些函数在任何机器上都可用,并且任何 C/C++ 开发人员都会了解它们。许许多多的程序都是以这种方式编写的,并且确实可行。                  
遗憾的是,要正确地做到这点却是令人吃惊的困难。下面是其中的一些问题:
strncpy(3) 还存在一个恼人的性能问题。从理论上讲,                         strncpy(3) 是                         strcpy(3) 的安全替代者,但是                         strncpy(3) 还会在源字符串结束时使用 NUL 来填充整个目标空间。 这是很奇怪的,因为实际上并不存在这样做的很好理由,但是它从一开始就是这样,并且有些程序还依赖这个特性。这意味着从                          strcpy(3) 切换到                         strncpy(3) 会降低性能 ―― 这在如今的计算机上通常不是一个严重的问题,但它仍然是有害的。                  
那么可以使用标准 C 库的例程来防止缓冲区溢出吗?是的,不过并不容易。如果计划沿着这条路线走,您需要理解上述的所有要点。或者,您可以使用下面几节将要讲述的一种替代方法。
OpenBSD 的 strlcpy/strlcatOpenBSD 开发人员开发了一种不同的静态方法,这种方法基于他们开发的新函数                         strlcpy(3) 和                         strlcat(3) 。这些函数执行字符串复制和拼接,不过更不容易出错。这些函数的原型如下:                  
1
2
size_t strlcpy (char *dst, const char *src, size_t size);
size_t strlcat (char *dst, const char *src, size_t size);




strlcpy() 函数把以 NUL 结尾的字符串从“                         src ”复制到“                         dst ”(最多 size-1 个字符)。                         strlcat() 函数把以 NUL 结尾的字符串                         src 附加到                         dst 的结尾(但是目标中的字符数目将不超过 size-1)。                  
初看起来,它们的原型和标准 C 库函数并没有多大区别。但是事实上,它们之间存在一些显著区别。这些函数都接受目标的总大小(而不是剩余空间)作为参数。这意味着您不必连续地重新计算空间大小,而这是一项易于出错的任务。此外,只要目标的大小至少为 1,两个函数都保证目标将以 NUL 结尾(您不能将任何内容放入零长度的缓冲区)。如果没有发生缓冲区溢出,返回值始终是组合字符串的长度;这使得检测缓冲区溢出真正变得容易了。
遗憾的是,                         strlcpy(3) 和                         strlcat(3) 并不是在类 UNIX 系统的标准库中普遍可用。OpenBSD 和 Solaris 将它们内置在 <string.h> 中,但是 GNU/Linux 系统却不是这样。这并不是一件那么困难的事情;因为当底层系统没有提供它们时,您甚至可以将一些小函数直接包括在自己的程序源代码中。                  
SafeStrMessier 和 Viega 开发了“SafeStr”库,这是一种用于 C 的动态方法,它自动根据需要调整字符串的大小。使用                         malloc() 实现所使用的相同技巧,Safestr 字符串很容易转换为常规的 C“                         char * ”字符串:safestr 在传递指针“之前”的地址处存储重要信息。这种技术的优点在于,在现有程序中使用 SafeStr 将会很容易。SafeStr 还支持“只读”和“受信任”的字符串,这也可能是有用的。这种方法的一个问题在于它需要 XXL(这是一个给 C 添加异常处理和资源管理支持的库),因此您实际上要仅为了处理字符串而引入一个重要的库。Safestr 是在开放源代码的 BSD 风格的许可证下发布的。                  
C++ std::string针对 C++ 用户的另一种解决方案是标准的                         std::string 类,这是一种动态的方法(缓冲区根据需要而增长)。它几乎是不需要伤脑筋的,因为 C++ 语言直接支持该类,因此不需要做特殊的工作就可使用它,并且其他库也可能会使用它。就其本身而言,                         std::string 通常会防止缓冲区溢出,但是如果通过它提取一个普通 C 字符串(比如使用                         data() 或                         c_str() ),那么上面讨论的所有问题都会重新出现。还要记住                         data() 并不总是返回以 NUL 结尾的字符串。                  
由于种种历史原因,许多 C++ 库和预先存在的程序都创建了它们自己的字符串类。这可能使得                         std::string 更难于使用,并且在使用那些库或修改那些程序时效率很低,因为不同的字符串类型将不得不连续地来回转换。并非其他所有那些字符串类都会防止缓冲区溢出,并且如果它们对 C 不受保护的                         char* 类型执行自动转换,那么缓冲区溢出缺陷很容易引入那些类中。                  
工具有许多工具可以在缓冲区溢出缺陷导致问题之前帮助检测它们。 例如,像我的 Flawfinder 和 Viega 的 RATS 这样的工具能够搜索源代码,识别出可能被不正确地使用的函数(基于它们的参数来归类)。这些工具的一个缺点在于,它们不是完美的 ―― 它们会遗漏一些缓冲区溢出缺陷,并且它们会识别出一些实际上不是问题的“问题”。但是使用它们仍然是值得的,因为与手工查找相比,它们将帮助您在短得多的时间内识别出代码中的潜在问题。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0