C++ 了解 sprintf(...) 的危险
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3662899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
understanding the dangers of sprintf(...)
提问by Kevin Meredith
OWASPsays:
OWASP说:
"C library functions such as strcpy (), strcat (), sprintf () and vsprintf () operate on null terminated strings and perform no bounds checking."
“诸如 strcpy()、strcat()、sprintf() 和 vsprintf() 等 C 库函数对空终止字符串进行操作,并且不执行边界检查。”
sprintfwrites formatted data to string int sprintf ( char * str, const char * format, ... );
sprintf将格式化数据写入字符串 int sprintf ( char * str, const char * format, ... );
Example:
例子:
sprintf(str, "%s", message); // assume declaration and
// initialization of variables
If I understand OWASP's comment, then the dangers of using sprintf are that
如果我理解 OWASP 的评论,那么使用 sprintf 的危险在于
1) if message's length > str's length, there's a buffer overflow
1) 如果message的长度 > str的长度,则存在缓冲区溢出
and
和
2) if messagedoes not null-terminate with \0
, then messagecould get copied into strbeyond the memory address of message, causing a buffer overflow
2) 如果message不以 null 终止\0
,则message可能会被复制到超出message内存地址的str中,从而导致缓冲区溢出
Please confirm/deny. Thanks
请确认/拒绝。谢谢
采纳答案by strager
You're correct on both problems, though they're really both the same problem (which is accessing data beyond the boundaries of an array).
您在这两个问题上都是正确的,尽管它们实际上都是同一个问题(访问超出数组边界的数据)。
A solution to your first problem is to instead use std::snprintf
, which accepts a buffer size as an argument.
您的第一个问题的解决方案是改为使用std::snprintf
,它接受缓冲区大小作为参数。
A solution to your second problem is to give a maximum length argument to snprintf
. For example:
您的第二个问题的解决方案是为 提供最大长度参数snprintf
。例如:
char buffer[128];
std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");
// std::strcmp(buffer, "This is a test\n") == 0
If you want to store the entire string (e.g. in the case sizeof(buffer)
is too small), run snprintf
twice:
如果你想存储整个字符串(例如在sizeof(buffer)
太小的情况下),运行snprintf
两次:
int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");
++length; // +1 for null terminator
char *buffer = new char[length];
std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");
(You can probably fit this into a function using va
or variadic templates.)
(您可能可以使用va
或可变参数模板将其放入函数中。)
回答by John Dibling
Both of your assertions are correct.
你的两个断言都是正确的。
There's an additional problem not mentioned. There is no type checking on the parameters. If you mismatch the format string and the parameters, undefined and undesirable behavior could result. For example:
还有一个没有提到的额外问题。没有对参数进行类型检查。如果格式字符串和参数不匹配,可能会导致未定义和不良行为。例如:
char buf[1024] = {0};
float f = 42.0f;
sprintf(buf, "%s", f); // `f` isn't a string. the sun may explode here
This can be particularly nasty to debug.
这对于调试来说可能特别讨厌。
All of the above lead many C++ developers to the conclusion that you should never use sprintf
and its brethren. Indeed, there are facilities you can use to avoid all of the above problems. One, streams, is built right in to the language:
以上所有导致许多 C++ 开发人员得出结论,你永远不应该使用sprintf
它的兄弟。事实上,您可以使用一些工具来避免上述所有问题。一,流,内置于语言中:
#include <sstream>
#include <string>
// ...
float f = 42.0f;
stringstream ss;
ss << f;
string s = ss.str();
...and another popular choice for those who, like me, still prefer to use sprintf
comes from the boost Format libraries:
...对于像我一样仍然喜欢使用的人来说,另一个流行的选择sprintf
来自boost 格式库:
#include <string>
#include <boost\format.hpp>
// ...
float f = 42.0f;
string s = (boost::format("%1%") %f).str();
Should you adopt the "never use sprintf" mantra? Decide for yourself. There's usually a best tool for the job and depending on what you're doing, sprintf
just might be it.
您应该采用“从不使用 sprintf”的口头禅吗?自己决定。通常有适合这项工作的最佳工具,这取决于你在做什么,sprintf
可能就是它。
回答by T.E.D.
Yes, it is mostly a matter of buffer overflows. However, those are quite serious business nowdays, since buffer overflows are the prime attack vector used by system crackers to circumvent software or system security. If you expose something like this to user input, there's a very good chance you are handing the keys to your program (or even your computer itself) to the crackers.
是的,这主要是缓冲区溢出的问题。然而,现在这些都是非常严肃的事情,因为缓冲区溢出是系统破解者用来规避软件或系统安全的主要攻击媒介。如果您将这样的东西暴露给用户输入,那么您很有可能将您的程序(甚至您的计算机本身)的密钥交给破解者。
From OWASP's perspective, let's pretend we are writing a web server, and we use sprintf to parse the input that a browser passes us.
从 OWASP 的角度来看,让我们假设我们正在编写一个 Web 服务器,我们使用 sprintf 来解析浏览器传递给我们的输入。
Now let's suppose someone malicious out there passes our web browser a string far larger than will fit in the buffer we chose. His extra data will instead overwrite nearby data. If he makes it large enough, some of his data will get copied over the webserver's instructions rather than its data. Now he can get our webserver to execute his code.
现在让我们假设某个恶意的人向我们的 Web 浏览器传递了一个远远大于我们选择的缓冲区的字符串。他的额外数据将覆盖附近的数据。如果他把它做得足够大,他的一些数据将被复制到网络服务器的指令而不是它的数据。现在他可以让我们的网络服务器执行他的代码。
回答by MSalters
Your 2 numbered conclusions are correct, but incomplete.
您的 2 个编号结论是正确的,但不完整。
There is an additional risk:
还有一个额外的风险:
char* format = 0;
char buf[128];
sprintf(buf, format, "hello");
Here, format
is not NULL-terminated. sprintf()
doesn't check that either.
在这里,format
不是以NULL 结尾的。sprintf()
也不检查。
回答by supercat
The sprintf function, when used with certain format specifiers, poses two types of security risk: (1) writing memory it shouldn't; (2) reading memory it shouldn't. If snprintf is used with a size parameter that matches the buffer, it won't write anything it shouldn't. Depending upon the parameters, it may still read stuff it shouldn't. Depending upon the operating environment and what else a program is doing, the danger from improper reads may or may not be less severe than that from improper writes.
sprintf 函数与某些格式说明符一起使用时,会带来两种类型的安全风险:(1) 写入不该写入的内存;(2) 不应该读内存。如果 snprintf 与匹配缓冲区的大小参数一起使用,它不会写任何它不应该写的东西。根据参数,它可能仍会读取不应该读取的内容。根据操作环境和程序正在执行的其他操作,不正确读取的危险可能会也可能不会比不正确写入的危险更小。
回答by bta
Your interpretation seems to be correct. However, your case #2 isn't really a buffer overflow. It's more of a memory access violation. That's just terminology though, it's still a major problem.
你的解释似乎是正确的。但是,您的案例 #2 并不是真正的缓冲区溢出。这更像是内存访问冲突。这只是术语,它仍然是一个主要问题。
回答by Jacob N.
It is very important to remember that sprintf() adds the ASCII 0 character as string terminator at the end of each string. Therefore, the destination buffer must have at least n+1 bytes (To print the word "HELLO", a 6-byte buffer is required, NOT 5)
记住 sprintf() 在每个字符串的末尾添加 ASCII 0 字符作为字符串终止符非常重要。因此,目标缓冲区必须至少有 n+1 个字节(要打印“HELLO”一词,需要 6 个字节的缓冲区,而不是 5 个)
In the example below, it may not be obvious, but in the 2-byte destination buffer, the second byte will be overwritten by ASCII 0 character. If only 1 byte was allocated for the buffer, this would cause buffer overrun.
在下面的例子中,可能不是很明显,但是在 2 字节的目标缓冲区中,第二个字节将被 ASCII 0 字符覆盖。如果只为缓冲区分配了 1 个字节,则会导致缓冲区溢出。
char buf[3] = {'1', '2'};
int n = sprintf(buf, "A");
Also note that the return value of sprintf() does NOT include the null-terminating character. In the example above, 2 bytes were written, but the function returns '1'.
另请注意, sprintf() 的返回值不包括空终止字符。在上面的示例中,写入了 2 个字节,但该函数返回“1”。
In the example below, the first byte of class member variable 'i' would be partially overwritten by sprintf() (on a 32-bit system).
在下面的示例中,类成员变量“i”的第一个字节将被 sprintf()(在 32 位系统上)部分覆盖。
struct S
{
char buf[4];
int i;
};
int main()
{
struct S s = { };
s.i = 12345;
int num = sprintf(s.buf, "ABCD");
// The value of s.i is NOT 12345 anymore !
return 0;
}
回答by PYK
I pretty much have stated a small example how you could get rid of the buffer size declaration for the sprintf(if you intended to, of course!) and no snprintfenvolved ....
我几乎已经说明了一个小示例,说明如何摆脱sprintf的缓冲区大小声明(当然,如果您打算这样做!)并且不涉及snprintf....
Note: This is an APPEND/CONCATENATION example, take a look at here
注意:这是一个 APPEND/CONCATENATION 示例,请看这里