编写安全C和安全C习语

时间:2010-01-05 18:26:33

标签: c security buffer-overflow exploit

  

“普通人不想自由。他只是想要安全。” - H. L. Menken

我正在尝试编写非常安全的C.下面我列出了一些我使用的技术,并且问我们是否像我认为的那样安全。请不要犹豫将我的代码/先入之见撕成碎片。任何能找到最微不足道的漏洞或者教会我一个新想法的答案都将被高度重视

从流中读取:

根据GNU C Programming Tutorial getline:

  

getline函数会   自动放大块   根据需要通过realloc记忆   功能,所以永远不会缺货   空间 - getline的一个原因   太安全了[..]请注意,getline可以   安全地处理你的输入线,没有   不管它有多长。

我认为getline应该在所有输入下,防止在从流中读取时发生buffer overflow

  • 我的假设是否正确?是否存在可能导致漏洞利用的输入和/或分配方案?例如,如果流中的第一个字符是bizarre control character,可能是0x08 BACKSPACE(ctl-H)。
  • 是否已做过任何工作以数学方式证明getline是安全的?

Malloc在失败时返回Null:

如果malloc遇到错误,malloc将返回NULL指针。这会带来安全风险,因为仍然可以将指针算法应用于NULL(0x0)指针,因此维基百科recommends

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

安全sscanf:

当使用sscanf时,我已经养成了将要提取的字符串分配给输入字符串大小的习惯,希望避免出现溢出的可能性。例如:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

因为str1和str2是inputStr的大小,并且不能从inputStr读取比strlen(inputStr)更多的字符,所以看起来不可能,因为inputStr的所有可能值导致缓冲区溢出?

  • 我说错了吗?是否有我没想过的奇怪的角落案件?
  • 有没有更好的方法来写这个?已经解决了它的图书馆?

一般问题:

虽然我发布了大量问题,但我不希望任何人回答所有这些问题。这些问题更多地是我正在寻找的各种答案的指南。我真的很想学习安全的C语言。

  • 还有哪些其他安全的C成语?
  • 始终检查需要哪些角落?
  • 如何编写单元测试来强制执行这些规则?
  • 如何以可测试性或可证明的正确方式强制执行约束?
  • C的任何推荐的静态/动态分析技术或工具?
  • 您遵循哪些安全的C实践,以及如何为自己和他人辩护?

的资源:

许多资源都来自答案。

7 个答案:

答案 0 :(得分:6)

我认为你的sscanf例子是错误的。用这种方式它仍然会溢出。

尝试此操作,指定要读取的最大字节数:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

Take a look at this IBM dev article about protecting against buffer overflows.

在测试方面,我会编写一个程序,生成随机长度的随机字符串并将它们提供给您的程序,并确保它们得到适当的处理。

答案 1 :(得分:4)

开始查看此内容的好地方是David Wheeler's excellent secure coding site

他的免费在线图书“Secure Programming for Linux and Unix HOWTO”是一个定期更新的优秀资源。

您可能还想看看他出色的静态分析器FlawFinder以获得更多提示。但是请记住,没有一个自动化工具可以替代一双经验丰富的眼睛,或者像大卫那样慷慨地说它。

  

任何静态分析工具,例如Flawfinder,都只是一种工具。没有工具可以替代人类思想!简而言之,“带工具的傻瓜仍然是个傻瓜”。认为分析工具(如flawfinder)可以替代安全培训和知识是错误的

我已经亲自使用David的资源已经好几年了,并且发现它们非常出色。

答案 2 :(得分:4)

  1. 从流中读取
  2. getline()“将根据需要自动扩大内存块这一事实”意味着这可以用作拒绝服务攻击,因为生成一个如此长的输入是微不足道的它会耗尽进程的可用内存(或者更糟糕的是系统!)。一旦出现内存不足的情况,其他漏洞也可能会发挥作用。低/无内存中的代码行为很少很好,很难预测。恕我直言,为所有事情设置合理的上限更安全,特别是在安全敏感的应用程序中。

    此外(正如您通过提及特殊字符所预期的那样),getline()仅为您提供缓冲区;它不保证缓冲区的内容(因为安全性完全取决于应用程序)。因此,对输入进行清理仍然是处理和验证用户数据的重要部分。

    1. sscanf的
    2. 我倾向于使用正则表达式库,并且为用户数据定义非常精确的regexp,而不是使用sscanf。这样,您可以在输入时执行大量验证。

      1. 一般评论

        • 可以使用模糊测试工具生成可用于测试输入处理的随机输入(有效和无效)
        • 缓冲区管理至关重要:缓冲区溢出,下溢,内存不足
        • 可以在其他安全代码中利用竞争条件
        • 可以操纵二进制文件,将无效值或超大值注入标题,因此文件格式代码必须坚如磐石,不要假设二进制数据有效
        • 临时文件通常可能是安全问题的根源,必须小心管理
        • 代码注入可用于用恶意版本替换系统或运行时库调用
        • 插件为攻击提供了巨大的载体
        • 作为一般原则,我建议明确定义接口,其中用户数据(或来自应用程序外部的任何数据)被假定为无效和恶意,直到它被处理,消毒和验证,以及用户数据进入的唯一方式申请

答案 3 :(得分:1)

答案 4 :(得分:1)

Yannick Moy在他的博士和applied it to the CERT managed strings library期间为C开发了一个Hoare / Floyd最弱的预处理系统。他发现了许多错误(见他的回忆录第197页)。好消息是图书馆现在更安全。

答案 5 :(得分:1)

您还可以查看Les Hatton的网站here以及您可以从亚马逊获得的更安全的C 这本书。

答案 6 :(得分:0)

请勿使用gets()进行输入,请使用fgets()。要使用fgets(),如果你的缓冲区是自动分配的(即“在堆栈上”),那么使用这个习语:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

如果您决定更改buf的大小,这将继续有效。我更喜欢这种形式:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

因为第一个表单使用buf来确定第二个参数,并且更清晰。


Check the return value of fclose().