我想知道这两个选项中哪一个更安全:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff, MAXLEN, "%s", name)
我的理解是两者都是一样的。请建议。
答案 0 :(得分:35)
你给出的两个表达式是 not 等价物:sprintf
没有指定要写入的最大字节数的参数;它只需要一个目标缓冲区,一个格式字符串和一堆参数。因此,它可能会写入比缓冲区空间更多的字节,并且这样写入任意代码。 %.*s
不是一个令人满意的解决方案,因为:
strlen
;这是字符串中字符数的度量,而不是内存中的长度(即它不计算空终止符)。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]
。