c++常见安全漏洞与防御方法_c++代码审计与安全编程【必看】

C++安全需依托机制而非经验:禁用不安全C函数、用智能指针防悬空、检查整数溢出、RAII管理资源,并启用编译器保护与静态分析。

C++ 安全漏洞多源于内存失控、类型模糊和边界失察,不靠“经验”靠机制——用现代语言特性+静态检查+明确约定,才能真正堵住高危缺口。

缓冲区溢出:越界写入仍是头号杀手

数组下标不校验、getsstrcpysprintf 等 C 风格函数是经典雷区。C++ 中若用 raw pointer + 手动计算长度,同样危险。

  • 禁用所有不带长度检查的 C 字符串函数,改用 std::stringstd::array
  • std::vector::at() 替代 operator[](前者抛异常,后者不检查)
  • 对输入长度做前置断言:if (len > buf.size()) throw std::length_error("input too long");
  • 启用编译器保护:GCC/Clang 加 -fstack-protector-strong,MSVC 开启 /GS

悬空指针与 Use-After-Free

对象析构后仍被访问,或智能指针管理不当(如 get() 暴露裸指针又未严格管控生命周期),极易引发未定义行为。

  • 优先使用 std::unique_ptrstd::shared_ptr,避免裸指针传递所有权
  • 禁止长期保存 ptr.get() 返回的裸指针;若必须,加注释说明生命周期依赖关系
  • 容器中存储智能指针而非对象本身,防止迭代器失效+析构导致的悬空
  • 启用 AddressSanitizer(-fsanitize=address)在测试阶段捕获绝大多数 use-after-free

整数溢出与符号混淆

无符号整数下溢(0u - 1 → 极大正数)、有符号溢出(未定义行为)、size_t 与 int 混用(尤其循环变量),常导致逻辑绕过或越界访问。

  • 敏感计算前用 __builtin_add_overflow(GCC/Clang)或 std::add_overflow(C++23)做显式检查
  • 循环索引统一用有符号类型(如 int),除非明确需要 >=2³¹ 的索引
  • 接收用户输入的 size 参数,先转为 size_t 前验证非负且不过大(如 if (n 1024*1024) throw ...
  • 开启警告:-Wsign-conversion -Wconversion -Woverflow

RAII 失效与资源泄漏

忘记 close/fclose、释放失败未回滚、异常路径遗漏 cleanup,都会让文件句柄、内存、锁等资源持续泄漏,甚至引发拒绝服务。

  • 所有资源封装进 RAII 类:文件用 std::fstream,锁用 std::lock_guard,自定义资源写专属 wrapper
  • 构造函数中完成资源获取,失败则抛异常;析构函数确保释放(no-fail guarantee)
  • 避免在析构函数中抛异常(会终止程序);释放失败可记录日志,但不 throw
  • 用静态分析工具(如 Clang Static Analyzer、Cppcheck)扫描未关闭的 FILE* 或未 unlock 的 pthread_mutex_t

基本上就这些。C++ 的安全不是靠“小心”,而是把规则编进类型系统、编译流程和团队规范里。写得费点劲,但比半夜修 CVE 强得多。