为什么gets()比scanf()更危险?

6

在我看来,两者都有可能导致缓冲区溢出。但是我被建议不要使用gets(),但仍然鼓励使用scanf()。

这是因为scanf()允许格式化参数,还是有其他原因呢?


由于转义字符的存在,gets() 函数无法过滤掉末尾的换行符。 - noMAD
3
可能是因为gets()函数存在缺陷,但如果你使用scanf,并且每次都限制输入的长度,就可以避免缓冲区溢出问题。 - loreb
如果我没记错的话,gets()函数在没有遇到换行符\n时不会返回输入的内容,而是返回EOF - pampeho
6个回答

10

gets 函数没有受到缓冲区溢出的保护。

使用 scanf 格式字符串,您可以定义从标准输入读取并存储在给定内存缓冲区中的字符串的最大长度。例如,使用 scanf("%10s\n", str); 将读取最多 10 个字符。需要将 str 缓冲区大小设置为 11 字节以存储 NULL 结束符。

就性能而言,如果您只是使用 scanf 来解决 gets 的缓冲区溢出问题,请优先使用 fgets 函数。


在做一些研究时,我还发现:scanf() 不仅允许您输入多个字段,而且还允许您输入结构化数据,包括指定出现在字段之间的字符。因此,我认为这也是值得记住的有趣信息。来源:《Head First C》, Daivid & Dawn Griffiths. - Dagoth Ulen
你是对的,scanfgets(或 fgets)并不涵盖相同的用例。你使用 fgets 从输入流中读取一行。你使用 scanf 解析输入流中的数据。话虽如此,你可以使用 scanf 从输入流中读取一行,就像使用 fgets 一样,但这会导致性能损失。 - greydet

2
因为您可以输入比缓冲区大小更多的字符,而gets()将乐意允许它。此外,gets()已被弃用(在C11中)。因此,与scanf()的比较不再有效。此外,scanf()在处理未格式化数据时也存在问题。
因此,更好的选择是使用fgets(),然后根据需要进行处理。

1
由于没有办法防止缓冲区溢出,因此指定最大缓冲区长度的 fgets 更加安全。

4
问题实际上是关于 getsscanf 之间的区别。 - Fred Foo

1

1

仍然鼓励使用scanf()。

快速搜索“scanf安全性”会出现约212k个结果。第一个是维基百科,其中指出:

不带长度说明符的%s占位符的用法本质上是不安全的,并且容易受到缓冲区溢出的攻击。

因此,在网络上有足够的关于scanf安全性的信息。与gets的区别在于,scanf具有安全用途,而使用gets几乎总是不明智的。


1
gets()的名义任务是从流中读取一个字符串。调用者告诉它要把输入的字符放在哪里。但是gets()不检查缓冲区空间。如果调用者提供了指向栈的指针,并且输入量超过了缓冲区空间,gets()将覆盖栈。大多数系统容易被覆盖堆栈中间现有条目的更大的东西所覆盖,并覆盖相邻的条目。恶意分子可以通过在参数字符串中藏入正确的二进制模式来修改堆栈上的过程激活记录中的返回地址。这将使执行的流不是返回到它来自的地方,而是转到一个特殊的指令序列,该序列调用execv()以用一个shell替换正在运行的映像。这个shell可以发出命令,将病毒拖到系统上。因此,gets()不安全。

1
这并没有解释为什么scanf更安全。 - jamesdlin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接