“普通人不想自由。他只是想安全。” - HL Menken

我正在尝试写非常安全的C。在下面,我列出了我使用的一些技术,并问它们像我认为一样安全。请不要毫不犹豫地将我的代码/先入为主撕成碎片。任何发现最琐碎的脆弱性或教给我新想法的答案都将是 高度重视.

从流来阅读:

根据 GNU C编程教程 getline:

GETLINE功能将根据需要自动扩大内存块,因此永远不会缺少空间 - Getline如此安全的原因之一。 [..]请注意,Getline可以安全地处理您的输入线,无论它有多长时间。

我认为getline应该 在所有输入下, ,防止 缓冲区溢出 从从流中阅读时发生。

  • 我的假设正确吗?是否存在可能导致利用的输入和/或分配方案?例如,如果流的第一个字符是某些 奇异的控制字符, ,也许是0x08 backspace(CTL-H)。
  • 是否有任何工作以数学证明是安全的?

Malloc在失败时返回无效:

如果Malloc遇到错误Malloc将返回空指针。这会带来安全风险,因为人们仍然可以将指针算术应用于null(0x0)指针,因此Wikipedia 推荐

/* 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是输入str的大小,并且可以从Inputstr读取strlen(Inputstr)的字符更多,所以似乎不可能,给定 所有可能的值 让输入量引起缓冲区溢出?

  • 我对么?有我没有想到的奇怪角案吗?
  • 有更好的写作方法吗?已经解决了它的库?

一般的问题:

尽管我发布了许多问题,但我不希望有人回答所有问题。这些问题是我想要的各种答案的指南。我真的很想学习安全的心态。

  • 那里还有哪些安全的C习惯?
  • 我需要什么角案 总是检查?
  • 如何编写单元测试来执行这些规则?
  • 如何以可测试性或可证明正确的方式执行约束?
  • 任何推荐的静态/动态分析技术还是C的工具?
  • 您遵循哪些安全的C练习,以及如何为自己和他人证明它们是合理的?

资源:

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

有帮助吗?

解决方案

  1. 从流读

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

此外(正如您提及特殊角色所预期的那样), getline() 只给您一个缓冲区;它不能保证缓冲区的内容(因为安全完全取决于应用程序)。因此,对输入进行消毒仍然是处理和验证用户数据的重要组成部分。

  1. SSCANF

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

  1. 普通的留言

    • 有模糊工具可生成随机输入(有效和无效),可用于测试输入处理
    • 缓冲区管理至关重要:缓冲区的溢出,下水下,内存外
    • 可以用否则安全的代码来利用比赛条件
    • 可以操纵二进制文件以将无效的值或超大值注入标题,因此文件格式代码必须是岩石固体的,并且不要假设二进制数据有效
    • 临时文件通常可以成为安全问题的来源,必须仔细管理
    • 代码注入可用于用恶意版本替换系统或运行时库呼叫
    • 插件为攻击提供了巨大的向量
    • 总的来说,我建议在用户数据(或应用程序外部的任何数据)中明确定义接口,直到对其进行处理,消毒和验证,并且用户数据输入应用程序的唯一方法是

其他提示

我认为您的SSCANF示例是错误的。使用这种方式,它仍然可以溢出。

尝试此方法,它指定要读取的最大字节数:

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

看一下IBM开发文章,内容涉及防止缓冲区溢出。

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

一个开始看这个的好地方是 戴维·惠勒(David Wheeler)出色的安全编码网站.

他的免费在线书”为Linux和Unix Howto提供安全编程“是定期更新的绝佳资源。

您可能还想看他的出色静态分析仪 缺点 要获得一些提示。但是请记住,没有自动化工具可以替代一双经验丰富的眼睛,或者像大卫如此彩色的那样。

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

我亲自使用了大卫的资源已有几年了,发现它们非常出色。

Yannick Moy在他的博士学位期间为C开发了C的Hoare/Floyd最弱的前提系统 将其应用于CERT托管库. 。他发现了许多虫子(请参阅他的回忆录的第197页)。好消息是,图书馆现在的工作更加安全。

您也可以查看Les Hatton的网站 这里 在他的书中 更安全c 您可以从亚马逊获得。

不要使用 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 确定第二个论点,并且更清楚。


检查返回值 fclose().


许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top