缓冲区溢出漏洞
Buffer overflow vulnerability
以下程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password)
{
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
if(strcmp(password_buffer, "unipi") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "SSL") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])!=0)
{
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Access Granted.\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
else
{
printf("\nAccess Denied.\n");
}
}
在第 strcpy(password_buffer, password);
行它有一个缓冲区溢出漏洞。如果我们不去掉strcpy
就想让这个程序安全,那怎么可能呢?
我在 Wiki 上找到了这个:
To prevent the buffer overflow from happening in this example, the
call to strcpy could be replaced with strncpy, which takes the maximum
capacity of A as an additional parameter and ensures that no more than
this amount of data is written to A:
strncpy(password_buffer, password, sizeof(A));
Note that the above code is not free from problems either; while a
buffer overrun has been prevented this time, the strncpy library
function does not null-terminate the destination buffer if the source
string's length is greater than or equal to the size of the buffer
(the third argument passed to the function), therefore A is, in this
case, not null-terminated and cannot be treated as a valid C-style
string.
简单。
您在其他地方更改了用户输入符合预期的验证。
使用 strlen 以便您可以检查字符串是否短于 16 个字节。
if(strlen(argv[1]) > 15)
{
printf("too long password\n");
exit(0);
}
有几种方法可以解决这个问题。
首先,也许是最重要的是,避免固定内存分配,尤其是在处理输入时。在上面的示例中,您可以将缓冲区设置为您需要的大小,而不是复制到固定大小的缓冲区。
char password_copy[strlen(password) + 1];
strcpy(password_copy, password);
但这并不总是可能的。也许你已经分配了内存。也许您确实想截断输入(尽管 16 is far too small for a password)。
一个是根本不使用 C 字符串处理函数,它们充满了缺陷和安全漏洞。而是使用像 Gnome Lib 这样的库,它具有 the G_String type。 G_Strings 跟踪它们的长度和分配的大小。这需要更多的内存,但速度更快。查找字符串的长度,这是经常发生的事情,不需要遍历字符串的每个字节。
G_Strings have their own set of string handling functions 比 C 的方便多了。它还可以根据需要增加字符串或为您分配新字符串。
/* Allocate memory for the copy and copy the password */
G_String *password_copy = g_string_new(password);
为了与常规字符串函数兼容 password_copy->str
returns 一个普通的 char *
.
这是 IMO 的最佳方式。您不再需要记住检查字符串长度和分配的大小,也不必担心在您使用字符串的任何地方出现空字节。你会忘记的。让电脑来做。
如果必须使用 C 标准函数,请不要使用 strncpy
because it fails to guarantee the truncated string will be null terminated. Instead use strlcpy
. It's like strncpy
but it guarantees the copied string will be null terminated. strlcpy
is a BSD extension, so it's not guaranteed to be portable. glibc refuses to implement it。
为了获得最大的便携性、效率和安全性,请使用 strlcpy
并使用 memmove
和 #ifndef strlcpy
提供回退,因此仅当 strlcpy
尚未使用时才会使用它可用。
#include <string.h>
#include <stdio.h>
#ifndef strlcpy
size_t strlcpy(char * restrict dst, const char * restrict src, size_t dst_size) {
/* size is the allocated size. len leaves space for the null byte */
size_t dst_len = dst_size - 1;
size_t src_len = strlen(src);
/* Use the smaller of the two string lengths to avoid buffer overflow */
size_t move_len = src_len > dst_len ? dst_len : src_len;
/* Copy the string, truncate if necessary. It will work
* even if src and dst overlap. */
memmove(dst, src, move_len);
/* Guarantee there's a null byte */
dst[move_len] = '[=12=]';
/* strlcpy returns the size of the string it tried to make.
* This is used to detect truncation. */
return src_len;
}
#endif
int main()
{
char dst[10];
char *src = "12345678901234567890";
printf("%zu\n", strlcpy(dst, src, 10));
printf("src: %s, dst: %s\n", src, dst);
return 0;
}
以下程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password)
{
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
if(strcmp(password_buffer, "unipi") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "SSL") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])!=0)
{
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Access Granted.\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
else
{
printf("\nAccess Denied.\n");
}
}
在第 strcpy(password_buffer, password);
行它有一个缓冲区溢出漏洞。如果我们不去掉strcpy
就想让这个程序安全,那怎么可能呢?
我在 Wiki 上找到了这个:
To prevent the buffer overflow from happening in this example, the call to strcpy could be replaced with strncpy, which takes the maximum capacity of A as an additional parameter and ensures that no more than this amount of data is written to A:
strncpy(password_buffer, password, sizeof(A));
Note that the above code is not free from problems either; while a buffer overrun has been prevented this time, the strncpy library function does not null-terminate the destination buffer if the source string's length is greater than or equal to the size of the buffer (the third argument passed to the function), therefore A is, in this case, not null-terminated and cannot be treated as a valid C-style string.
简单。 您在其他地方更改了用户输入符合预期的验证。 使用 strlen 以便您可以检查字符串是否短于 16 个字节。
if(strlen(argv[1]) > 15)
{
printf("too long password\n");
exit(0);
}
有几种方法可以解决这个问题。
首先,也许是最重要的是,避免固定内存分配,尤其是在处理输入时。在上面的示例中,您可以将缓冲区设置为您需要的大小,而不是复制到固定大小的缓冲区。
char password_copy[strlen(password) + 1];
strcpy(password_copy, password);
但这并不总是可能的。也许你已经分配了内存。也许您确实想截断输入(尽管 16 is far too small for a password)。
一个是根本不使用 C 字符串处理函数,它们充满了缺陷和安全漏洞。而是使用像 Gnome Lib 这样的库,它具有 the G_String type。 G_Strings 跟踪它们的长度和分配的大小。这需要更多的内存,但速度更快。查找字符串的长度,这是经常发生的事情,不需要遍历字符串的每个字节。
G_Strings have their own set of string handling functions 比 C 的方便多了。它还可以根据需要增加字符串或为您分配新字符串。
/* Allocate memory for the copy and copy the password */
G_String *password_copy = g_string_new(password);
为了与常规字符串函数兼容 password_copy->str
returns 一个普通的 char *
.
这是 IMO 的最佳方式。您不再需要记住检查字符串长度和分配的大小,也不必担心在您使用字符串的任何地方出现空字节。你会忘记的。让电脑来做。
如果必须使用 C 标准函数,请不要使用 strncpy
because it fails to guarantee the truncated string will be null terminated. Instead use strlcpy
. It's like strncpy
but it guarantees the copied string will be null terminated. strlcpy
is a BSD extension, so it's not guaranteed to be portable. glibc refuses to implement it。
为了获得最大的便携性、效率和安全性,请使用 strlcpy
并使用 memmove
和 #ifndef strlcpy
提供回退,因此仅当 strlcpy
尚未使用时才会使用它可用。
#include <string.h>
#include <stdio.h>
#ifndef strlcpy
size_t strlcpy(char * restrict dst, const char * restrict src, size_t dst_size) {
/* size is the allocated size. len leaves space for the null byte */
size_t dst_len = dst_size - 1;
size_t src_len = strlen(src);
/* Use the smaller of the two string lengths to avoid buffer overflow */
size_t move_len = src_len > dst_len ? dst_len : src_len;
/* Copy the string, truncate if necessary. It will work
* even if src and dst overlap. */
memmove(dst, src, move_len);
/* Guarantee there's a null byte */
dst[move_len] = '[=12=]';
/* strlcpy returns the size of the string it tried to make.
* This is used to detect truncation. */
return src_len;
}
#endif
int main()
{
char dst[10];
char *src = "12345678901234567890";
printf("%zu\n", strlcpy(dst, src, 10));
printf("src: %s, dst: %s\n", src, dst);
return 0;
}