编写安全C和安全C习语

Eth*_*man 41 c security exploit buffer-overflow

"普通人不想自由.他只是想要安全." - HL Menken

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

从流中读取:

根据GNU C编程教程 getline:

getline函数将根据需要通过realloc函数自动扩大内存块,因此永远不会缺少空间 - 这是getline如此安全的一个原因.[..]请注意,无论多长时间,getline都可以安全地处理您的输入线.

我假设getline应该在所有输入下防止从流中读取时发生缓冲区溢出.

  • 我的假设是否正确?是否存在可能导致漏洞利用的输入和/或分配方案?例如,如果流中的第一个字符是一些奇怪的控制字符,可能是0x08 BACKSPACE(ctl-H).
  • 有没有做过任何工作来数学证明getline是安全的?

Malloc在失败时返回Null:

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

/* 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. */
} 
Run Code Online (Sandbox Code Playgroud)

安全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);
Run Code Online (Sandbox Code Playgroud)

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

  • 我对么?有没有想到的奇怪的角落案件?
  • 有没有更好的方法来写这个?图书馆已经解决了吗?

一般的问题:

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

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

资源:

许多资源都是从答案中借来的.

non*_*ont 6

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

试试这个,它指定要读取的最大字节数:

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

看看这篇关于防止缓冲区溢出的IBM开发文章.

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

  • `void main`是错误的.在C中,`main`返回`int`. (6认同)
  • @wallyk`<code> sscanf(argv [0],"%*s",sizeof buf - 1,buf); </ code>`不起作用,在扫描的上下文中*是一个赋值抑制标记.见http://www.opengroup.org/onlinepubs/009695399/functions/scanf.html.任何想法如何做你的意图而不重写字符串在这里发布http://stackoverflow.com/questions/545018/how-to-pass-variable-length-width-specifier-in-sscanf (3认同)
  • 使用严重的重音字符(与代字号相同的键)包裹您的代码...这将允许您在评论中发布内联代码. (2认同)

gav*_*inb 4

  1. 从流中读取

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

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

  2. 扫描仪

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

  3. 普通的留言

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