sprintf/snprintf哪个更安全?

问题描述 投票:0回答:7

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

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

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

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

c security unix printf secure-coding
7个回答
47
投票

您给出的两个表达式等价:

sprintf
不带参数指定要写入的最大字节数;它只需要一个目标缓冲区、一个格式字符串和一堆参数。因此,它可能会写入比您的缓冲区空间更多的字节,并在这样做时写入任意代码。
%.*s
不是一个令人满意的解决方案,因为:

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

23
投票

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

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 的字符串,您可以提前找出扩展字符串的长度,并使用它来分配必要的内存。


15
投票

对于问题中的简单示例,两个调用之间的安全性可能没有太大差异。但是,在一般情况下,

snprintf()
可能更安全。一旦您拥有具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保您在不同的转换中准确计算缓冲区长度 - 特别是因为以前的转换不一定产生固定的数字输出字符。

所以,我会坚持

snprintf()
.

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

最后一点——您应该在

snprintf()
调用中指定实际缓冲区大小——它会为您处理空终止符:

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

12
投票

我会说

snprintf()
好多了,直到我读到这段话:

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

简短的总结是:

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

所以现在我还是说

snprintf()
更安全但是用的时候也要小心


6
投票

这两者之间有一个重要的区别——

snprintf
调用将扫描
name
参数直到结束(终止 NUL),以便找出正确的返回值。另一方面,
sprintf
调用将从
name
读取最多 255 个字符。

因此,如果

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


3
投票

你的 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

-1
投票

两者都会给出您想要的结果,但

snprintf
更通用,无论给出的格式字符串如何,都会保护您的字符串免于溢出。

此外,因为

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

© www.soinside.com 2019 - 2024. All rights reserved.