C 哪个sprintf/snprintf更安全?
我想知道这两个选项中哪一个更安全:C 哪个sprintf/snprintf更安全?,c,security,unix,printf,secure-coding,C,Security,Unix,Printf,Secure Coding,我想知道这两个选项中哪一个更安全: #define MAXLEN 255 char buff[MAXLEN + 1] sprintf(buff,'%s',MAXLEN,name) snprintf(buff,MAXLEN,“%s”,name) 我的理解是两者是相同的。请提出建议。两者都会给出您想要的结果,但是snprintf更通用,并且无论给定的字符串格式如何,都会保护您的字符串不受溢出的影响 此外,由于snprintf(或sprintf)添加了一个最终的\0,因此您应该将字符串缓冲区增大一个
#define MAXLEN 255
char buff[MAXLEN + 1]
sprintf(buff,'%s',MAXLEN,name)
snprintf(buff,MAXLEN,“%s”,name)
我的理解是两者是相同的。请提出建议。两者都会给出您想要的结果,但是
snprintf
更通用,并且无论给定的字符串格式如何,都会保护您的字符串不受溢出的影响
此外,由于
snprintf
(或sprintf
)添加了一个最终的\0
,因此您应该将字符串缓冲区增大一个字节,char buff[MAXLEN+1]
您给出的两个表达式并不等价:sprintf
不接受指定要写入的最大字节数的参数;它只需要一个目标缓冲区、一个格式字符串和一组参数。因此,它可能会写入超过缓冲区空间的字节,并在这样做时写入任意代码。%.*s
不是令人满意的解决方案,因为:
sprintf
版本在缓冲区溢出方面的行为。使用snprintf
,无论格式字符串或输入类型如何更改,都会设置一个固定的清除最大值您的sprintf声明是正确的,但我没有足够的自信将其用于安全目的(例如,缺少一个神秘字符,您就没有防护罩),而snprintf可以应用于任何格式。。。哦,等等,snprintf不在ANSI C中。它是(仅?)C99。这可能是选择另一个的(微弱的)理由 嗯。你也可以使用strncpy,不是吗 e、 g
对于问题中的简单示例,两个调用之间的安全性可能没有太大差异。但是,在一般情况下,
snprintf()
可能更安全。一旦有了具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保在不同的转换中准确地说明缓冲区长度,尤其是因为以前的转换不一定会产生固定数量的输出字符
因此,我坚持使用snprintf()
snprintf()
(虽然与安全性无关)的另一个小优点是它可以告诉您需要多大的缓冲区
最后一个注意事项-您应该在snprintf()
调用中指定实际缓冲区大小-它将为您处理空终止符的计算:
snprintf(buff, sizeof(buff), "%s", name);
在我读到这篇文章之前,我会说snprintf()
要好得多:
简短的总结是:snprintf()
不可移植,它的行为会随着系统的不同而变化。snprintf()
最严重的问题可能发生在通过调用sprintf()
来实现snprintf()
时。您可能认为它可以保护您免受缓冲区溢出,降低您的警惕性,但可能不会
所以现在我仍然在说
snprintf()
更安全,但在使用时也要谨慎。最好、最灵活的方法是使用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的字符串,您可以提前找到扩展字符串的长度,并使用它分配必要的内存。这两者之间有一个重要的区别--snprintf
调用将name
参数扫描到底(终止NUL)以便计算出正确的返回值。另一方面,sprintf
调用将从name
读取最多255个字符
因此,如果
name
是指向至少255个字符的非NUL终止缓冲区的指针,那么snprintf
调用可能会从缓冲区的末尾运行并触发未定义的行为(例如崩溃),而sprintf
版本则不会。实际上,大约是1,我弄错了-它确实指定了最大字符数(但没有计算\0
)。我已经相应地编辑了我的答案。好吧,这取决于OP的问题是关于什么样的安全性的。正式使用的sprintf
在这种特定情况下与snprintf
一样安全。在这个答案中,您所说的是缺乏对懒惰/不称职程序员的保护。我不知道OP是否询问安全性的这一方面。我假设字符串name
是由不受信任的用户提供的;这是唯一一个安全问题,在这种情况下,这是一个相当严重的问题。使用一个非常糟糕的sprintf
格式字符串,恶意用户精心设计其输入可以覆盖堆栈并执行任意代码;使用此格式字符串,恶意用户只能用零(空终止符)覆盖堆栈上的一个字节,这可能是一种非常有效的DOS攻击。但是,一个好的格式字符串提供与snprintf
相同级别的保护(同样,专门应用于字符串输入)。它确实,但是它的可验证性要差得多-很难查看sprintf
调用,很难一眼就确定它将覆盖的内存上限。您确定ANSI C中有%.*s
吗?我
size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */
char *str = malloc(nbytes);
snprintf(str, nbytes, "%s", name);