据我所知,strlcpy
和strlcat
被设计为安全的替代品strncpy
和strncat
.然而,有些人仍然认为他们不安全,只是造成不同类型的问题.
有人可以举例说明如何使用strlcpy
或strlcat
(即一个总是 null终止其字符串的函数)会导致安全问题吗?
Ulrich Drepper和James Antill表示这是事实,但从未提供实例或澄清这一点.
首先,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.
Ulrich的批评基于这样一种观点,即程序未检测到的字符串截断可能会通过错误的逻辑导致安全问题.因此,为了安全起见,您需要检查截断.为字符串连接执行此操作意味着您正在检查以下内容:
if (destlen + sourcelen > dest_maxlen) { /* Bug out */ }
现在,strlcat
如果程序员记得检查结果,那么有效地进行此检查 - 这样您就可以安全地使用它:
if (strlcat(dest, source, dest_bufferlen) >= dest_bufferlen) { /* Bug out */ }
Ulrich的观点是,既然你必须拥有destlen
它sourcelen
(或者重新计算它们,这实际上是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
).
当人们说" 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实现与其他实现不同.这使得它甚至不如其他方面有用.
我认为乌尔里希和其他人认为它会给人一种虚假的安全感.一不留神截取字符串可以有代码的其他部分安全隐患(例如,如果一个文件系统路径被截断,该程序可能没有预期的文件进行操作).
使用strl函数有两个"问题":
您必须检查返回值以避免截断.
c1x标准草稿编写者和Drepper认为程序员不会检查返回值.Drepper说我们应该以某种方式知道长度并使用memcpy并完全避免字符串函数.标准委员会认为,除非_TRUNCATE
标志另有说明,否则安全strcpy应在截断时返回非零值.这个想法是人们更有可能使用if(strncpy_s(...)).
不能用于非字符串.
有些人认为即使在输入虚假数据时,字符串函数也不会崩溃.这会影响标准功能,例如strlen,在正常情况下会发生段错误.新标准将包括许多此类功能.检查当然会有性能损失.
建议的标准函数的优点是,您可以通过strl函数知道错过了多少数据.
我不认为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