我想知道这两个选项中哪一个更安全:
#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
,无论格式字符串或输入类型如何变化,都会设置一个固定的、明确的最大值。对于问题中的简单示例,两个调用之间的安全性可能没有太大区别。但是,在一般情况下snprintf()
可能更安全。一旦您拥有具有多个转换规范的更复杂的格式字符串,就很难(或几乎不可能)确保您在不同的转换中准确计算缓冲区长度 - 特别是因为先前的转换不一定会产生固定数字的输出字符。
所以,我会坚持snprintf()
.
另一个小优势snprintf()
(尽管与安全无关)是它会告诉您需要多大的缓冲区。
最后一点——你应该在调用中指定实际的缓冲区大小snprintf()
——它将为你处理空终止符的计算:
snprintf(buff, sizeof(buff), "%s", name);
在我读到这段话之前,我会说snprintf()
要好得多:
https://buildsecurityin.us-cert.gov/bsi/articles/knowledge/coding/838-BSI.html
简短的总结是:snprintf()
不可移植它的行为从系统到系统的变化。最严重的问题snprintf()
可能发生在snprintf()
仅通过调用实现时sprintf()
。您可能认为它可以保护您免受缓冲区溢出并放松警惕,但事实并非如此。
所以现在我仍然说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
另一方面,该调用将从 中读取最多 255 个字符name
。
因此,如果name
是指向至少 255 个字符的非 NUL 终止缓冲区的指针,则snprintf
调用可能会在缓冲区末尾运行并触发未定义的行为(例如崩溃),而sprintf
版本不会。
您的 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
两者都会给出您想要的结果,但snprintf
更通用,并且无论给出的格式字符串如何,都会保护您的字符串不被溢出。
此外,因为snprintf
(或sprintf
就此而言)添加了 final \0
,您应该使字符串缓冲区大一个字节,char buff[MAXLEN + 1]
.