我正在寻找为什么 strncpy 被认为是不安全的。是否有人对此有任何类型的文档或使用它的利用示例?
5 回答
看看这个网站;这是一个相当详细的解释。基本上,strncpy()
不需要 NUL 终止,因此容易受到各种攻击。
最初的问题显然是strcpy(3)不是内存安全操作,因此攻击者可以提供比缓冲区更长的字符串,这会覆盖堆栈上的代码,如果精心安排,可以执行攻击者的任意代码。
但是strncpy(3)的另一个问题是它不会在目的地的每种情况下都提供空终止。(想象一个源字符串比目标缓冲区长。)未来的操作可能期望在相同大小的缓冲区之间符合 C 以 nul 结尾的字符串,并且当结果被复制到第三个缓冲区时下游会发生故障。
使用 strncpy(3) 比 strcpy(3) 更好,但像 strlcpy(3) 这样的东西更好。
为了安全地使用 strncpy,必须 (1) 手动将空字符粘贴到结果缓冲区,(2) 事先知道缓冲区以空结尾,并将 (length-1) 传递给 strncpy,或者 (3) 知道永远不会使用任何不会将其长度绑定到缓冲区长度的方法来复制缓冲区。
重要的是要注意 strncpy 会将缓冲区中复制的字符串之后的所有内容填零,而其他长度受限的 strcpy 变体不会。这在某些情况下可能会消耗性能,但在其他情况下会带来安全优势。例如,如果使用 strlcpy 将“supercalifragilisticexpalidocious”复制到缓冲区,然后复制“it”,则缓冲区将保存“it^ercalifragilisticexpalidocious^”(使用“^”表示零字节)。如果缓冲区被复制为固定大小的格式,额外的数据可能会随之标记。
该问题基于“加载”的前提,这使得问题本身无效。
这里的底线strncpy
是不被认为是不安全的,也从未被认为是不安全的。可以附加到该函数的唯一“不安全”声明是 C 内存模型和 C 语言本身的普遍不安全的广泛声明。(但这显然是一个完全不同的话题)。
在 C 语言领域内,对某种固有的“不安全性”的误导性信念strncpy
源自于广泛使用strncpy
的“安全字符串复制”的可疑模式,即此功能不做且从未打算用于的事情。这种用法确实很容易出错。但即使你在“高度容易出错”和“不安全”之间加上一个等号,它仍然是一个使用问题(即缺乏教育问题)而不是strncpy
问题。
基本上,可以说唯一的问题strncpy
是一个不幸的命名,这使得新手程序员假设他们理解这个函数的作用,而不是实际阅读规范。查看函数名称,一个不称职的程序员会认为它strncpy
是 的“安全版本” strcpy
,而实际上这两个函数完全不相关。
例如,可以针对除法运算符提出完全相同的主张。正如你们大多数人所知,关于 C 语言的最常见问题之一是“我认为它1/2
会计算结果,0.5
但我得到了0
。为什么?” 然而,我们不会仅仅因为语言初学者倾向于误解它的行为就声称除法运算符是不安全的。
再举一个例子,我们之所以称伪随机数生成器函数为“不安全”,只是因为不称职的程序员常常对他们的输出并非真正随机这一事实感到不快。
这正是它的strncpy
功能。就像初学者程序员需要时间来了解伪随机数生成器的实际作用一样,他们也需要时间来了解strncpy
实际作用。学习这strncpy
是一个转换函数需要时间,用于将零结尾的字符串转换为固定宽度的字符串。学习strncpy
与“安全字符串复制”完全无关并且不能有意义地用于该目的需要时间。
strncpy
诚然,语言学生学习目的通常比使用除法运算符解决问题要花更长的时间。但是,这是针对strncpy
.
PS 在接受的答案中链接的 CERT 文档专门用于:展示典型的无能滥用strncpy
功能作为“安全”版本的不安全性strcpy
。它绝不是要声称strncpy
自己在某种程度上是不安全的。
Git 2.19(Q3 2018)的一个pathc发现太容易误用系统API函数如strcat()
;strncpy()
; ...并禁止此代码库中的这些功能。
请参阅Jeff King ( ) 的提交 e488b7a、提交 cc8fdae、提交 1b11b64(2018 年 7 月 24 日)和提交 c8af66a(2018 年 7 月 26 日)。(由Junio C Hamano 合并 -- --在提交 e28daf2中,2018 年 8 月 15 日)peff
gitster
banned.h
: 标记strcat()
为禁止该
strcat()
函数具有与strcpy()
.
作为奖励,很容易意外地以二次方告终,因为每个后续调用都必须遍历现有字符串。最后一次
strcat()
调用在f063d38中消失(守护进程:重生时使用 cld->env_array,2015-09-24,Git 2.7.0)。
通常,strcat()
可以用动态字符串(strbuf
或xstrfmt
)替换,或者xsnprintf
如果您知道长度是有界的,则可以替换为。