当前位置:  开发笔记 > 编程语言 > 正文

为什么strlcpy和strlcat被认为是不安全的?

如何解决《为什么strlcpy和strlcat被认为是不安全的?》经验,为你挑选了6个好方法。

据我所知,strlcpystrlcat被设计为安全的替代品strncpystrncat.然而,有些人仍然认为他们不安全,只是造成不同类型的问题.

有人可以举例说明如何使用strlcpystrlcat(即一个总是 null终止其字符串的函数)会导致安全问题吗?

Ulrich Drepper和James Antill表示这是事实,但从未提供实例或澄清这一点.



1> AnT..:

首先,strlcpy从未打算作为安全版本strncpy(并且strncpy从未打算作为安全版本strcpy).这两个功能完全不相关.strncpy是一个与C字符串(即以空字符结尾的字符串)无关的函数.事实上,它的str...名字中有前缀只是一个历史错误.历史和目的strncpy是众所周知和有据可查的.这是为在Unix文件系统的某些历史版本中使用所谓的"固定宽度"字符串(不使用C字符串)而创建的函数.一些程序员今天对其名称感到困惑,并假设它strncpy在某种程度上应该作为有限长度的C字符串复制功能(一个"安全"的兄弟姐妹strcpy),实际上完全是胡说八道,导致糟糕的编程习惯.当前形式的C标准库对于有限长度的C字符串复制没有任何功能.这是strlcpy适合的.strlcpy确实是一个真正的有限长度复制功能,用于处理C字符串.strlcpy正确地做有限长度复制功能应该做的一切.人们可以瞄准的唯一批评是遗憾的是,它不是标准的.

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 of strcat). 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 the strncat. 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 from strcat 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.


有很多理由不喜欢strlcpy/strlcat,但你没有说明任何一个.对C++和Java的讨论是无关紧要的.这个答案对实际问题的主题没有帮助.
@John Ripley:首先,我不是"说明他们中的任何一个",因为我不知道有任何理由不喜欢"strlcpy/strlcat".人们可能"不喜欢"零终止字符串的一般概念,但这不是问题所在.如果你知道"有很多理由不喜欢'strlcpy/strlcat`",你应该写下自己的答案,而不是期望我能够读懂别人的想法.
我不完全同意.如果`strlcpy`和`strlcat`在遇到目标缓冲区大小限制时报告某种错误条件,那将是很好的.虽然您可以检查返回的长度来测试它,但这并不明显.但我认为这是一个小小的批评.'他们鼓励使用C字符串,所以他们很糟糕'的说法很愚蠢.
@John Ripley:其次,问题是特指"strlcpy/strlcat"所谓的"安全问题".虽然我相信我明白这是什么,但我个人拒绝承认这是传统C语言中的"安全问题",正如我所知道的那样.我在答案中说过.
"这些功能如何导致安全问题" - fwiw我认为这里的问题是某些C功能比其他功能更难正确使用.有些人错误地认为存在一个特殊的难度阈值,低于该难度阈值时,一个函数是"安全的",超过该阈值就是"不安全".这些人通常也认为`strcpy`高于阈值并因此"不安全",他们首选的字符串复制函数(无论是`strlcpy`,`strcpy_s`还是`strncpy`)低于阈值因此"安全".
"目前形式的C标准库对于有限长度的C字符串复制没有任何作用." 只是说','snprintf` ......

2> caf..:

Ulrich的批评基于这样一种观点,即程序未检测到的字符串截断可能会通过错误的逻辑导致安全问题.因此,为了安全起见,您需要检查截断.为字符串连接执行此操作意味着您正在检查以下内容:

if (destlen + sourcelen > dest_maxlen)
{
    /* Bug out */
}

现在,strlcat如果程序员记得检查结果,那么有效地进行此检查 - 这样您就可以安全地使用它:

if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen)
{
    /* Bug out */
}

Ulrich的观点是,既然你必须拥有destlensourcelen(或者重新计算它们,这实际上是strlcat有效的),你最好memcpy还是使用效率更高的方法:

if (destlen + sourcelen > dest_maxlen)
{
    goto error_out;
}
memcpy(dest + destlen, source, sourcelen + 1);
destlen += sourcelen;

(在上面的代码中,dest_maxlen是可以存储的字符串的最大长度dest- 比dest缓冲区的大小小一个. dest_bufferlen是完整大小的dest buffer).


Drepper代码的可读性很差.使用strlcpy(或任何str函数)我直接知道我正在复制一个0终止的C字符串.使用`memcpy`它可以是任何类型的内存,我有一个补充维度来检查何时尝试理解代码.我有一个遗留的应用程序来调试memcpy所做的一切,这是一个真正的PITA来纠正.移植到专用的String函数后,它更容易阅读(并且更快,因为可以删除许多不必要的`strlen`).
@domen:`memcpy()`*是一个字符串操作 - 它毕竟是在``中声明的.
@domen:因为要复制的大小已经知道,所以`memcpy()`就足够了(并且可能比`strcpy()`更有效).
@domen我同意存在混淆的可能性,但实际情况是使用C字符串无论如何都要使用原始内存.可以说,如果人们根本不再认为C具有"字符串"(与任何其他连续的内存块不同),那么我们会更好.

3> Alok Singhal..:

当人们说" strcpy()危险,strncpy()反而使用"(或类似的陈述strcat()等等,但我将在strcpy()这里作为我的焦点),他们意味着没有界限检查strcpy().因此,过长的字符串将导致缓冲区溢出.他们是对的.strncpy()在这种情况下使用将防止缓冲区溢出.

我觉得strncpy()真的不能修复错误:它解决了一个好的程序员可以轻易避免的问题.

作为C程序员,在尝试复制字符串之前,必须知道目标大小.这是在假设strncpy()strlcpy()的最后太参数:您尺寸提供给他们.在复制字符串之前,您还可以知道源大小.然后,如果目的地不够大,请不要打电话strcpy().要么重新分配缓冲区,要么做其他事情.

我为什么不喜欢strncpy()

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实现与其他实现不同.这使得它甚至不如其他方面有用.


你怎么得到那个?您可以提前知道的唯一n是缓冲区大小.当然如果你建议每次你需要使用`memcpy`和`strlen`来实现`strlcpy`也没关系,但是为什么停在`strlcpy`,你不需要*一个'memcpy`函数,你可以逐个复制字节.在正常情况下,参考实现仅循环数据一次,这对大多数体系结构更好.但即使最好的实现使用`strlen` +`memcpy`,仍然没有理由不必一次又一次地重新实现安全的strcpy.
"*((char*)mempcpy(dst,src,n))= 0;" - 正确 - 是的,显然是正确的,没有.失败的代码审查......
在许多架构上,`strlcpy`比`memcpy`更快,特别是如果`memcpy`复制不必要的尾随数据.`strlcpy`还会返回您错过的数据量,这可能会让您更快地恢复并且代码更少.

