C语言 sprintf/snprintf 哪个更安全?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7315936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Which of sprintf/snprintf is more secure?
提问by Arpit
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)
sprintf(buff, "%.*s", MAXLEN, name)snprintf(buff, MAXLEN, "%s", name)
My understanding is that both are same. Please suggest.
我的理解是两者都是一样的。请建议。
回答by azernik
The two expressions you gave are notequivalent: sprintftakes 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 %.*sis not a satisfactory solution because:
您给出的两个表达式并不等效:sprintf不指定要写入的最大字节数的参数;它只需要一个目标缓冲区、一个格式字符串和一堆参数。因此,它可能会写入比缓冲区空间更多的字节,并在这样做时写入任意代码。这%.*s不是一个令人满意的解决方案,因为:
- When the format specifier refers to length, it's referring to the equivalent of
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). - Any change in the format string (adding a newline, for example) will change the behavior of the
sprintfversion with respect to buffer overflows. Withsnprintf, a fixed, clear maximum is set regardless of changes in the format string or input types.
- 当格式说明符指的是长度时,它指的是等价于
strlen; 这是字符串中字符数的度量,而不是它在内存中的长度(即它不计算空终止符)。 - 格式字符串的任何更改(例如,添加换行符)都将更改
sprintf版本在缓冲区溢出方面的行为。使用snprintf,无论格式字符串或输入类型如何变化,都会设置一个固定的、明确的最大值。
回答by Michael Burr
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.
对于问题中的简单示例,两个调用之间的安全性可能没有太大差异。但是,在一般情况下snprintf()可能更安全。一旦您拥有具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保您在不同的转换中准确地考虑了缓冲区长度 - 特别是因为以前的转换不一定产生固定的数字输出字符。
So, I'd stick with snprintf().
所以,我会坚持snprintf()。
Another small advantage to snprintf()(though not security related) is that it'll tell you how big of a buffer you need.
另一个小优势snprintf()(尽管与安全无关)是它会告诉您需要多大的缓冲区。
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()调用中指定实际缓冲区大小- 它会为您处理空终止符的会计处理:
snprintf(buff, sizeof(buff), "%s", name);
回答by Kadir Erdem Demir
I would say snprintf()is much more better until I read this passage:
snprintf()在我阅读这段话之前,我会说要好得多:
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
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 with snprintf()can occur when snprintf()is implemented simply by calling sprintf().You may think it protects you from buffer overflow and let your guard down, but it may not.
简短的总结是:snprintf()不可移植其行为随系统而变化。最严重的问题snprintf()可能发生在snprintf()简单地通过调用实现时sprintf()。您可能认为它可以保护您免受缓冲区溢出的影响并放松警惕,但事实并非如此。
So now I am still saying snprintf()safer but also being cautious when I use it.
所以现在我仍然说snprintf()更安全,但在我使用它时也要谨慎。
回答by Giorgian Borca-Tasciuc
The best and most flexible way would be to use snprintf!
最好和最灵活的方法是使用snprintf!
size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the ' 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
' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);
In C99, snprintfreturns the number of bytes written to the string excluding the '\0'. If there were less than the necessary amount of bytes, snprintfreturns the number of bytes that would have been necessary to expand the format (still excluding the '\0'). By passing snprintfa 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.
在 C99 中,snprintf返回写入字符串的字节数,不包括'\0'. 如果少于所需的字节数,则snprintf返回扩展格式所需的字节数(仍不包括'\0')。通过传递snprintf一个长度为 0 的字符串,您可以提前找出扩展字符串的长度,并使用它来分配必要的内存。
回答by Chris Dodd
There's an important difference between these two -- the snprintfcall will scan the nameargument to the end (terminating NUL) in order to figure out the correct return value. The sprintfcall on the other hand will read AT MOST 255 characters from name.
这两者之间有一个重要的区别——snprintf调用将扫描name参数到末尾(终止 NUL)以找出正确的返回值。sprintf另一方面,调用将从中读取最多 255 个字符name。
So if nameis a pointer to a non-NUL terminated buffer with at least 255 characters, the snprintfcall might run off the end of the buffer and trigger undefined behavior (such as crashing), while the sprintfversion will not.
因此,如果name是指向至少 255 个字符的非 NUL 终止缓冲区的指针,则snprintf调用可能会在缓冲区末尾运行并触发未定义的行为(例如崩溃),而sprintf版本则不会。
回答by PypeBros
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.
您的 sprintf 声明是正确的,但我没有足够的自信将其用于安全目的(例如,缺少一个神秘字符并且您没有屏蔽),而周围有可以应用于任何格式的 snprintf ......哦等待snprintf 不在 ANSI C 中。它是(仅?)C99。这可能是选择另一个的(弱)理由。
Well. You could use strncpy, too, couldn't you ?
好。你也可以使用strncpy,不是吗?
e.g.
例如
##代码##回答by Eli Iser
Both will give the result you want, but snprintfis more generic, and will protect your string from overruns no matter the format string given.
两者都会给出您想要的结果,但snprintf更通用,并且无论给定的格式字符串如何,都会保护您的字符串免于溢出。
In addition, because snprintf(or sprintffor that matter) adds a final \0, you should make the string buffer one byte bigger, char buff[MAXLEN + 1].
此外,因为snprintf(或sprintf就此而言)添加了 final \0,您应该使字符串缓冲区大一个字节,char buff[MAXLEN + 1].

