简体   繁体   English

函数 strcpy 总是危险的吗?

[英]Is the function strcpy always dangerous?

Are functions like strcpy, gets, etc. always dangerous? strcpy、gets 等函数总是危险的吗? What if I write a code like this:如果我写这样的代码怎么办:

int main(void)
{

char *str1 = "abcdefghijklmnop";
char *str2 = malloc(100); 
strcpy(str2, str1);


}

This way the function doesn't accept arguments(parameters...) and the str variable will always be the same length...which is here 16 or slightly more depending on the compiler version...but yeah 100 will suffice as of march, 2011 :).这样函数不接受参数(参数...)并且 str 变量将始终具有相同的长度...这里是 16 或略多,具体取决于编译器版本...但是 100 就足够了2011 年 3 月 :)。 Is there a way for a hacker to take advantage of the code above?有没有办法让黑客利用上面的代码? 10x! 10 倍!

Absolutely not.绝对不。 Contrary to Microsoft's marketing campaign for their non-standard functions, strcpy is safe when used properly .与 Microsoft 为其非标准功能进行的营销活动相反, strcpy在正确使用时是安全的。

The above is redundant, but mostly safe.以上是多余的,但大多是安全的。 The only potential issue is that you're not checking the malloc return value, so you may be dereferencing null (as pointed out by kotlinski).唯一的潜在问题是您没有检查malloc返回值,因此您可能正在取消引用 null(如 kotlinski 所指出的)。 In practice, this likely to cause an immediate SIGSEGV and program termination.在实践中,这可能会导致立即 SIGSEGV 和程序终止。

An improper and dangerous use would be:不当和危险的使用是:

char array[100];
// ... Read line into uncheckedInput
// Extract substring without checking length
strcpy(array, uncheckedInput + 10);

This is unsafe because the strcpy may overflow, causing undefined behavior.这是不安全的,因为 strcpy 可能会溢出,导致未定义的行为。 In practice, this is likely to overwrite other local variables (itself a major security breach).在实践中,这很可能会覆盖其他局部变量(这本身就是一个重大的安全漏洞)。 One of these may be the return address.其中之一可能是退货地址。 Through a return to lib C attack, the attacker may be able to use C functions like system to execute arbitrary programs.通过返回lib C攻击,攻击者可以使用system等C函数来执行任意程序。 There are other possible consequences to overflows.溢出还有其他可能的后果。

However, gets is indeed inherently unsafe, and will be removed from the next version of C (C1X).但是, gets本质上确实是不安全的,并且将从 C (C1X) 的下一个版本中删除。 There is simply no way to ensure the input won't overflow (causing the same consequences given above).根本无法确保输入不会溢出(导致与上述相同的后果)。 Some people would argue it's safe when used with a known input file, but there's really no reason to ever use it.有些人会争辩说,与已知的输入文件一起使用时它是安全的,但实际上没有理由使用它。 POSIX's getline is a far better alternative. POSIX 的getline是一个更好的选择。

Also, the length of str1 doesn't vary by compiler.此外, str1的长度不会因编译器而异。 It should always be 17, including the terminating NUL.它应该总是 17,包括终止 NUL。

You are forcefully stuffing completely different things into one category.你强行将完全不同的东西归为一类。

Functions gets is indeed always dangerous.函数gets确实总是危险的。 There's no way to make a safe call to gets regardless of what steps you are willing to take and how defensive you are willing to get.有没有办法让一个安全的呼吁gets不论什么步骤,你愿意花,你是如何防守愿意得到。

Function strcpy is perfectly safe if you are willing to take the [simple] necessary steps to make sure that your calls to strcpy are safe.如果您愿意采取 [简单] 必要步骤来确保对strcpy strcpy是安全的,那么函数strcpy是完全安全的。

That already puts gets and strcpy in vastly different categories, which have nothing in common with regard to safety.这已经将getsstrcpy置于截然不同的类别中,它们在安全性方面没有任何共同之处。

The popular criticisms directed at safety aspects of strcpy are based entirely on anecdotal social observations as opposed to formal facts, eg "programmers are lazy and incompetent, so don't let them use strcpy ".针对strcpy安全方面的流行批评完全基于轶事社会观察,而不是正式事实,例如“程序员是懒惰和无能的,所以不要让他们使用strcpy ”。 Taken in the context of C programming, this is, of course, utter nonsense.在 C 编程的上下文中,这当然是无稽之谈。 Following this logic we should also declare the division operator exactly as unsafe for exactly the same reasons.按照这个逻辑,出于完全相同的原因,我们还应该声明除法运算符完全不安全。

In reality, there are no problems with strcpy whatsoever.实际上, strcpy没有任何问题。 gets , on the other hand, is a completely different story, as I said above.另一方面, gets是一个完全不同的故事,正如我上面所说的。

yes, it is dangerous.是的,这很危险。 After 5 years of maintenance, your code will look like this:经过 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);


}

at that point, someone will go and change str1 to在这一点上,有人会去改变 str1 到