4> jamesdlin..:

我认为乌尔里希和其他人认为它会给人一种虚假的安全感.一不留神截取字符串可以有代码的其他部分安全隐患(例如,如果一个文件系统路径被截断,该程序可能没有预期的文件进行操作).


例如,电子邮件客户端可能会将电子邮件附件的文件名从"malware.exe.jpg"截断为"malware.exe".

5> 小智..:

使用strl函数有两个"问题":

    您必须检查返回值以避免截断.

c1x标准草稿编写者和Drepper认为程序员不会检查返回值.Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免字符串函数.标准委员会认为,除非_TRUNCATE标志另有说明,否则安全strcpy应在截断时返回非零值.这个想法是人们更有可能使用if(strncpy_s(...)).

    不能用于非字符串.

有些人认为即使在输入虚假数据时,字符串函数也不会崩溃.这会影响标准功能,例如strlen,在正常情况下会发生段错误.新标准将包括许多此类功能.检查当然会有性能损失.

建议的标准函数的优点是,您可以通过strl函数知道错过了多少数据.



6> rsp..:

我不认为strlcpy并且strlcat被认为是不安全的,或者至少这不是为什么它们不包含在glibc中的原因-毕竟,glibc包含strncpy甚至strcpy。

他们受到的批评是据称他们效率低下,而不是没有安全感

根据Damien Miller 的“ 安全可移植性”论文:

strlcpy和strlcat API会正确检查目标缓冲区的边界,在所有情况下均以nul结尾,并返回源字符串的长度,从而可以检测到截断。该API已被大多数现代操作系统和许多独立软件包所采用,包括OpenBSD(起源于此),Sun Solaris,FreeBSD,NetBSD,Linux内核,rsync和GNOME项目。值得注意的例外是GNU标准C库glibc [12],其维护者坚定地拒绝包括这些改进的API,并对其进行标记 “效率极低的BSD废话”。[4],尽管有以前的证据表明它们在大多数情况下比它们替代的API更快[13]。结果,OpenBSD端口树中存在的100多个软件包都维护着自己的strlcpy和/或strlcat替代品或等效的API,这不是理想的状态。

这就是为什么它们在glibc中不可用,但是在Linux上它们不可用并不是事实。它们在libbsd的Linux上可用:

https://libbsd.freedesktop.org/

它们打包在Debian,Ubuntu和其他发行版中。您也可以只获取一份副本并在您的项目中使用-它是简短的,并得到许可:

http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有