我想知道这两种选项中哪一个更安全:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff, MAXLEN, "%s", name)
我的理解是这两者是相同的。请给予建议。
我想知道这两种选项中哪一个更安全:
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff, "%.*s", MAXLEN, name)
snprintf(buff, MAXLEN, "%s", name)
我的理解是这两者是相同的。请给予建议。
sprintf
不接受指定要写入的最大字节数的参数;它只需要一个目标缓冲区、一个格式化字符串和一些参数。因此,它可能会写入比你的缓冲区还要多的字节,并在这样做时写入任意代码。 %.*s
不是一个令人满意的解决方案,因为:
strlen
的长度;这是字符串中字符数的度量,而不是其在内存中的长度(即它不计算空终止符)。sprintf
版本相对于缓冲区溢出的行为。使用snprintf
,无论格式字符串或输入类型如何改变,都设置了一个固定的、明确的最大值。\0
)。我已相应地编辑了我的答案。 - Eli Isersprintf
被正确使用,则在此特定情况下与snprintf
一样安全。您在本答案中所说的是对懒惰/不称职程序员的保护不足。我不知道OP是否正在问及安全方面的这一方面。 - AnT stands with Russianame
是由不受信任的用户提供的;这是唯一可能存在安全问题的情况,而且在这种情况下,它是相当严重的。使用一个非常糟糕的 sprintf
格式字符串,恶意用户可以仔细地构造他们的输入,覆盖堆栈并执行任意代码;而使用这个格式字符串,恶意用户只能用零(空终止符)覆盖堆栈上的一个字节,这可能是一种相当有效的 DOS 攻击。 - azerniksnprintf
相同级别的保护(再次特别应用于字符串输入)。 - AnT stands with Russiasprintf
调用会覆盖多少内存上限。 - azernik最好、最灵活的方法是使用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'
)。通过传递长度为0的字符串给snprintf
,您可以提前了解扩展后字符串的长度,并使用它来分配必要的内存。对于问题中的简单示例,这两个调用之间可能并没有太大的安全差异。然而,在一般情况下,snprintf()
可能更加安全。一旦您有了一个更复杂的格式字符串,并且其中包含多个转换说明符,就很难(或者几乎不可能)确保您在不同的转换说明符中准确地考虑了缓冲区长度,特别是因为前面的转换说明符不一定会产生固定数量的输出字符。
所以,我建议坚持使用 snprintf()
。
snprintf()
的另一个小优点(虽然与安全无关)是它会告诉您需要多大的缓冲区。
最后要注意的一点是,您应该在 snprintf()
调用中指定实际的缓冲区大小 - 它会为您处理空终止符:
snprintf(buff, sizeof(buff), "%s", name);
buff
必须是堆栈上的数组。如果您有一个 char *
,它将无法工作。(嗯,它可能会,但至少不总是有效。)是的,它在 OP 的情况下可以工作,但原因对于使用此作为参考的新人来说是微妙的。 - anon在读到这段话之前,我会说snprintf()
更好用。
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
简短地说,snprintf()
不具可移植性,其行为在系统之间可能会发生变化。最严重的问题是当 snprintf()
仅通过调用 sprintf()
实现时可能会发生的。你可能认为它可以保护你免受缓冲区溢出的影响,从而放松警惕,但实际上并非如此。
因此,我现在仍然认为使用snprintf()
更安全,但在使用时也要谨慎小心。
snprintf
在 ISO C 99 中被指定,这比本答案早了14年,比今天早了24年。引用的2007年参考文献正确地说明了 snprintf
不在 C90 中,并且有不同的变体。但是...那又怎样? - Kaz这两者之间有一个重要的区别--snprintf
调用将扫描name
参数直到末尾(终止NUL),以确定正确的返回值。另一方面,sprintf
调用将从name
中读取最多255个字符。
所以如果name
是指向非NUL终止缓冲区且至少有255个字符的指针,则snprintf
调用可能会超过缓冲区的末尾并触发未定义行为(例如崩溃),而sprintf
版本则不会。
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
%.*s
在 ANSI C 中可用吗?我尝试查找规范,但只找到了一个(不可靠的)参考,没有指定 .*
。 - Eli Isersnprintf()
是在 C99 标准中添加的。 - Michael Burr%s
)也适用吗?这个好知道,谢谢。 - Eli Iserstrncpy
不是他想要的,请在此处搜索strlcpy
以了解原因。例如:https://dev59.com/l3I95IYBdhLWcg3w3iDG#2115015 - quinmarssnprintf(char * restrict str, size_t size, const char * restrict format, ...);
两种方法都可以得到你想要的结果,但是snprintf
更通用,并且无论给定的格式字符串如何,都会保护你的字符串免受溢出的影响。
此外,因为snprintf
(或者说sprintf
)会添加一个最终的\0
,所以你应该将字符串缓冲区增加一个字节,即char buff[MAXLEN + 1]
。
MAXLEN+1
,它们在所有情况下写入buff
中的内容将是相同的(如果strlen(name)>255
,则返回值将不同)。 - Chris Dodd