当前位置: 首页 > news >正文

通用安全指南

第4章通用安全指南

4.1C/C++使用错误

4.1.1不得直接使用无长度限制的字符拷贝函数

使用无长度限制的函数,它会引发中风险漏洞和高风险漏洞:信息泄露漏洞和缓冲区溢出漏洞。

不应直接使用legacy的字符串拷贝、输入函数,如strcpy、strcat、sprintf、wcsscpy、mbscpy等,这些函数的特征是:可以输出一长串字符串,而不限制长度。如果环境允许,应当使用其_s安全版本替代,或者使用n版本函数(如:snprintf、vsnprintf)。

若使用形如sscanf之类的函数时,在处理字符串输入时应当通过%10s这样的方式来严格限制字符串长度,同时确保字符串末尾有\0。如果环境允许,应当使用_s安全版本。

转移字符:

所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示。而C中定义了一些字母前加“\”来表示常见的那些不能显示的ASCII字符,如\0, \t, \n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了。例如:\0表示空字符,作为字符串结束符使用。\t表示水平制表符(相当于Tab键)。\n表示换行符,可用于输出时的结束符。

转移字符的使用场合:

  1. C语言中的转义字符

有一些字符,如回车符、退格符等控制码,它们不能在屏幕上显示,也无法从键盘输入,只能用转义字符来表示。转义字符由反斜杠加上一个字符或者数字组成,它把反斜杠后面的字符或数字转换成别的意义。

在使用n系列拷贝函数时,要确保正确计算缓冲区长度,同时,如果你不确定是否代码在各个编译器下都能确保末尾有0时,建议可以适当增加1字节输入缓冲区,并将其置为\0,以保证输出的字符串结尾一定有\0。

字符型常量

C语言中字符型常量所表示的值是int型所能包含的值。我们可以用ASCII表达式来表示一个字符型常量,或者用单引号内加反斜杠表示转义字符。 例如: 'A', '\x2f', '\013'; 其中:\x表示后面的字符是十六进制数,\0表示后面的字符是八进制数。例如十进制的17用十六进制表示就是'\x11',用八进制表示就是'\021';上面我们见到的\x, \n, \a等等都是叫转义字

编程语言

HTML 转义符、Java 转义符、XML 转义符、Oracle 转义符、SQL 转义符、SQL Server 转义符、PHP 转义符、ASP 转义符、VB 转义符、JavaScript 转义符等,还有网址中的百分号。

例如:HTML 的转义字符:<、>、&、"、©,分别是 <、>、&、"、© 的转义字符。XML 只有 5 个转义符:<、>、&、"、'。

作用

转义字符串(Escape Sequence)也称字符实体(Character Entity)。

在HTML中,定义转义字符串的原因有两个:

第一个原因是像“<”和“>”这类符号已经用来表示HTML标签,因此就不能直接当做文本中的符号来使用。为了在HTML文档中使用这些符号,就需要定义它的转义字符串。当解释程序遇到这类字符串时就把它解释为真实的字符。在输入转义字符串时,要严格遵守字母大小写的规则。

第二个原因是,有些字符在ASCII字符集中没有定义,因此需要使用转义字符串来表示。

其实所有编程语言,拥有转义字符的原因基本上是两点:

使用转义字符来表示字符集中定义的字符,比如ASCII里面的控制字符及回车换行等字符,这些字符都没有现成的文字代号。所以只能用转义字符来表示。

