sprintf/snprintf 哪个更安全?

发布于 2024-12-03 06:08:34 字数 254 浏览 2 评论 0原文

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

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

  2. 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]
  1. sprintf(buff, "%.*s", MAXLEN, name)

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

My understanding is that both are same. Please suggest.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

美胚控场 2024-12-10 06:08:34

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

  1. 当格式说明符引用长度时,它引用的是 strlen 的等效项;这是字符串中字符数量的度量,而不是它在内存中的长度(即它不计算空终止符)。
  2. 格式字符串中的任何更改(例如添加换行符)都将更改 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:

  1. 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).
  2. Any change in the format string (adding a newline, for example) will change the behavior of the sprintf version with respect to buffer overflows. With snprintf, a fixed, clear maximum is set regardless of changes in the format string or input types.
一曲爱恨情仇 2024-12-10 06:08:34

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

The best and most flexible way would be to use snprintf!

size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);

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 passing snprintf 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.

白云不回头 2024-12-10 06:08:34

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

所以,我会坚持使用 snprintf()

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

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

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

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(buff, sizeof(buff), "%s", name);
与他有关 2024-12-10 06:08:34

我想说 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 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.

So now I am still saying snprintf() safer but also being cautious when I use it.

や三分注定 2024-12-10 06:08:34

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

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

There's an important difference between these two -- the snprintf call will scan the name argument to the end (terminating NUL) in order to figure out the correct return value. The sprintf call on the other hand will read AT MOST 255 characters from name.

So if name is a pointer to a non-NUL terminated buffer with at least 255 characters, the snprintf call might run off the end of the buffer and trigger undefined behavior (such as crashing), while the sprintf version will not.

胡渣熟男 2024-12-10 06:08:34

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

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.

  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
一抹微笑 2024-12-10 06:08:34

所以这是在搜索 sprintf 与 snprintf 时出现的第一个与堆栈溢出相关的问题。所以我认为这是添加这个答案的最佳位置。

看来现在不能在 OSX 13.3 上使用 sprintf 了,因为我收到了这些警告。

'sprintf' 已弃用:提供此函数是为了兼容性
仅原因。由于设计中固有的安全考虑
sprintf(3),强烈建议您使用 snprintf(3)。
[-Werror,-Wdeprecated-声明]

所以我将它们全部转换为

snprintf(char * restrict str, size_t size, const char * restrict format, ...);

这非常简单,并且使用 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.

'sprintf' is deprecated: This function is provided for compatibility
reasons only. Due to security concerns inherent in the design of
sprintf(3), it is highly recommended that you use snprintf(3) instead.
[-Werror,-Wdeprecated-declarations]

So I went through and converted them all to

snprintf(char * restrict str, size_t size, const char * restrict format, ...);

It was pretty straight forward and it's always safer to use snprintf vs sprintf.

扭转时空 2024-12-10 06:08:34

两者都会给出您想要的结果,但 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 (or sprintf for that matter) adds a final \0, you should make the string buffer one byte bigger, char buff[MAXLEN + 1].

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文