文章目录
-
- 一、C/C++安全性编码和其他方面编码的规范和建议
-
- 1、什么是安全性编码?
- 2、是否需要它?
- 3、 安全编码实践的规则和建议
-
- Rules and Recommendations
-
- 1)Preprocessor(预处理器)
- 2)Declarations and Initialization(声明和初始化)
- 2)Expressions(表达式)
- 3)Integers(整数)
- 4)Floating Point(浮点数)
- 5)Array(数组)
- 6)Characters and Strings(字符和字符串)
- 7)Memory Management(内存管理)
- 8)Input/Outpu(输入输出)
- 9)Environment(环境)
- 10)Signal(信号)
- 11)Error Handling(错误处理)
- 12)Concurrency(并发)
- 13)Miscellaneous(其他方面)
- 4、安全代码合规检测机制
- 5、工业级安全编码规范的相关介绍
- 二、其他方面的编码实践
- 三、关于编码风格规范
- 四、一些基础概念和代码例子
- 五、References[参考资料]
一、C/C++安全性编码和其他方面编码的规范和建议
在此之前,我想说,认真地遵循好的Rules和Recommendations,但不要盲目跟从,对每一件事物都要有批判性思维,重在理解,而不是无脑背诵,即使面对权威也是如此。C语言安全编程的难度可能超乎许多有经验的程序员的想象,学会借助工具来辅助编写安全、可靠、稳固的代码。
1、什么是安全性编码?
可以抵御非法输入,在极端情况下也能稳健运行的代码。
2、是否需要它?
- 一个小例子:
- 如,缓冲区溢出漏洞,攻击者可以通过精心设计的输入,改写或覆盖调用函数栈的函数返回地址,从而执行非法的代码,成功地入侵了系统。(如ASCII码或其他编码上的特殊字符,’^'可表示0x5E十六进制数)
- 如果项目中到处都是不安全的编码,将会导致缓冲区溢出漏洞、I/O漏洞、栈粉碎等潜在的可怕错误。
- 在Cisco(思科)也在使用了工业级CERT C安全编码规范,作为所有C程序员的安全编码规范指导文件。
- 我刚开始学习和收集这方面信息的时候,也一直在问一个问题,我们现在的股票交易系统不是运行得好好的吗,为什么需要安全性编码?
- 答案是:可能是我想得还不够深远,也没有接触过大型的项目,所以不能体会到安全性编码的重要性。
- 举个例子,Mozilla基金会旗下有个(Firefox brower)火狐浏览器的产品,其中SVG(Scalable Vector Graphics)查看器中有一个堆缓冲区溢出漏洞,它是由于有符号整数unsigned int值和size_t值相乘时的无符号整数回绕造成的。一个那么大的项目,百万行级别代码的项目想要找出这个逻辑错误,可能并没有那么简单,至少会比像几万行Redis这个量级的项目要困难得多。
- 我们的交易系统以及现存的所有软件系统,都会随时公司的业务需求的复杂而变得更庞大,所以健壮性是衡量一个大型项目的重要指标,所以现在为什么像华为这样的公司都会研发自己的静态代码扫描工具,如TscanCode、Hexlic QAC、DeepCode(基于AI的静态代码扫描工具)等产品。
- 违反规则的编码实践有可能导致产生可被利用的漏洞。
3、 安全编码实践的规则和建议
- 配合代码demo来理解。
- 可先看,然后思考其中的rules为什么是这样的,再搜寻一些相关信息,再编写一些demo,可更深入了解一些安全编码规范背后的原理。
Rules and Recommendations
1)Preprocessor(预处理器)
>>>>> Rules:
0- Do not create a universal character name through concatenation.
1- Avoid side effects in arguments to unsafe macros.
2- Do not use preprocessor directives in invocations of function-like macros.
>>>>> Recommendations:
PRE00-C: 用内联函数或静态函数代替与函数相似的宏
PER01-C: 在宏参数名两边加上括号
PER02-C: 宏替换列表应该加上括号
PRE03-C: 应该使用typdef定义编码类型,而不是宏定义
PRE04-C: 不要复用标准头文件名,一定要不同与标准头文件的自定义头文件名
PRE05-C: 理解连接标记或执行字符串化时的宏替换,尤其在宏中嵌套有宏的##连接或#字符串化操作
2)Declarations and Initialization(声明和初始化)
>>>>> Rules:
0- A project shall not contain unused variables.(Golang也是这样的规则)
(从不使用的变量在代码中不应该存在,即定义了却未使用的变量)
1- Declare objects with appropriate storage durations.
2- Declare identifiers before using them.
PRE30-C:(3) Do not declare an identifier with conflicting linkage classifications.
PRE31-C:(4) Do not declare or define a reserved identifier.
PRE32-C:(5) Use the correct syntax when declaring a flexible array member.(灵活数组成员)
6- Avoid information leakage when passing a structure across a trust boundary.
7- Do not create incompatible declarations of the same function or object.
8- Do not declare variables inside a switch statement before the first case label.
----------------------------------------- C++ -----------------------------------------
0- Do not define a C-style variadic function.
1- Do not declare or define a reserved identifier.
2- Never qualify a reference type with const or volatile.
3- Do not write syntactically ambiguous declarations.
4- Overload allocation and deallocation functions as a pair in the same scope.
5- Avoid information leakage when passing a class object across a trust boundary.
6- Avoid cycles during initialization of static objects.
7- Do not let exceptions escape from destructors or deallocation functions.
2)Expressions(表达式)
>>>>> Rules:
EXP00-C:(0) The precedence of operators within expressions should be made explicit.
1- Do not depend on the order of evaluation for side effects.
2- Do not access a volatile object through a nonvolatile reference.
3- Do not read uninitialized memory.
4- Do not dereference null pointers.
5- Do not modify objects with temporary lifetime.
6- Do not cast pointers into more strictly aligned pointer types.
7- Call functions with the correct number and type of arguments.
8- Do not access a variable through a pointer of an incompatible type.
9- Do not modify constant objects.
EXP42-C(10): Do not compare padding data.(不要比较填充数据)
11- Avoid undefined behavior when using restrict-qualified pointers.
12- Do not rely on side effects in operands to sizeof, _Alignof, or _Generic.
13- Do not perform assignments in selection statements.
14- Do not use a bitwise operator with a Boolean-like operand.
3)Integers(整数)
INT30-C: Ensure that unsigned integer operations do not wrap.
(具体代码请参考Integer/CERT_INT30.h和.cpp)
INT31-C: Ensure that integer conversion do not result in lost or misinterpreted data.
INT32-C: Ensure that operations on signed integers do not result in overflow.
INT33-C: Ensure that division and remainder operations do not result in divide-by-zero errors.
INT34-C: Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand.
- In general, shifts should be performed only on unsigned operands.
- Use bitwise operators only on unsigned operands.(INT13-C)
(只能在无符号数上进行移位操作)
INT35-C: Use correct integer precisions.(使用正确整数的精度)
INT36-C: Converting a pointer to integer or intger to pointer.
- intptr_t and uniptr_t,是提高可移植性,用于32bit或64bit的系统平台上。
- 做正确的整数和指针直接的转换。
4)Floating Point(浮点数)
FLP30-C: Do not use floating-point variables as loop counters.
FLP32-C: Prevent or detect domain and range errors in math functions.
- 例如:function:acos(x), Domain: -1 <= x && x <= 1.
FLP34-C: Ensure that floating-point conversions are within range of the new type.
FLP36-C: Preserve precision when converting integeral values to floating-point type.
FLP37-C: Dot not use object representations to compare floating-point values.
5)Array(数组)
ARR30-C: Do not form or use out-of-bounds pointers or array subscripts.
ARR32-C: Ensure size arguments for variable length arrarys are in a valid range.
ARR36-C: Do not subtract or compare two pointers that do not refer to the same array.
ARR37-C: Do not add or subtract an integer to a pointer to a non-array object.
ARR38-C: Guarantee that library functions do not form invalid pointers.
ARR39-C: Do not add or subtract a scaled integer to a pointer.(!!!)
6)Characters and Strings(字符和字符串)
STR30-C: Do not attempt to modify string literals.
STR31-C: Guarantee that storage for strings has sufficient space for character data and the null terminator.
STR32-C: Do not pass a non-null-terminated character sequence to a library function that expects a string.
STR34-C: cast characters to unsigned char before converting to larger integer sizes.
STR37-C: Arguments to character-handling functions must be representable as an unsigned char.
STR38-C: Do not confuse narrow and wide character strings and functions.
- Passing narrow string arguments to wide string functions or wide string arguments to narrow string functions can lead to unexpected and undefined behavior.
- Narrow Strings with Wide String Functions, is undefined behavior.
7)Memory Management(内存管理)
MEM04-C: Beware of zero-length allocations.
MEM30-C: Do not access freed memory.
MEM31-C: Free dynamically allocated memory when no longer needed.
MEM33-C: Allocate and copy structures containing a flexible array member dynamically.
MEM34-C: Only free memory allocated dynamically.
MEM35-C: Allocate sufficient memory for an object.
- malloc的时候,要区分好sizeof(int)还是sizeof(long)
MEM36-C: Do not modify the alignment of objects by calling realloc().
8)Input/Outpu(输入输出)
FIO24-C: Do not open a file that is already open.
FIO30-C: Exclude user input from format strings.
FIO32-C: Do not perform operations on devices that are only appropriate for files.
FIO34-C: Distinguish between characters read from a file and EOF or WEOF.
FIO37-C: Do not assume that fgets() or fgetws() returns a nonempty string when successful.
FIO38-C: Do not copy a FILE object.
FIO39-C: Do not alternately input and output from a stream without an intervening flush or positioning call.
FIO40-C: Reset strings on fgets() or fgetws() failure.
FIO41-C: Do not call getc(), putc(), getwc(), or putwc() with a stream argument that has side effects.
FIO42-C: Close file when they are no longer needed.
FIO44-C: Only use values for fsetpos() that are returned from fgetpos().
FIO