哪个sprintf / snprintf更安全?

时间:2011-09-06 06:50:16

标签: c security unix printf secure-coding

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

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

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

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

7 个答案:

答案 0 :(得分:35)

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

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

答案 1 :(得分:10)

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

所以,我坚持使用snprintf()

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

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

snprintf(buff, sizeof(buff), "%s", name);

答案 2 :(得分:5)

我会说snprintf()要好得多,直到我读到这段话:

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

简短摘要是:snprintf()不可移植,其行为从系统变为系统。简单地通过调用snprintf()实现snprintf()时,sprintf()可能会出现最严重的问题。您可能认为它可以保护您免受缓冲区溢出的影响并让您放松警惕,但可能不会。 / p>

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

答案 3 :(得分:2)

你的sprintf声明是正确的,但是我没有足够的自信来使用它来达到安全目的(例如,缺少一个神秘的char并且你是无盾的),而周围的snprintf可以应用于任何格式。 ..哦等待 snprintf不在ANSI C 中。它(仅?)C99。这可能是一个(弱)理由而不喜欢另一个。

好。您也可以使用strncpy,不是吗?

e.g。

  char buffer[MAX_LENGTH+1];
  buffer[MAX_LENGTH]=0;             // just be safe in case name is too long
  strncpy(buffer,MAX_LENGTH,name);  // strncpy will never overwrite last byte

答案 4 :(得分:2)

最好和最灵活的方式是使用snprintf

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

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

答案 5 :(得分:2)

这两者之间存在重要差异 - snprintf调用将name参数扫描到结尾(终止NUL)以便找出正确的返回值。另一方面,sprintf调用将从name读取最多255个字符。

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

答案 6 :(得分:0)

两者都会给出你想要的结果,但是snprintf更通用,无论给出的格式字符串如何,都会保护你的字符串免于超支。

此外,由于snprintf(或sprintf)会添加最终\0,因此您应该将字符串缓冲区增大一个字节char buff[MAXLEN + 1]