CC++安全编程要点
1. 字符串操作安全
字符串操作不当会导致字符串缓存区被恶意代码和数据覆盖破坏,进⽽引发拒绝服务攻击(合法⽤户⽆法正常使⽤)以及执⾏任意代码,⽐如绕过验证。
2. 格式化函数
a) 格式化操作常见的问题包括格式化参数类型不匹配、参数数⽬不匹配、格式化字符串长度没有限制、以及字符串的全部或者部分由
⽤户输⼊;
b) 格式化函数使⽤不当会导致程序由于未定义⾏为⽽异常终⽌(拒绝服务),甚⾄执⾏任意代码(查看栈内容、改写内存、执⾏任意
代码)。
3. 整数操作安全
a) 整数操作不当的常见场景包括有/⽆符号整数运算溢出、整形转换时出现数据截断、以及对有符号整数使⽤为操作符运算
b) 整数操作不当会导致内存分配出错(分配过⼤内存或0内存)、以及执⾏任意代码(整数溢出可导致内存破坏)
c) 有符号整数溢出的结果是不确定的,⽆符号整数溢出会出现反转环绕,即对⽐(类型最⼤值+1)取模
d) 使⽤整数要多校验数值的合法性
4. 内存(堆)管理安全
a) 常见的危险的堆操作包括内存拷贝/分配时未检验合理性、引⽤空指针/野指针/未定义指针/已释放内存的指针、使⽤不匹配的内存
管理操作、重复释放、使⽤有隐患的函数⽐如realloc和alloca等;
b) 内存管理使⽤不当会导致拒绝服务攻击(⽐如程序崩溃)、执⾏任意代码(溢出越界)等
5. ⽂件操作安全
a) 常见的⽂件操作问题包括⽂件操作返回值判断有误(通常⽤int⽽不是char之类的)、⽂件创建时没有指定合适的⽂件权限(⽐如为
防⽌其他进程访问重要⽂件,需要以独占⽅式打开)、对⽂件的路径校验不够完善(使⽤相对路径、权限突破)、条件竞争漏洞(TOCTOU,time-of-check time-of-use,利⽤从检查到⽂件资源到使⽤⽂件这段间隙,发动扩⼤权限的攻击,⽐如利⽤符号链接进⾏偷天换⽇)、使⽤⽂件名为依据对⽂件进⾏操作(⽂件名与⽂件本⾝的关系是松绑定的,容易发⽣⽂件被替换⽽不知)。
b) ⽂件操作不当会导致重要⽂件丢失、关键⽂件被不预期的访问者查看甚⾄执⾏
6. 内核操作安全
a) 内核mmap接⼝实现中要对映射起始地址和⼤⼩进⾏合法性校验,否则会导致⽤户态可以读写任意内核地址,甚⾄通过传参执⾏任
意代码
b) 内核程序中必须通过内核专⽤函数读写⽤户态缓存区,⽐如copy_from_user() copy_to_user() put_user()和get_user(),这些
函数都有内部校验,否则如果直接引⼊⽤户态传⼊的指针,可导致内核崩溃、读写任意地址等
c) 使⽤copy_from_user()时要对拷贝长度进⾏校验,否则可造成内核缓冲区溢出,导致内核panic或提权
d) 使⽤copy_to_user()时要对缓冲区进⾏初始化防⽌内核数据泄露
e) 在异常处理中使⽤BUG_ON宏会造成内核panic,推荐使⽤WARN_ON宏。
f) 临时通过set_fs关闭地址校验后,在操作完成后必须及时恢复,否则PXN机制就会失效,使得漏洞利⽤变得更容易
g) 在中断处理程序或者持有⾃旋锁的进程上下⽂代码中禁⽌使⽤可能会引起休眠(⽐如vmalloc() msleep()等)、阻塞(⽐如
copy_from_user() copy_to_user()等)或者耗费⼤量时间(⽐如printk()等)的函数。
h) 合理使⽤内核栈,防⽌内核栈溢出,因为linux内核栈⼤⼩是固定的,资源⾮常宝贵,不合理的使⽤内核站可能会导致栈溢出、系统
挂死,因此在栈上申请内存空间时不要超过内核栈⼤⼩,注意函数嵌套,以及不要定义过多的变量。
7. 命令注⼊
a) 可以⽤POSIX的exec系列函数或Win32 API CreateProcess()等与命令解释器⽆关的进程创建函数来替代system()/popen(),
以避免命令注⼊漏洞。
b) 若使⽤system函数,则应使⽤内部硬编码的命令或者外部输⼊进⾏合法性校验
8. 不安全的随机数的随机性不够好,随机数容易预测,容易导致安全机制失效,禁⽌⽤来产⽣安全⽤途的随机数(⽐如sessionID的⽣
成、挑战认证算法中的随机数、验证码、重要随机⽂件的随机⽂件名、密码相关)。建议Unix/Linux平台下读取/dev/random⽂件,Windows使⽤随机数⽣成函数CryptGenRandom(),使⽤开源组件openssl,使⽤公司开发的iPSI组件,如有可能,则使⽤硬件随机数。
9. 多线程下禁⽌使⽤不安全函数,否则会导致拒绝服务(数据冲突导致程序运⾏不正常甚⾄崩溃),以及执⾏任意代码(攻击者通过其
它线程写⼊恶意数据代码)。线程不安全函数包括strtok(内部使⽤静态变量)、printf和cout混⽤(⼆者输出的机制不同,cout有缓存机制,混⽤会导致不可预知的错误,打印结果不符合预期,甚⾄内部缓存区溢出程序崩溃),以及在信号处理程序中使⽤异步不安全函数,⽐如I/O。C标准库中的abort、
_Exit、quick_exit和signal函数可以在信号处理程序中安全调⽤。
安全函数,⽐如I/O。C标准库中的abort、_Exit、quick_exit和signal函数可以在信号处理程序中安全调⽤。
10. 安全函数的安全特性包括
a) 强化边界检查,添加buffer长度参数避免溢出
b) 保证结果字符串以\0结尾,避免访问buffer边界之外的信息
c) 发现缓存区溢出发⽣时,将⽬的缓存区的⾸字节置零
d) 添加错误返回值⽅便快速定位
e) 增强⼊参检查
f) 增加⼊参的内存重叠检查,⽐如memcpy
g) 定义多个宏⽅便⽤户根据实际情况定制使⽤
11. std::ostrstream是不安全的,因为当缓存区不够⼤时,str()会调⽤freeze函数冻结字符串,容易造成内存泄露和数据丢失,str不会
附加字符串终⽌符号\0,data返回所有字符串没有附带\0终⽌符,容易导致内存访问越界缓存区溢出等问题。std::stringstream没有这些问题,可以作为替代⽅案
12. C++代码中应优先使⽤C++类库函数,因为⽐C标准函数有更好的安全性,⽐如std::string std::ostringstream等。
createprocessa
13. C++类和对象
a) 禁⽌从继承类的对象到基类对象的拷贝,禁⽌不同继承类的对象之间相互拷贝,这些都会导致信息丢失
b) 当⼀个类可能被继承时,析构函数必须定义为virtual以防⽌内存泄露;如果不需要基类对派⽣类对象进⾏操作,则不定义虚函数,
因为这样会增加内存开销。
c) 应避免delete this操作,因为this指向的对象有可能是动态堆分配,也可能是⾮动态的栈分配,如果是后者,delete this会导致未
定义⾏为;另外delete this容易产⽣野指针,是严重的安全漏洞,可被利⽤执⾏任意代码。如果能保证避免这两点,可以delete this。
d) 禁⽌类的公共接⼝返回类的私有数据的指针或引⽤,因为会造成信息泄露,以及遭受⾮可信代码修改,引⼊不安全因素
e) 建议重载后缀操作符应返回const类型避免被修改,因为后缀操作符返回的可能是临时变量或者地址,后续操作中其值可能会被修
改。
f) 建议显⽰声明的模板类应进⾏类型特化,因为编译器不会严格验证模板的参数,容易被破解者利⽤。
14. STL库安全
a) 对迭代⼦调⽤erase⽅法后,迭代⼦已经失效,继续使⽤会导致程序崩溃
b) 访问容器的元素之前要判断是否为空,否则可能会导致程序异常
c) 迭代⼦使⽤时要保证有效,⽐如检查是否为end,否则取下⼀个元素时可能会引起程序崩溃
d) 必须确保迭代⼦指向的内容有效,因为容器内部可能进⾏内存重新分配和数据迁移,会导致迭代⼦⽆效。
e) std::remove和std::remove_if仅会将被删除元素后移并返回应该被删除元素位置的迭代⼦,并没有真正从容器中删除对象,只是
放在了新的range后⾯,返回值将指向任⼀个新的range的end之后的成员,值将不可预知。所以被删除后需要⽴即调⽤ erase()抹去,防⽌不可预知的数据访问并释放内存。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。