【问题标题】:Is it more secure to add a length specifier to a printf call在 printf 调用中添加长度说明符是否更安全
【发布时间】:2013-02-23 03:28:28
【问题描述】:

我的问题是从安全角度出发的。我正在清理一些代码,我发现当前代码存在不受控制的格式字符串漏洞;它将一个字符串传递给printf(),类似于:

void print_it(char * str)
{
    printf(str);

这显然被认为是不安全的编程,即使 gcc 通常也会给你一些警告:

警告:格式不是字符串文字,也没有格式参数

现在要“修复”这个问题,我们可以确保您得到的内容被视为字符串

printf("%s", str);

但我想知道是否有任何...使用长度指定器的额外安全性。比如:

printf("%.*s", (int)sizeof(str), str);

我想不出有什么理由会更安全,但如果我在这里遗漏了一些明显的东西,我也不会感到惊讶。

【问题讨论】:

  • 问题是“你为什么需要这个”...

标签: c security printf format-string


【解决方案1】:

有点,但不是现代 C 商店安全地保护他们的 printf 语句的程度。当您处理非空终止字符串时使用它,这在与 Fortran 代码交互时非常常见。

严格来说,为了防止读取失控,这将是一种安全增益,也许您正要在违反空字符后打印敏感数据。

但是

printf("%.*s", (int)sizeof(str), str);

更糟;您刚刚说“忽略空字符并打印出全部内容”。或者更确切地说,除非您一直处理空格填充的字符串直到它们的内存结束,否则情况会更糟,如果字符串来自 Fortran,这很可能就是这种情况。

然而非常很重要:

printf("%s", str);

printf(str) 是一个重大的安全漏洞。使用%n 说明符了解printf attacks写入

【讨论】:

    【解决方案2】:

    还有一些额外的安全性

    printf("%.*s", (int)sizeof(str), str);
    

    因为它最多会打印sizeof(char*) 字节——通常是四个或八个字节——所以如果str 指向一个不是0 终止的char 数组,它就不会去读取大部分内存。

    但更典型的是,它会在没有充分理由的情况下缩短输出。

    如果你的意思是

    printf("%.*s", (int)strlen(str), str);
    

    这完全没有意义,因为在printf 需要精确的情况下,strlen 调用将执行相同的无效内存访问。

    【讨论】:

      【解决方案3】:

      当数组被传递给函数时,这个想法将不起作用,因为数组衰减为指针或任何 malloc 的指针。因为sizeof(var) 将给出指针的大小,而不是数组。所以不能在 printf() 中使用来指定长度。

      这仅适用于自动(堆栈分配)数组。那么这什么时候有用呢?我可以想到两种情况:

      1.当你写入数组超过数组的大小时。

      在这种情况下,你已经在不属于你的地方写了未定义的行为。故事结束。

      2。当字符串末尾没有空字节时(但没有越过数组的边界)。

      在这种情况下,数组有一些有效的内容,但不是空字节。这里有两种可能性:

      2.a. 使用长度说明符将打印数组的全部内容。因此,如果您访问数组中未初始化的字节(即使在其大小范围内),它仍然会导致未定义的行为。否则,您必须跟踪有效内容的长度,以便在 printf() 中使用 %s 以及长度说明符以避免 UB。在这种情况下,您已经知道有效内容的长度。因此,您可以简单地自己终止它,而不是告诉 printf() 打印 only 有效内容。

      2.b. 假设您已经用零初始化了整个数组。在这种情况下,数组将是一个有效的 string,因此不需要长度修饰符。

      所以我会说它没有多大用处。

      【讨论】:

        猜你喜欢
        • 2013-10-09
        • 2017-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-07
        • 2017-11-16
        • 2013-07-22
        相关资源
        最近更新 更多