某一些特定的字符在编程语言中被定义为特殊用途的字符。这些字符由于被定义为特殊用途,它们失去了原有的意义。比如说Html中,<被HTML定义为标签的开始,所以当我们转入<时,HTML就会把它当作为开始,而不是当作一个<来看待。再如PHP的双引号("),被PHP定义为字符串的外围标签,所以如果你在一对双引号里面,还想要使用双引号,只能使用转义字符了。不然PHP就会报错了。

第三个原因,出于网站的安全。在数据写入数据库前,都会使用转义字符(函数)对一些敏感字符进行转义。这样做可以避免一些别有用心的人利用特殊符号的注入攻击。

显示空格

通常情况下,HTML会自动截去多余的空格。不管加多少空格,都被看做一个空格。例如在两个字之间加了10个空格,HTML会截去9个空格,只保留一个。为了在网页中增加空格,可以使用   表示空格。

URL 编码

在Web开发中,通过英文问号(?)方式在浏览器地址栏中传值。浏览器是通过“&”来区分问号后的参数个数的。如果出现传值参数中带有“&”时,在接收页面就会出现错误。例如请求路径:/next.jsp?param1=hendhs89&furej& param2=sss

参数param1中含有转义字符“&”,这样会导致被请求页的参数接收错误。在传值前,通过java.net.URLEncoder.encode(param1)编码处理后,可将转义字符转为16进制。

一些需要注意的函数,例如 strncpy 和 _snprintf 是不安全的。strncpy 不应当被视为 strcpy 的n系列函数,它只是恰巧与其他n系列函数名字很像而已。strncpy 在复制时,如果复制的长度超过n,不会在结尾补\0。

同样,MSVC snprintf 系列函数在超过或等于n时也不会以\0结尾。如果后续使用非\0结尾的字符串,可能泄露相邻的内容或者导致程序崩溃。

// Bad

char a[4] = {0};

_snprintf(a, 4, "%s", "AAAA");

foo = strlen(a);

上述代码在MSVC中执行后,a[4] == 'A',因此字符串未以\0结尾。a的内容是"AAAA",调用 strlen(a) 则会越界访问。因此,正确的操作举例如下:

// Good

char a[4] = {0};

_snprintf(a, sizeof(a), "%s", "AAAA");

a[sizeof(a) - 1] = '\0';

foo = strlen(a);

在 C++ 中,强烈建议使用 string、vector 等更高封装层次的基础组件代替原始指针和动态数组,对提高代码的可读性和安全性都有很大的帮助。

关联漏洞:

中风险 - 信息泄露

低风险 - 拒绝服务

高风险 - 缓冲区溢出

4.1.2 创建进程类的函数的安全规范

使用 system、WinExec、CreateProcess、ShellExecute 等启动进程类的函数时,需要严格检查其参数。

启动进程需要加上双引号,错误例子:

WinExec("D:\\program files\\my folder\\foobar.exe", SW_SHOW);

当存在 D:\program files\my.exe 的时候,my.exe 会被启动。而 foobar.exe 不会启动。

WinExec("\"D:\\program files\\my folder\\foobar.exe\"", SW_SHOW);

另外,如果启动时从用户输入、环境变量读取组合命令行时,还需要注意是否可能存在命令注入。

// Bad

std::string cmdline = "calc ";

cmdline += user_input;

system(cmdline.c_str());

比如,当用户输入 1+1 && ls 时,执行的实际上是 calc 1+1 和 ls 两个命令,导致命令注入。需要检查用户输入是否含有非法数据。

std::string cmdline = "ls ";

cmdline += user_input;

if(cmdline.find_first_not_of("1234567890.+-*/e ") == std::string::npos)

    system(cmdline.c_str());

else

    warning(...);

关联漏洞: 高风险 - 代码执行

 高风险 - 权限提升

4.1.3 尽量减少使用 _alloca 和可变长度数组

_alloca 和可变长度数组使用的内存量在编译期间不可知。尤其是在循环中使用时,根据编译器的实现不同,可能会导致:

栈溢出,即拒绝服务;

缺少栈内存测试的编译器实现可能导致申请到非栈内存,并导致内存损坏。这在栈比较小的程序上,例如IoT设备固件上影响尤为大。对于 C++,可变长度数组也属于非标准扩展,在代码规范中禁止使用。

ASLR保护

ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。据研究表明ASLR可以有效的降低缓冲区溢出攻击的成功率,如今Linux、FreeBSD、Windows等主流操作系统都已采用了该技术。

PE头文件中会设置:IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE,标示来说明其支持ASLR。

4.1.5 防止泄露指针(包括%p)的值

所有 printf 系列函数,要防止格式化完的字符串泄露程序布局信息。例如,如果将带有 %p 的字符串泄露给程序,则可能会破坏 ASLR(地址空间布局随机化)的防护效果,使得攻击者更容易攻破程序。

%p 的值只应当在程序内使用,而不应当输出到外部或被外部以某种方式获取。

4.1.6 不应把用户可修改的字符串作为 printf 系列函数的“format”参数

如果用户可以控制字符串,则通过 %n %p 等内容,最坏情况下可以直接执行任意恶意代码。在以下情况尤其需要注意:WIFI名,设备名等。

4.1.7 对数组 delete 时需要使用 delete[]

`delete []` 操作符用于删除数组。`delete` 操作符用于删除非数组对象。它们分别调用 `operator delete[]` 和 `operator delete`。

不良示例:

Foo* b = new Foo[5];

delete b;  // trigger assert in DEBUG mode

在 `new[]` 返回的指针上调用 `delete` 将取决于编译器的未定义行为。代码中存在对未定义行为的依赖是错误的。

良好示例

Foo* b = new Foo[5];

delete[] b;

在 C++ 代码中,使用 `string`、`vector`、智能指针(比如 `std::unique_ptr`)等可以消除绝大多数 `delete[]` 的使用场景,并且代码更清晰。

关联漏洞:

高风险 - 内存破坏

中风险 - 逻辑漏洞

低风险 - 内存泄漏

低风险 - 拒绝服务

4.1.8 注意隐式符号转换

当两个无符号数相减为负数时,结果应当为一个很大的无符号数,但是小于 int 的无符号数在运算时可能会有预期外的隐式符号转换。

4.2 不推荐的编程习惯

4.2.1 switch中应有default

在 switch 语句中应该有 default 分支,以处理各种预期外的情况。这样可以确保 switch 能够接受用户输入,或者在后期其他开发者修改函数后,确保 switch 仍可以覆盖到所有情况,并确保逻辑正常运行。

中风险 - 逻辑漏洞中风险 - 内存泄漏

4.2.2 不应当在Debug或错误信息中提供过多内容

包含过多信息的Debug消息不应当被用户获取到。Debug信息可能会泄露一些值,例如内存数据、内存地址等内容,这些内容可以帮助攻击者在初步控制程序后,更容易地攻击程序。

关联漏洞:

中风险 - 信息泄漏

4.2.3 不应该在客户端代码中硬编码对称加密秘钥

不应在客户端代码中硬编码对称加密秘钥,例如:不应在客户端代码使用硬编码的 AES/ChaCha20-Poly1305/SM1 密钥,使用固定密钥的程序基本和没有加密一样。

如果业务需求是认证加密数据传输,应优先考虑直接用 HTTPS 协议。

如果是其他业务需求,可考虑由服务器端生成对称秘钥,客户端通过 HTTPS 等认证加密通信渠道从服务器拉取。

或者根据用户特定的会话信息,比如登录认证过程可以根据用户名用户密码业务上下文等信息,使用HKDF 等算法衍生出对称秘钥。

又或者使用 RSA/ECDSA + ECDHE 等进行认证秘钥协商,生成对称秘钥。

关联漏洞:

中风险 - 信息泄漏

4.2.4 返回栈上变量的地址

对于 C++ 程序来说强烈建议:返回 string、vector 等类型,会让代码更加简单和安全。

关联漏洞:

高风险 - 内存破坏

4.2.5 有逻辑联系的数组必须仔细检查

例如:将字符串转换为 week day,但是两个数组并不一样长,导致程序可能会越界读一个 int。

关联漏洞:

高风险 - 内存破坏

4.2.6 避免函数的声明和实现不同

在头文件、源代码、文档中列举的函数声明应当一致,不应当出现定义内容错位的情况。

关联漏洞:

中风险 - 逻辑问题

4.2.7 检查复制粘贴的重复代码(相同代码通常代表错误)

当开发中遇到较长的句子时,如果选择了复制粘贴语句,请记得检查每一行代码,不要出现上下两句一模一样的情况,这通常代表代码哪里出现了错误。

最好是把重复的代码片段提取成函数,如果函数比较短,可以考虑定义为 inline 函数,在减少冗余的同时也能确保不会影响性能。

关联漏洞:

中风险 - 逻辑问题

4.2.8 左右一致的重复判断/永远为真或假的判断(通常代表错误)

4.2.9 函数每个分支都应有返回值

开启适当级别的警告(GCC 中为 -Wreturn-type 并包含在 -Wall 中)并设置为错误,可以在编译阶段发现这类错误。

关联漏洞:

中风险 - 逻辑问题

中风险 - 信息泄漏

4.2.10 不得使用栈上未初始化的变量

关联漏洞:

中风险 - 逻辑问题

中风险 - 信息泄漏

4.2.11 不得直接使用刚分配的未初始化的内存(如 realloc)

一些刚申请的内存通常是直接从堆上分配的,可能包含有旧数据的,直接使用它们而不初始化,可能会导致安全问题。例如,CVE-2019-13751。应确保初始化变量,或者确保未初始化的值不会泄露给用户。

在 C++ 中,再次强烈推荐用 string、vector 代替手动内存分配。

关联漏洞:

中风险 - 逻辑问题

中风险 - 信息泄漏

4.2.12 校验内存相关函数的返回值

与内存分配相关的函数需要检查其返回值是否正确,以防导致程序崩溃或逻辑错误。

关联漏洞:

中风险 - 逻辑问题

高风险 - 越界操作

4.2.13 不要在 if 里面赋值

4.3 多线程

4.3.1 变量应确保线程安全性

对于 C 代码,C11 后推荐使用 atomic 标准库。对于 C++ 代码,C++11 后,推荐使用 std::atomic。

关联漏洞:

高风险 - 内存破坏

中风险 - 逻辑问题

4.3.2 注意 signal handler 导致的条件竞争

竞争条件经常出现在信号处理程序中,因为信号处理程序支持异步操作。攻击者能够利用信号处理程序争用条件导致软件状态损坏,从而可能导致拒绝服务甚至代码执行。

当信号处理程序中发生不可重入函数或状态敏感操作时,就会出现这些问题。

因为信号处理程序中随时可以被调用。例如,当在信号处理程序中调用 free 时,通常会出现另一个信号争用条件,从而导致双重释放。即使给定指针在释放后设置为 NULL,在释放内存和将指针设置为 NULL 之间仍然存在竞争的可能。

为多个信号设置了相同的信号处理程序,这尤其有问题——因为这意味着信号处理程序本身可能会重新进入。

例如,malloc() 和 free() 是不可重入的,因为它们可能使用全局或静态数据结构来管理内存,并且它们被 syslog() 等看似无害的函数间接使用;这些函数可能会导致内存损坏和代码执行。

可以借由下列操作规避问题:

避免在多个处理函数中共享某些变量。

在信号处理程序中使用同步操作。

屏蔽不相关的信号,从而提供原子性。

避免在信号处理函数中调用不满足异步信号安全的函数。

关联漏洞:

高风险 - 内存破坏

中风险 - 逻辑问题

4.3.3 注意 Time of check Time of use (TOCTOU) 条件竞争

TOCTOU:软件在使用某个资源之前检查该资源的状态,但是该资源的状态可以在检查和使用之间更改,从而使检查结果无效。当资源处于这种意外状态时,这可能会导致软件执行错误操作。

当攻击者可以影响检查和使用之间的资源状态时,此问题可能与安全相关。这可能发生在共享资源(如文件、内存,甚至多线程程序中的变量)上。在编程时需要注意避免出现TOCTOU问题。

TOCTOU 难以修复,但是有以下缓解方案:

限制对来自多个进程的文件的交叉操作。如果必须在多个进程或线程之间共享对资源的访问,那么请尝试限制“检查”(CHECK)和“使用”(USE)资源之间的时间量,使他们相距尽量不要太远。这不会从根本上解决问题,但可能会使攻击更难成功。

在 Use 调用之后重新检查资源,以验证是否正确执行了操作。确保一些环境锁定机制能够被用来有效保护资源。但要确保锁定是在检查之前进行的,而不是在检查之后进行的,以便检查时的资源与使用时的资源相同。

关联漏洞:

高风险 - 内存破坏

中风险 - 逻辑问题

4.4 加密解密

4.4.1 不得明文存储用户密码等敏感数据

http://www.dtcms.com/a/328726.html

相关文章:

  • 关于在img标签的src里面直接使用“~/assets/images/xxx“可以,但是若将这个路径写成变量的形式就会报错
  • Java Stream API 中常用方法复习及项目实战示例
  • BGP综合实验_Te. BGP笔记
  • 七大排序算法全解析:从入门到精通
  • 开源模型应用落地-用LLaMA-Factory点亮Qwen3-4B的“读心术”(十九)
  • Java开发环境搭建(WIN+IDEA+Maven)
  • davici configurator 报错:License file of SIP has no valid checksum.
  • 高可用实战之Nginx + Apache篇
  • 【IntelliJ IDEA】如何在pom.xml中去除maven中未使用的依赖
  • EI学术会议 | 低碳经济、可持续发展
  • 人机虚拟样机仿真
  • Linux的进程信号
  • 开发WPF项目时遇到的问题总结
  • 《吃透 C++ 类和对象(中):构造函数与析构函数的核心逻辑》
  • WPF 开发的瑞士军刀:Prism 框架从入门到精通指南
  • k8s兼容沐曦c500
  • 【AI实践】本地部署ASR模型OpenAI Whisper
  • Kafka工作机制深度解析:Broker、Partition 与消费者组协作原理
  • 自由学习记录(83)
  • Linux 软件编程:文件IO、目录IO、时间函数
  • GitHub分支保护介绍(Branch Protection)(git分支保护)(通过设置规则和权限来限制对特定分支的操作的功能)
  • 11.用反射为静态类的属性赋值 C#例子 WPF例子
  • K8S中,kubectl cordon、uncordon、drain、taint的区别
  • 计算机网络---用户数据报协议User Datagram Protocol(UDP)
  • 【Part 4 未来趋势与技术展望】第一节|技术上的抉择:三维实时渲染与VR全景视频的共生
  • vue--video使用动态src时,视频不更新
  • Java零基础笔记16(Java编程核心:存储读写数据方案—File文件操作、IO流、IO框架)
  • 利用生成式AI与大语言模型(LLM)革新自动化软件测试 —— 测试工程师必读深度解析
  • PythonDay25
  • Android实现Glide/Coil样式图/视频加载框架,Kotlin