像strcpy、gets等函数总是不安全的吗?如果我写了下面这样的代码,会怎么样:
int main(void)
{
char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100);
strcpy(str2, str1);
}
这种方式的函数不接受参数,str变量的长度始终相同...在这里是16或稍微长一点,具体取决于编译器版本...但是从2011年3月开始,100就足够了 :). 黑客有没有办法利用上面的代码呢? 谢谢!
像strcpy、gets等函数总是不安全的吗?如果我写了下面这样的代码,会怎么样:
int main(void)
{
char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100);
strcpy(str2, str1);
}
这种方式的函数不接受参数,str变量的长度始终相同...在这里是16或稍微长一点,具体取决于编译器版本...但是从2011年3月开始,100就足够了 :). 黑客有没有办法利用上面的代码呢? 谢谢!
绝对不是这样的。与微软针对他们的非标准函数的营销活动相反,strcpy
是安全的,当使用正确时。
上面的代码有些冗余,但大体是安全的。唯一潜在的问题是您没有检查malloc
的返回值,因此可能会出现null引用(正如kotlinski所指出的)。实际上,这很可能会导致立即的SIGSEGV和程序终止。
一种不正确和危险的使用方式是:
char array[100];
// ... Read line into uncheckedInput
// Extract substring without checking length
strcpy(array, uncheckedInput + 10);
这段代码不安全,因为 strcpy
可能会溢出,导致未定义的行为。 实际上,这很可能会覆盖其他本地变量(这本身就是一个重大的安全漏洞)。 这些变量之一可能是返回地址。 通过 return to lib C 攻击,攻击者可能能够使用像 system
这样的 C 函数来执行任意程序。 溢出还可能有其他可能的后果。
然而,gets
确实是固有不安全的,并将在下一个 C 版本(C1X)中被删除。根本没有办法确保输入不会溢出(导致与上述相同的后果)。 有些人会认为在使用已知输入文件时是安全的,但真的没有理由去使用它。 POSIX 的 getline
是一个更好的选择。
此外,str1
的长度不会因为编译器的不同而变化。 它应该始终为17,包括终止的 NUL 字符。
你正在强制把完全不同的事物归为一类。
gets
函数总是存在安全隐患。无论你采取什么措施,多么防御性地编程,都不可能对gets
调用进行安全保证。
如果你愿意采取必要简单的措施来确保你对strcpy
的调用安全,那么strcpy
函数就是完全安全的。
这已经将gets
和strcpy
归为完全不同的类别,它们在安全方面没有任何共同之处。
针对strcpy
安全性的普遍批评完全基于社会观察,而非实际事实,例如“程序员懒惰而且无能,所以不能让他们使用strcpy
”。当然,在C编程环境中,这纯属胡言乱语。如果按照这种逻辑,我们也应该宣布除法运算符因为完全相同的原因而不安全。
事实上,strcpy
不存在任何问题。正如我上面提到的,gets
则完全不同。
gets
从外部世界读取数据。在gets
实际开始读取并填充缓冲区之前,无法预测将有多少数据存在。 - AnT stands with Russiagets()
之前“检查”行的长度 - 那么您可以在“检查”函数中读取数据。这就打败了首次使用 gets()
的目的。 ;) 再说一遍,使用 gets()
时您无法预先知道其大小,如果您确实知道它的大小 - 那么为什么数据还没有被存储呢? - Xupicor是的,这很危险。经过5年的维护,你的代码将会变成这样:
int main(void)
{
char *str1 = "abcdefghijklmnop";
{enough lines have been inserted here so as to not have str1 and str2 nice and close to each other on the screen}
char *str2 = malloc(100);
strcpy(str2, str1);
}
到那时,有人会更改str1为:
str1="这是一个非常长的字符串,如果在复制它时不采取预防措施来范围检查字符串的限制,它将会溢出任何正在使用的缓冲区。当修复一个五年前存在的漏洞时,很少有人记得这样做."
然后忘记查看str1的使用位置,接着随机错误就开始发生了...
p = malloc(strlen(s)+1); if(!p) error; strcpy(p, s)
并不危险。按照你的论点,所有编程,特别是在C语言中,都是危险的,但这显然不是问题所在。 - Jim Baltermalloc
的返回值,如果失败并返回0,则strcpy
将产生未定义行为。strcpy
并不危险,只要你知道目标缓冲区足够大以容纳源字符串的字符; 否则,strcpy
将愉快地复制比目标缓冲区更多的字符,这可能会导致几种不幸的后果(堆栈/其他变量覆盖,可能导致崩溃、堆栈破坏攻击等)。
但是:如果您有一个未经检查的通用char *
输入,唯一确定的方法是对该字符串应用strlen
并检查它是否过大而无法容纳在您的缓冲区中; 但是,现在您必须两次遍历整个源字符串,一次用于检查其长度,一次用于执行复制。
这是次优的,因为如果strcpy
稍微先进一点,它可以接收缓冲区的大小作为参数,并在源字符串过长时停止复制;在完美的世界中,这就是strncpy
的表现方式(遵循其他strn***
函数的模式)。然而,这不是一个完美的世界,strncpy
并没有设计成这样。相反,非标准(但流行的)替代方案是{{link1:strlcpy
}},它不会越过目标缓冲区的边界,而是截断。
一些CRT实现不提供此功能(特别是glibc),但您仍然可以获得其中一个BSD实现并将其放入应用程序中。标准(但速度较慢)的替代方案是使用snprintf
和"%s"
作为格式字符串。
std::string
呢?所有这些潜在的安全问题都会消失,字符串操作也变得更加容易。strcpy
总是将NUL字节写入目标,无论它是否溢出(它无法知道)。 - Matthew Flaschen除了可能会取消引用 NULL(因为您没有检查 malloc 的结果)这是 UB 并且可能不是安全威胁之外,此代码没有潜在的安全问题。
gets()
总是不安全的; 其他函数可以安全使用。
即使您完全控制输入,gets()
也是不安全的 -- 总有一天,程序可能会被其他人运行。
使用gets()
的唯一安全方法是将其用于单次运行:创建源代码;编译;运行;删除二进制文件和源代码;解释结果。