哪个sprintf/snprintf更安全?

Arp*_*pit 40 c unix security printf secure-coding

我想知道这两个选项中哪一个更安全:

#define MAXLEN 255
char buff[MAXLEN + 1]
Run Code Online (Sandbox Code Playgroud)
  1. sprintf(buff, "%.*s", MAXLEN, name)

  2. snprintf(buff, MAXLEN, "%s", name)

我的理解是两者都是一样的.请建议.

aze*_*nik 35

你给出的两个表达式等价:sprintf没有参数指定要写入的最大字节数; 它只需要一个目标缓冲区,一个格式字符串和一堆参数.因此,它可能会写入比缓冲区空间更多的字节,并且这样写入任意代码.这%.*s不是一个满意的解决方案,因为:

  1. 当格式说明符指的是长度时,它指的是等价的strlen; 这是字符串中字符数的度量,而不是内存中的长度(即它不计算空终止符).
  2. 格式字符串中的任何更改(例如,添加换行符)都将更改sprintf版本相对于缓冲区溢出的行为.使用时snprintf,无论格式字符串或输入类型如何变化,都会设置固定的清除最大值.

  • 它确实如此,但它的可验证性要低得多 - 很难看到`sprintf`调用,并且一眼就能确定它将覆盖的内存的上限. (5认同)
  • 好吧,这一切都取决于OP的问题涉及哪种安全。正式地,在这种特定情况下,正确使用的“sprintf”与“snprintf”一样安全。您在这个答案中谈论的是缺乏对懒惰/无能程序员的保护。我不知道OP是否询问了这方面的安全问题。 (2认同)

Mic*_*urr 11

对于问题中的简单示例,两个调用之间的安全性可能没有太大差异.但是,在一般情况下snprintf()可能更安全.一旦你有一个具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保在不同的转换中准确计算缓冲区长度 - 特别是因为之前的转换不一定产生固定数字输出字符.

所以,我坚持下去snprintf().

另一个小优势snprintf()(虽然不是安全相关)是它会告诉你需要多大的缓冲区.

最后一点 - 你应该在snprintf()调用中指定实际的缓冲区大小- 它将为你处理空终止符的计算:

snprintf(buff, sizeof(buff), "%s", name);
Run Code Online (Sandbox Code Playgroud)

  • 我认为值得明确说明的是,对于最后一行代码,“buff”必须是堆栈上的数组。如果你有一个“char *”,它将不起作用。(嗯,它_可能_,但至少不会一直有效。)是的,它_在OP的情况下_有效,但对于使用它作为参考的新人来说,其原因很微妙。 (2认同)

Gio*_*iuc 8

最好和最灵活的方法是使用snprintf!

size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);
Run Code Online (Sandbox Code Playgroud)

在 C99 中,snprintf返回写入字符串的字节数,不包括'\0'. 如果少于所需的字节数,则snprintf返回扩展格式所需的字节数(仍不包括'\0')。通过传递snprintf一个长度为 0 的字符串,您可以提前找出扩展字符串的长度,并使用它来分配必要的内存。

  • 是的,这是最好的方法,我不知道为什么这个答案没有被投票到顶部。用户通常不知道他们可以将 NULL 作为第一个参数。 (2认同)

Kad*_*mir 6

snprintf()在我读这篇文章之前,我会说要好得多:

https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html

简短的总结是:snprintf()不可移动其行为从系统变为系统.最严重的问题snprintf()时,可能发生snprintf()通过调用简单地实现sprintf().你可能认为它可以保护你免受缓冲区溢出和放松警惕,但也可能不是.

所以现在我仍然说snprintf()更安全,但在使用它时也要谨慎.


Chr*_*odd 6

这两者之间有一个重要的区别——snprintf调用将扫描name参数到底(终止 NUL),以便找出正确的返回值。sprintf另一方面,该调用将从 中读取最多 255 个字符name

因此,如果name是指向至少包含 255 个字符的非 NUL 终止缓冲区的指针,则snprintf调用可能会超出缓冲区末尾并触发未定义的行为(例如崩溃),而版本则sprintf不会。


Eli*_*ser -1

两者都会给出您想要的结果,但snprintf更通用,并且无论给出的格式字符串如何,都会保护您的字符串免遭溢出。

此外,因为snprintf(或sprintf就此而言)添加了一个final \0,您应该使字符串缓冲区大一个字节,char buff[MAXLEN + 1].

  • 事实证明并非如此。来自 snprintf 文档:“函数 snprintf() 和 vsnprintf() 最多将 size 字节(包括终止空字节 ('\0'))写入 str。” (2认同)