str1 = "THIS IS A REALLY LONG STRING WHICH WILL NOW OVERRUN ANY BUFFER BEING USED TO COPY IT INTO UNLESS PRECAUTIONS ARE TAKEN TO RANGE CHECK THE LIMITS OF THE STRING. AND FEW PEOPLE REMEMBER TO DO THAT WHEN BUGFIXING A PROBLEM IN A 5 YEAR OLD BUGGY PROGRAM" str1 = "这是一个非常长的字符串,现在将超出任何用于将其复制到的缓冲区,除非采取预防措施来检查字符串的限制。很少有人记得在使用 BLEMGFIX 时会这样做越野车计划”

and forget to look where str1 is used and then random errors will start happening...忘记查看 str1 的使用位置,然后随机错误将开始发生......

Your code is not safe.您的代码不安全。 The return value of malloc is unchecked, if it fails and returns 0 the strcpy will give undefined behavior. malloc的返回值是未经检查的,如果它失败并返回 0,则strcpy将给出未定义的行为。

Besides that, I see no problem other than that the example basically does not do anything.除此之外,除了示例基本上没有做任何事情之外,我认为没有其他问题。

strcpy isn't dangerous as far as you know that the destination buffer is large enough to hold the characters of the source string; strcpy并不危险,因为您知道目标缓冲区足够大以容纳源字符串的字符; otherwise strcpy will happily copy more characters than your target buffer can hold, which can lead to several unfortunate consequences (stack/other variables overwriting, which can result in crashes, stack smashing attacks & co.).否则strcpy会很高兴地复制比目标缓冲区可以容纳的更多的字符,这可能会导致一些不幸的后果(堆栈/其他变量覆盖,这可能导致崩溃、堆栈粉碎攻击等)。

But: if you have a generic char * in input which hasn't been already checked, the only way to be sure is to apply strlen to such string and check if it's too large for your buffer;但是:如果您在输入中有一个尚未检查过的通用char * ,唯一确定的方法是将strlen应用于此类字符串并检查它是否对您的缓冲区太大; however, now you have to walk the entire source string twice, once for checking its length, once to perform the copy.但是,现在您必须遍历整个源字符串两次,一次检查其长度,一次执行复制。

This is suboptimal, since, if strcpy were a little bit more advanced, it could receive as a parameter the size of the buffer and stop copying if the source string were too long;这是次优的,因为如果strcpy更先进一点,它可以接收缓冲区大小作为参数并在源字符串太长时停止复制; in a perfect world, this is how strncpy would perform (following the pattern of other strn*** functions).在完美的世界中,这就是strncpy执行方式(遵循其他strn***函数的模式)。 However, this is not a perfect world, and strncpy is not designed to do this.然而,这并不是一个完美的世界,而且strncpy并不是为了做到这一点而设计的。 Instead, the nonstandard (but popular) alternative is strlcpy , which, instead of going out of the bounds of the target buffer, truncates.相反,非标准(但流行)的替代方法是strlcpy ,它不会超出目标缓冲区的边界,而是截断。

Several CRT implementations do not provide this function (notably glibc), but you can still get one of the BSD implementations and put it in your application.一些 CRT 实现不提供此功能(特别是 glibc),但您仍然可以获得 BSD 实现之一并将其放入您的应用程序中。 A standard (but slower) alternative can be to use snprintf with "%s" as format string.标准(但较慢)的替代方法是使用snprintf"%s"作为格式字符串。

That said, since you're programming in C++ ( edit I see now that the C++ tag has been removed), why don't you just avoid all the C-string nonsense (when you can, obviously) and go with std::string ?也就是说,既然你用 C++ 编程(编辑我现在看到 C++ 标签已被删除),你为什么不避免所有的 C 字符串废话(如果你可以,显然)并使用std::string All these potential security problems vanish and string operations become much easier.所有这些潜在的安全问题都消失了,字符串操作变得更加容易。

The only way malloc may fail is when an out-of-memory error occurs, which is a disaster by itself. malloc 可能失败的唯一方式是发生内存不足错误,这本身就是一场灾难。 You cannot reliably recover from it because virtually anything may trigger it again, and the OS is likely to kill your process anyway.您无法可靠地从中恢复,因为几乎任何事情都可能再次触发它,而且操作系统很可能会终止您的进程。

As you point out, under constrained circumstances strcpy isn't dangerous.正如您所指出的,在受限情况下 strcpy 并不危险。 It is more typical to take in a string parameter and copy it to a local buffer, which is when things can get dangerous and lead to a buffer overrun.更典型的做法是接收字符串参数并将其复制到本地缓冲区,这时事情会变得危险并导致缓冲区溢出。 Just remember to check your copy lengths before calling strcpy and null terminate the string afterward.请记住在调用 strcpy 之前检查您的副本长度,然后 null 终止字符串。

除了可能取消引用 NULL(因为您没有检查 malloc 的结果)这是 UB 并且可能不是安全威胁之外,这没有潜在的安全问题。

gets() is always unsafe ; gets()总是不安全的 the other functions can be used safely.其他功能可以安全使用。
gets() is unsafe even when you have full control on the input -- someday, the program may be run by someone else.即使您完全控制输入, gets()也是不安全的——总有一天,程序可能会被其他人运行。

The only safe way to use gets() is to use it for a single run thing: create the source;使用gets()的唯一安全方法是将其用于单次运行:创建源; compile;编译; run;跑; delete the binary and the source;删除二进制文件和源代码; interpret results.解释结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM