Bootstrap

非安全函数

在C++中,非安全函数通常是指那些在使用时容易引发安全问题(如缓冲区溢出等)的函数。以下是一些常见的非安全函数:

字符串处理函数

  • strcpy():用于复制字符串。如果目标字符串空间不足,会导致缓冲区溢出。例如:

    char dest[10];
    char src[] = "Hello, World!";
    strcpy(dest, src);  // 目标数组dest空间只有10个字符,而src有14个字符(包括'\0'),会溢出

    安全替代:strncpy(),它可以限制复制的字符数,但需要注意的是,如果源字符串长度大于目标数组长度,strncpy()不会自动在目标字符串末尾添加空字符,可能会导致目标字符串未正确终止。

  • strcat():用于连接字符串。同样存在缓冲区溢出的风险。比如:

    char str1[10] = "Hello";
    char str2[] = " World!";
    strcat(str1, str2);  // str1长度只有10,连接str2后会溢出

    安全替代:strncat(),可以指定连接的最大字符数。

输入输出函数

  • gets():用于从标准输入读取一行字符串,不会检查目标缓冲区的大小,容易造成缓冲区溢出。例如:

    char buffer[10];
    gets(buffer);  // 用户输入超过9个字符(不包括'\0')就会溢出

    安全替代:fgets(),可以指定读取的最大字符数,包括换行符。

内存操作函数

  • memcpy():用于内存拷贝。如果目标内存空间不足,或者源地址和目标地址有重叠区域且拷贝方向不当,也会引发问题。例如:

    char arr[10] = "abcdefghij";
    memcpy(arr + 2, arr, 10);  // 源地址和目标地址有重叠,且拷贝长度超过了剩余空间,会导致未定义行为

    安全替代:memmove(),它允许源地址和目标地址有重叠区域,并且会正确处理拷贝方向。

在使用这些非安全函数时,一定要格外小心,确保目标缓冲区或内存空间足够大,避免出现安全漏洞。在现代C++编程中,推荐使用更安全的库函数或智能指针等现代C++特性来降低安全风险。

以下是一些C++安全编程的最佳实践:

使用安全函数替代非安全函数

  • 字符串处理

    • strcpy_s()替代strcpy(),前者通过要求提供目标缓冲区大小参数,防止缓冲区溢出,例如char src[] = "Hello, World!"; char dest[20]; strcpy_s(dest, sizeof(dest), src);

    • strncpy_s()替代strncpy()strncpy_s()可以指定缓冲区大小,避免溢出,如char src[] = "Hello, World!"; char dest[20]; strncpy_s(dest, sizeof(dest), src, 5); dest[5] = '\0';

    • strncat_s()替代strcat()strncat_s()要求提供目标缓冲区大小参数,防止缓冲区溢出,比如char dest[20] = "Hello"; char src[] = "World"; strncat_s(dest, sizeof(dest), src, 3);

    • sprintf_s()替代sprintf()sprintf_s()可以限制目标缓冲区大小,避免溢出。

  • 内存操作:用memmove()替代memcpy()memmove()允许源地址和目标地址有重叠区域,并且会正确处理拷贝方向,例如char arr[10] = "abcdefghij"; memmove(arr + 2, arr, 5);

使用智能指针管理动态内存

  • std::unique_ptr:提供独占式所有权的内存管理,确保一个时刻只有一个指针拥有所管理的对象,并在指针超出作用域时自动释放内存。例如std::unique_ptr<int> ptr(new int(10));,也可以使用std::make_unique<int>(10);来创建,避免使用new关键字。unique_ptr不允许复制,但可以通过std::move转移所有权,如auto ptr1 = std::make_unique<int>(10); std::unique_ptr<int> ptr2 = std::move(ptr1);unique_ptr还可以管理动态分配的数组,如std::unique_ptr<int[]> arr(new int[5]);

  • std::shared_ptr:是一种引用计数智能指针,允许多个指针拥有同一个对象,当最后一个拥有该对象的shared_ptr被销毁时,会自动释放所指向的内存。使用std::make_shared可以高效地创建一个shared_ptr实例。

  • std::weak_ptr:与shared_ptr配合使用,主要用于解决shared_ptr循环引用的问题,weak_ptr不会增加引用计数,可以用来打破潜在的循环引用,防止内存无法释放。在访问所引用的对象前必须先转换为std::shared_ptr

输入验证和过滤

  • 对所有输入数据进行类型和格式检查,确保数据符合预期的规范,利用正则表达式来验证输入数据的合法性,拒绝不符合规则的输入。

  • 在处理输入之前,对特殊字符进行转义或编码,以防止恶意用户利用这些字符来注入恶意代码,如SQL注入、跨站脚本(XSS)等。

错误处理和异常安全

  • 使用异常处理机制来处理程序错误,良好的异常处理策略有助于提高程序的健壮性和用户满意度。例如,对于可能出现除零错误的代码,可以使用try-catch块来捕获并处理异常。

  • 在使用智能指针时,尽量使用make_sharedmake_unique,不仅减少代码冗余,还可以优化内存分配性能,减少异常情况下的资源泄漏。

避免使用不安全的函数和特性

  • 不要使用system函数或任何可以执行外部命令的函数,以防止命令注入。

  • 避免混用裸指针和智能指针,智能指针的存在是为了取代裸指针的手动管理,如果混用两者,可能导致资源重复释放或泄漏。

其他注意事项

  • 设置正确的文件和目录权限,以限制未授权用户的访问内容。

  • 使用最小权限原则,即只授予用户完成工作所需的最低权限。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;