56

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

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

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

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

4

7 回答 7

44

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

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

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

所以,我会坚持snprintf().

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

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

snprintf(buff, sizeof(buff), "%s", name);
于 2011-09-06T07:42:35.627 回答
11

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

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

简短的总结是:snprintf()不可移植它的行为从系统到系统的变化。最严重的问题snprintf()可能发生在snprintf()仅通过调用实现时sprintf()。您可能认为它可以保护您免受缓冲区溢出并放松警惕,但事实并非如此。

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

于 2013-01-24T14:41:59.187 回答
9

最好和最灵活的方法是使用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 的字符串,您可以提前了解扩展字符串的长度,并使用它来分配必要的内存。

于 2016-02-15T05:09:05.117 回答
3

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

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

于 2017-08-22T16:31:33.217 回答
2

您的 sprintf 声明是正确的,但我没有足够的自信将其用于安全目的(例如,缺少一个神秘字符并且您是无屏蔽的),而周围有可以应用于任何格式的 snprintf ......哦等待snprintf 不在 ANSI C中。它是(仅?)C99。这可能是偏爱另一个的(弱)理由。

出色地。你也可以用strncpy,不是吗?

例如

  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
于 2011-09-06T07:20:20.083 回答
-1

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

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

于 2011-09-06T06:56:32.030 回答