为什么 strlcpy 和 strlcat 被认为不安全?
据我了解,strlcpy
和 strlcat
被设计为 strncpy
和 strncat
的安全替代品。然而,有些人仍然认为它们不安全,并且只会导致不同类型的问题< /a>.
有人可以举例说明使用 strlcpy
或 strlcat
(即总是 null 终止其字符串的函数)如何导致安全问题吗?
乌尔里希·德雷珀 (Ulrich Drepper) 和詹姆斯·安蒂尔 (James Antill) 表示这是正确的,但从未提供示例或澄清这一点。
I understand that strlcpy
and strlcat
were designed as secure replacements for strncpy
and strncat
. However, some people are still of the opinion that they are insecure, and simply cause a different type of problem.
Can someone give an example of how using strlcpy
or strlcat
(i.e. a function that always null terminates its strings) can lead to security problems?
Ulrich Drepper and James Antill state this is true, but never provide examples or clarify this point.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
首先,
strlcpy
从未被设计为strncpy
的安全版本(并且strncpy
也从未被设计为strcpy 的安全版本)。这两个功能完全不相关。
strncpy
是一个与 C 字符串(即以 null 结尾的字符串)完全无关的函数。事实上,它的名称中有str...
前缀只是一个历史错误。strncpy
的历史和目的是众所周知的并且有详细记录。这是一个为处理 Unix 文件系统的某些历史版本中使用的所谓“固定宽度”字符串(不是 C 字符串)而创建的函数。如今,一些程序员对其名称感到困惑,并认为strncpy
在某种程度上应该充当有限长度的 C 字符串复制函数(strcpy
的“安全”兄弟),这实际上完全是无稽之谈,并导致糟糕的编程实践。当前形式的 C 标准库没有任何有限长度 C 字符串复制的功能。这就是strlcpy
的用武之地。strlcpy
确实是一个真正的有限长度复制函数,为处理 C 字符串而创建。strlcpy
正确地完成了有限长度复制函数应该做的一切。人们可以针对它的唯一批评是,遗憾的是,它不是标准的。其次,另一方面,strncat 确实是一个使用 C 字符串并执行有限长度连接的函数(它确实是 strcat 的“安全”兄弟) )。为了正确使用此函数,程序员必须特别小心,因为此函数接受的大小参数实际上并不是接收结果的缓冲区的大小,而是其剩余部分的大小(也是终止符字符)是隐式计算的)。这可能会令人困惑,因为为了将该大小与缓冲区的大小联系起来,程序员必须记住执行一些额外的计算,这通常用于批评
strncat
。strlcat
解决了这些问题,更改了接口,以便不需要额外的计算(至少在调用代码中)。同样,我认为有人可以批评这一点的唯一依据是该功能不标准。此外,由于基于重新扫描的字符串连接这一概念的可用性有限,您在专业代码中不会经常看到来自strcat
组的函数。至于这些功能如何导致安全问题……他们根本不能。它们导致的安全问题不会比 C 语言本身“导致安全问题”更大。你看,很长一段时间以来,人们有一种强烈的情绪,认为 C++ 语言必须朝着发展成某种奇怪的 Java 风格的方向发展。这种情绪有时也会蔓延到 C 语言领域,导致对 C 语言特性和 C 标准库特性的相当无知和强迫的批评。我怀疑在这种情况下我们也可能会遇到类似的事情,尽管我当然希望事情没有那么糟糕。
Firstly,
strlcpy
has never been intended as a secure version ofstrncpy
(andstrncpy
has never been intended as a secure version ofstrcpy
). These two functions are totally unrelated.strncpy
is a function that has no relation to C-strings (i.e. null-terminated strings) at all. The fact that it has thestr...
prefix in its name is just a historical blunder. The history and purpose ofstrncpy
is well-known and well-documented. This is a function created for working with so called "fixed width" strings (not with C-strings) used in some historical versions of Unix file system. Some programmers today get confused by its name and assume thatstrncpy
is somehow supposed to serve as limited-length C-string copying function (a "secure" sibling ofstrcpy
), which in reality is complete nonsense and leads to bad programming practice. C standard library in its current form has no function for limited-length C-string copying whatsoever. This is wherestrlcpy
fits in.strlcpy
is indeed a true limited-length copying function created for working with C-strings.strlcpy
correctly does everything a limited-length copying function should do. The only criticism one can aim at it is that it is, regretfully, not standard.Secondly,
strncat
on the other hand, is indeed a function that works with C-strings and performs a limited-length concatenation (it is indeed a "secure" sibling ofstrcat
). In order to use this function properly the programmer has to take some special care, since the size parameter this function accepts is not really the size of the buffer that receives the result, but rather the size of its remaining part (also, the terminator character is counted implicitly). This could be confusing, since in order to tie that size to the size of the buffer, programmer has to remember to perform some additional calculations, which is often used to criticize thestrncat
.strlcat
takes care of these issues, changing the interface so that no extra calculations are necessary (at least in the calling code). Again, the only basis I see one can criticise this on is that the function is not standard. Also, functions fromstrcat
group is something you won't see in professional code very often due to the limited usability of the very idea of rescan-based string concatenation.As for how these functions can lead to security problems... They simply can't. They can't lead to security problems in any greater degree than the C language itself can "lead to security problems". You see, for quite a while there was a strong sentiment out there that C++ language has to move in the direction of developing into some weird flavor of Java. This sentiment sometimes spills into the domain of C language as well, resulting in rather clueless and forced criticism of C language features and the features of C standard library. I suspect that we might be dealing with something like that in this case as well, although I surely hope things are not really that bad.
乌尔里希的批评基于这样的想法:程序未检测到的字符串截断可能会通过不正确的逻辑导致安全问题。因此,为了安全起见,您需要检查是否有截断。对字符串连接执行此操作意味着您正在按照以下方式进行检查:
现在,如果程序员记得检查结果,
strlcat
确实会有效地执行此检查 - 所以您可以安全地使用它:Ulrich 的观点是,既然你必须有
destlen
和sourcelen
(或重新计算它们,这就是strlcat 有效地做到了),无论如何你也可以使用更高效的
memcpy
:(在上面的代码中,
dest_maxlen
是可以存储的字符串的最大长度indest
- 比dest
缓冲区的大小小 1,dest_bufferlen
是dest 缓冲区
的完整大小。 )。Ulrich's criticism is based on the idea that a string truncation that is not detected by the program can lead to security issues, through incorrect logic. Therefore, to be secure, you need to check for truncation. To do this for a string concatenation means that you are doing a check along the lines of this:
Now,
strlcat
does effectively do this check, if the programmer remembers to check the result - so you can use it safely:Ulrich's point is that since you have to have
destlen
andsourcelen
around (or recalculate them, which is whatstrlcat
effectively does), you might as well just use the more efficientmemcpy
anyway:(In the above code,
dest_maxlen
is the maximum length of the string that can be stored indest
- one less than the size of thedest
buffer.dest_bufferlen
is the full size of thedest buffer
).当人们说“
strcpy()
很危险,请使用strncpy()
”(或关于strcat()
等的类似陈述,但是我将在这里使用strcpy()
作为我的焦点),它们意味着strcpy()
中没有边界检查。因此,过长的字符串将导致缓冲区溢出。他们是正确的。在这种情况下使用 strncpy() 可以防止缓冲区溢出。我觉得
strncpy()
确实不能修复错误:它解决了一个优秀程序员可以轻松避免的问题。作为一名 C 程序员,在尝试复制字符串之前,您必须知道目标大小。这也是
strncpy()
和strlcpy()
最后一个参数的假设:您向它们提供该大小。您还可以在复制字符串之前知道源大小。然后,如果目的地不够大,不要调用strcpy()
。要么重新分配缓冲区,要么做其他事情。为什么我不喜欢
strncpy()
?strncpy()
效率非常低。它写入目标缓冲区中的每个字节。您不需要在目的地末尾添加数千个'\0'
。'\0'
。所以,无论如何,你必须亲自这样做。这样做的复杂性不值得这么麻烦。现在,我们来到
strlcpy()
。 strncpy() 的更改使其变得更好,但我不确定 strl* 的特定行为是否保证它们的存在:它们太具体了。您仍然必须知道目的地大小。它比strncpy()
更高效,因为它不必写入目标中的每个字节。但它解决了一个可以通过执行以下操作来解决的问题:*((char *)mempcpy(dst, src, n)) = 0;
。我认为没有人说
strlcpy()
或strlcat()
会导致安全问题,他们(和我)所说的是它们会导致错误,例如,当您希望写入完整的字符串而不是其中的一部分时。这里的主要问题是:要复制多少字节?程序员必须知道这一点,如果他不知道,
strncpy()
或strlcpy()
将无法拯救他。strlcpy()
和strlcat()
都不是标准,ISO C 和 POSIX 都不是标准。因此,它们在可移植程序中的使用是不可能的。事实上,strlcat()有两种不同的变体:对于涉及长度 0 的边缘情况,Solaris 实现与其他实现不同。这使得它比其他情况更没有用处。When people say, "
strcpy()
is dangerous, usestrncpy()
instead" (or similar statements aboutstrcat()
etc., but I am going to usestrcpy()
here as my focus), they mean that there is no bounds checking instrcpy()
. Thus, an overly long string will result in buffer overruns. They are correct. Usingstrncpy()
in this case will prevent buffer overruns.I feel that
strncpy()
really doesn't fix bugs: it solves a problem that can be easily avoided by a good programmer.As a C programmer, you must know the destination size before you are trying to copy strings. That is the assumption in
strncpy()
andstrlcpy()
's last parameters too: you supply that size to them. You can also know the source size before you copy strings. Then, if the destination is not big enough, don't callstrcpy()
. Either reallocate the buffer, or do something else.Why do I not like
strncpy()
?strncpy()
is a bad solution in most cases: your string is going to be truncated without any notice—I would rather write extra code to figure this out myself and then take the course of action that I want to take, rather than let some function decide for me about what to do.strncpy()
is very inefficient. It writes to every byte in the destination buffer. You don't need those thousands of'\0'
at the end of your destination.'\0'
if the destination is not big enough. So, you must do so yourself anyway. The complexity of doing this is not worth the trouble.Now, we come to
strlcpy()
. The changes fromstrncpy()
make it better, but I am not sure if the specific behavior ofstrl*
warrants their existence: they are far too specific. You still have to know the destination size. It is more efficient thanstrncpy()
because it doesn't necessarily write to every byte in the destination. But it solves a problem that can be solved by doing:*((char *)mempcpy(dst, src, n)) = 0;
.I don't think anyone says that
strlcpy()
orstrlcat()
can lead to security issues, what they (and I) are saying that they can result in bugs, for example, when you expect the complete string to be written instead of a part of it.The main issue here is: how many bytes to copy? The programmer must know this and if he doesn't,
strncpy()
orstrlcpy()
won't save him.strlcpy()
andstrlcat()
are not standard, neither ISO C nor POSIX. So, their use in portable programs is impossible. In fact,strlcat()
has two different variants: the Solaris implementation is different from the others for edge cases involving length 0. This makes it even less useful than otherwise.我认为乌尔里希和其他人认为这会给人一种错误的安全感。意外截断字符串可能会对代码其他部分产生安全影响(例如,如果文件系统路径被截断,程序可能无法对预期文件执行操作)。
I think Ulrich and others think it'll give a false sense of security. Accidentally truncating strings can have security implications for other parts of the code (for example, if a file system path is truncated, the program might not be performing operations on the intended file).
有两个与使用 strl 函数相关的“问题”:
以避免截断。
c1x 标准草案编写者和 Drepper 认为程序员不会检查返回值。 Drepper 说我们应该以某种方式知道长度并使用 memcpy 并完全避免字符串函数。标准委员会认为安全 strcpy 在截断时应返回非零,除非
_TRUNCATE
标志另有说明。这个想法是人们更有可能使用 if(strncpy_s(...))。有些人认为,即使输入虚假数据,字符串函数也不应该崩溃。这会影响标准函数,例如 strlen,在正常情况下会出现段错误。新标准将包括许多此类功能。这些检查当然会带来性能损失。
与建议的标准函数相比,您可以知道使用 strl 函数丢失了多少数据。
There are two "problems" related to using strl functions:
to avoid truncation.
The c1x standard draft writers and Drepper, argue that programmers won't check the return value. Drepper says we should somehow know the length and use memcpy and avoid string functions altogether, The standards committee argues that the secure strcpy should return nonzero on truncation unless otherwise stated by the
_TRUNCATE
flag. The idea is that people are more likely to use if(strncpy_s(...)).Some people think that string functions should never crash even when fed bogus data. This affects standard functions such as strlen which in normal conditions will segfault. The new standard will include many such functions. The checks of course have a performance penalty.
The upside over the proposed standard functions is that you can know how much data you missed with strl functions.
我不认为
strlcpy
和strlcat
被认为是不安全,或者至少这不是它们不包含在 glibc 中的原因 -毕竟glibc包括strncpy甚至strcpy。他们受到的批评是,据称他们效率低下,而不是缺乏安全感。
根据 Damien Miller 的安全可移植性论文:
这就是为什么它们在 glibc 中不可用,但它们在 Linux 上不可用是不正确的。它们可以在 Linux 上的 libbsd 中找到:
它们打包在 Debian 中以及 Ubuntu 和其他发行版。您也可以获取一份副本并在您的项目中使用 - 它很短并且需要许可:
I don't think
strlcpy
andstrlcat
are consider insecure or it least it isn't the reason why they're not included in glibc - after all, glibc includes strncpy and even strcpy.The criticism they got was that they are allegedly inefficient, not insecure.
According to the Secure Portability paper by Damien Miller:
That is why they are not available in glibc, but it is not true that they are not available on Linux. They are available on Linux in libbsd:
They're packaged in Debian and Ubuntu and other distros. You can also just grab a copy and use in your project - it's short and under a permissive license:
安全性不是布尔值。 C 函数并不完全“安全”或“不安全”、“安全”或“不安全”。如果使用不当,C 中的简单赋值操作可能是“不安全的”。 strlcpy() 和 strlcat() 可以安全地(securely)使用,就像当程序员提供正确使用的必要保证时可以安全地使用 strcpy() 和 strcat() 一样。
所有这些 C 字符串函数(无论是标准的还是非标准的)的要点是它们使安全/可靠使用简单的程度。 strcpy() 和 strcat() 的安全使用并不简单;多年来 C 程序员犯错的次数以及随之而来的令人讨厌的漏洞和利用就证明了这一点。 strlcpy() 和 strlcat() 以及 strncpy() 和 strncat()、strncpy_s() 和 strncat_s() 更容易安全使用,但仍然很重要。他们不安全/没有保障吗?如果使用不正确,就和 memcpy() 一样。
Security is not a boolean. C functions are not wholly "secure" or "insecure", "safe" or "unsafe". When used incorrectly, a simple assignment operation in C can be "insecure". strlcpy() and strlcat() may be used safely (securely) just as strcpy() and strcat() can be used safely when the programmer provides the necessary assurances of correct usage.
The main point with all of these C string functions, standard and not-so-standard, is the level to which they make safe/secure usage easy. strcpy() and strcat() are not trivial to use safely; this is proven by the number of times that C programmers have gotten it wrong over the years and nasty vulnerabilities and exploits have ensued. strlcpy() and strlcat() and for that matter, strncpy() and strncat(), strncpy_s() and strncat_s(), are a bit easier to use safely, but still, non-trivial. Are they unsafe/insecure? No more than memcpy() is, when used incorrectly.
如果
src
不是以NUL
结尾,strlcpy
可能会触发SIGSEGV
。strlcpy
may triggerSIGSEGV
, ifsrc
is notNUL
-terminated.