sprintf/snprintf 哪个更安全?
我想知道这两个选项中哪一个更安全:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff,MAXLEN,“%s”,名称)
我的理解是两者是相同的。请建议。
I wish to know which of these two options is the more secure one to use:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff, MAXLEN, "%s", name)
My understanding is that both are same. Please suggest.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您给出的两个表达式不等效:
sprintf
不接受指定要写入的最大字节数的参数;它只需要一个目标缓冲区、一个格式字符串和一堆参数。因此,它可能会写入比缓冲区空间更多的字节,从而编写任意代码。%.*s
不是一个令人满意的解决方案,因为:strlen
的等效项;这是字符串中字符数量的度量,而不是它在内存中的长度(即它不计算空终止符)。sprintf
版本在缓冲区溢出方面的行为。使用snprintf
,无论格式字符串或输入类型如何变化,都会设置一个固定、明确的最大值。The two expressions you gave are not equivalent:
sprintf
takes no argument specifying the maximum number of bytes to write; it simply takes a destination buffer, a format string, and a bunch of arguments. Therefore, it may write more bytes than your buffer has space for, and in so doing write arbitrary code. The%.*s
is not a satisfactory solution because:strlen
; this is a measure of the number of characters in the string, not its length in memory (i.e. it doesn't count the null terminator).sprintf
version with respect to buffer overflows. Withsnprintf
, a fixed, clear maximum is set regardless of changes in the format string or input types.最好、最灵活的方法是使用
snprintf
!在 C99 中,
snprintf
返回写入字符串的字节数(不包括'\0'
)。如果少于所需的字节数,snprintf
将返回扩展格式所需的字节数(仍不包括'\0'
)。通过向snprintf
传递一个长度为 0 的字符串,您可以提前知道扩展后的字符串有多长,并使用它来分配必要的内存。The best and most flexible way would be to use
snprintf
!In C99,
snprintf
returns the number of bytes written to the string excluding the'\0'
. If there were less than the necessary amount of bytes,snprintf
returns the number of bytes that would have been necessary to expand the format (still excluding the'\0'
). By passingsnprintf
a string of 0 length, you can find out ahead of time how long the expanded string would have been, and use it to allocate the necessary memory.对于问题中的简单示例,两个调用之间的安全性可能没有太大差异。然而,在一般情况下,
snprintf()
可能更安全。一旦您拥有具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保您在不同的转换中准确地考虑了缓冲区长度 - 特别是因为以前的转换不一定会产生固定的数字输出字符数。所以,我会坚持使用
snprintf()
。snprintf()
的另一个小优点(尽管与安全无关)是它会告诉您需要多大的缓冲区。最后一点 - 您应该在
snprintf()
调用中指定实际的缓冲区大小 - 它将为您处理空终止符的计算:For the simple example in the question, there might not be much difference in the security between the two calls. However, in the general case
snprintf()
is probably more secure. Once you have a more complex format string with multiple conversion specifications it can be difficult (or near impossible) to ensure that you have the buffer length accounted for accurately across the different conversions - especially since a previous conversions don't necessarily produce a fixed number of output characters.So, I'd stick with
snprintf()
.Another small advantage to
snprintf()
(though not security related) is that it'll tell you how big of a buffer you need.A final note - you should specify the actual buffer size in the
snprintf()
call - it'll handle accounting for the null terminator for you:我想说
snprintf()
更好,直到我读到这段话:https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
简短摘要是:
snprintf()
其行为不可移植从一个系统到另一个系统的变化。当仅通过调用sprintf()
实现snprintf()
时,可能会出现snprintf()
最严重的问题。您可能认为它可以保护您避免缓冲区溢出并放松警惕,但事实可能并非如此。所以现在我仍然说
snprintf()
更安全,但在使用它时也要谨慎。I would say
snprintf()
is much more better until I read this passage:https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
Short summary is:
snprintf()
not portable its behaviour change from system to system. The most serious problem withsnprintf()
can occur whensnprintf()
is implemented simply by callingsprintf()
.You may think it protects you from buffer overflow and let your guard down, but it may not.So now I am still saying
snprintf()
safer but also being cautious when I use it.这两者之间有一个重要的区别 -
snprintf
调用将扫描name
参数到底(终止 NUL),以便找出正确的返回值。另一方面,sprintf
调用将从name
读取最多 255 个字符。因此,如果
name
是指向至少包含 255 个字符的非 NUL 终止缓冲区的指针,则snprintf
调用可能会超出缓冲区末尾并触发未定义的行为(例如崩溃),而 sprintf 版本则不会。There's an important difference between these two -- the
snprintf
call will scan thename
argument to the end (terminating NUL) in order to figure out the correct return value. Thesprintf
call on the other hand will read AT MOST 255 characters fromname
.So if
name
is a pointer to a non-NUL terminated buffer with at least 255 characters, thesnprintf
call might run off the end of the buffer and trigger undefined behavior (such as crashing), while thesprintf
version will not.你的 sprintf 声明是正确的,但我没有足够的自信将其用于安全目的(例如,缺少一个神秘的字符,你就无盾了),而周围有 snprintf 可以应用于任何格式......哦等等snprintf 不是 ANSI C。它是(仅?)C99。这可能是选择另一个的(微弱的)理由。
出色地。您也可以使用
strncpy
,不是吗?例如
Your sprintf statement is correct, but I'd not be self-confident enough to use that for safety purpose (e.g. missing one cryptic char and you're shieldless) while there is snprintf around that can be applied to any format ... oh wait snprintf is not in ANSI C. It is (only?) C99. That could be a (weak) reason to prefer the other one.
Well. You could use
strncpy
, too, couldn't you ?e.g.
所以这是在搜索 sprintf 与 snprintf 时出现的第一个与堆栈溢出相关的问题。所以我认为这是添加这个答案的最佳位置。
看来现在不能在 OSX 13.3 上使用 sprintf 了,因为我收到了这些警告。
所以我将它们全部转换为
这非常简单,并且使用 snprintf 与 sprintf 总是更安全。
So this was the first stack overflow related question that came up when searching for sprintf vs snprintf. So I figured this was the best place to add this answer.
It seems it's not an option to use sprintf on OSX 13.3 now because I'm getting these warnings.
So I went through and converted them all to
It was pretty straight forward and it's always safer to use snprintf vs sprintf.
两者都会给出您想要的结果,但
snprintf
更通用,并且无论给出的格式字符串如何,都会保护您的字符串免遭溢出。此外,由于
snprintf
(或sprintf
)添加了最后一个\0
,因此您应该使字符串缓冲区大一个字节,<代码>char buff[MAXLEN + 1]。Both will give the result you want, but
snprintf
is more generic, and will protect your string from overruns no matter the format string given.In addition, because
snprintf
(orsprintf
for that matter) adds a final\0
, you should make the string buffer one byte bigger,char buff[MAXLEN + 1]
.