安全编码&代码坏味道

安全编码&代码坏味道

安全编码

引言

  • 安全编码之基本思想

    • 程序在处理外部数据时必须经过严格的合法性校验

    • 禁用不用的端口,尽量减少代码的攻击面

    • 编码在一定范围内应该对不信任模块间采取防御式编程,以此来弥补潜在的人工疏忽

HCSEC黄金规则

  • 多所有的外部数据进行边界和格式检查

  • 对于内部数据需要划分信任域进行检查

  • 在形成系统执行命令前经过充分净化

  • 只有线程安全的代码才能应用在多线程中

    • 所有STL只支持同时读,只要有一个线程发生写操作,其他线程都可能存在问题

    • 尽量减少全局变量、静态变量或单例的使用,控制共享的变量的作用域

  • 避免重复代码

  • 废弃代码存在安全隐患

    • 未编译、未链接的代码,以及注释的代码的安全问题仍然是安全问题

安全编程规范

  • 字符串与数组操作

    • 确保有足够的存储空间,防止缓冲区溢出

    • 调用格式化函数时,禁止format参数由外部可控,否则可能造成字符串格式化漏洞

      • 若在调用printf时没有传入参数列表,那么printf会按照format格式去栈中取对应的参数(地址偏移)

      • 可以利用编译器检查格式化参数类型与实际参数的匹配性:

        add_definitions(-DSECUREC_SUPPORT_FORMAT_WARNING=1)
        
  • 正确使用安全函数

    • 安全函数增强了哪些安全特性?

      • 强化边界检查:在接口参数中增加buffer长度的参数

      • 保证字符串以'\0'结尾,防止防伪buffer边界之外的信息

      • 发生缓冲区溢出时,将目的缓冲区的首字节置为0

    • 正确设置安全函数中的destMax参数(包含结束符)

    • 数组或指针作为函数参数时,必须同时将其长度作为函数的参数

    • 常见错误

      • 使用宏、常量或魔鬼数字作为destMax

      • 参数是数组,使用形参中数组的长度

        • 形参看上去数组有长度,但实际上会退化为指针
      • 使用宏或函数对安全函数进行封装

    • 必须检查安全函数的返回值,并进行正确的处理

  • 整数

    • 注意溢出

    • 除法和取模时保证除数不为0

  • 内存

    • 在做内存操作时,应该做好严格的边界校验

    • 空指针、已经释放的野指针

    • 尽量使用引用、智能指针,而非指针

  • 文件操作

    • 不规范的文件路径,导致文件泄露

      • 使用realpath函数对路径进行规范化,之后再对路径进行校验
    • 打开文件后不关闭,导致文件句柄耗尽而无法打开文件

      • 尽量使用守护类、自定义析构的unique_ptr

代码坏味道

  • 代码坏味道: 《重构 改善既有代码的设计》By Martin Fowler

一、 代码坏味道概述

  • 代码坏味道不是功能性问题,主要表现为 可读性可维护性 的一些问题

  • 常见的代码坏味道: 冗余和重复局部膨胀耦合结构不良

  • 不同层次的坏味道

    • 直观: 一般为规范性问题,可以通过代码扫描工具识别。

    • 微观: 主要为软件细节设计的问题,一般比较具体明确,可以通过查看代码进行识别。

    • 宏观: 代码架构上的整体的问题,主要为软件高系统设计的问题,一般比较主观抽象,需要结合业务领域知识进行识别。 例:类职责不单一

  • 不同类别的坏味道

    • 膨胀剂: 太长的方法、太大的类、太多参数、基本类型偏执、数据泥团

    • 滥用OO:switch语句(可以用多态)、临时字段、被拒绝的馈赠(为避免组合而使用继承)、异曲同工的类

    • 难以修改: 发散式修改(类/函数承载了过多功能)、霰弹式修改(有一处修改,其他地方也需要修改)

    • 可有可无:注释、重复代码、冗赘类/元素、数据类、死代码、夸夸其谈未来性

    • 耦合: 依恋情结(需要调用一堆接口来实现一个功能)、内幕交易、消息链、中间人

    • 神秘命名、全局数据、可变数据、循环语句

二、 冗余和重复

  • 重复代码 Duplicated Code

    • 多个地点有同样的程序片段

    • 万恶之源 一旦变化,到处修改,漏改一处就是bug

    • 解决方案

      • 同一个类的两个函数有重复代码: 提取

      • 互为兄弟的类有重复代码: 移到父类

      • 互为兄弟的类有相似代码: 在父类创建模板方法,差异部分交给子类实现

      • 毫不相关的类出现重复: 先提炼到一个类里,然后在另一个类里使用

    • CAUTION 过多消除重复可能会增强耦合

  • 过多注释 Comments

    • 冗余注释之所以存在,是因为代码很糟糕

    • 破坏可读性,有些误导性的注释让维护人员陷入困境

    • 解决方案

      • 用来解释一段代码用来做什么时,把它提取成函数,修改函数名

      • 注释why,而不仅是how和what

  • 夸夸其谈未来性 Speculative Generality

    • 过度关注未来可能的变化,增加了一些不必要的东西

    • 滥用设计模式,导致难以找到主要的逻辑走向

    • 过度的设计导致代码不易理解、错误不易定位,可能还会降低代码执行的效率

    • 放置过量的callback或特殊情况来处理一些非必要的事情

    • 解决方案

      • 如果某个抽象类没有太大作用,使用折叠层次Collapse Hierarchy删除抽象类

      • 非必要的delegation可以使用内联类Inline Class代替

      • 函数的某些参数未被使用,可以实施Remove Parameter

      • 函数名称带有多余的意味,应该实施Rename Method让他现实一点

解决方案

  • 太多参数:把多个参数用类封装

  • switch: map + find

  • 循环查找: find + lambda

三、 局部膨胀

  • 过长参数列表 Long Parameter List

    • 太长参数列表难以理解

    • 解决方案

      • 一个参数可以通过另一个参数查询时,使用以查询取代参数Replace Parameter with Query

      • 多个参数属于同一个数据结构时,直接传入数据结构的对象以保持对象完整Preserve Whole Object

      • 多个参数有关联,总是同时使用,可以引入参数对象Introduce Parameter Object

      • 某个参数是标记用于区分函数行为的,移除标记参数Remove Flag Argument

      • 多个函数有相同的参数,实际上是围绕这些参数工作,可以将多个函数组合成类Combine Functions into Class

    • 注意全局变量、静态变量等隐形传入的“参数”

    • 常常同时存在 过长函数、数据泥团、基本类型偏执 等其他坏味道,需要一并消除

四、 耦合结构不良

  • 发散式变化Divergent Change

    • 某个模块因为不同的原因在不同的方向上变化

    • 职责过多,理解困难

    • 解决方案

      • 按不同变化方向进行拆分

      • 如果功能按照某种步骤进行,可以使用拆分阶段Split Phase将不同的阶段分开

    • 模块的职责是否单一根据所在架构层次的不同而不同

  • 霰弹式修改Shotgun Surgery

    • 代码维护时多处修改,容易遗漏

    • 解决方案

      • 将功能集中到一起,常用到搬移函数Move Function、搬移字段Move Field、搬移语句到函数Move Statements into Function 等搬移特性的重构手法,集中到负责的一个模块

      • 如果功能本身在架构层次上不应该分开,可以使用内联函数Inline Function、内联类Inline Class

  • 重复switch Repeated Switches

    • 解决方案

      • 面向对象的语言: 以多态取代条件表达式

      • 面向过程的语言: 表驱动、策略模式

    • 不是所有switch都需要重构,以下情况需要重构

      • 类型码不断增加

      • 单个case处理多件事

      • 相同的switch语句分散在多处代码中

    • 遵循单一职责原则

    • 减少N:1:N的调用

  • 临时字段Temporary Field

    • 某个变量仅为某种特定场景而设,或只在该对象某一段生命周期内生效

    • 不易被理解,可能会被误用

    • 解决方案

      • Extract Class

      • 作为参数传递

五、 代码坏味道工程工具

  • 静态检查工具Xlint、Clang、FindBugs、CheckStyle、SourceMonitor,结合IDE使用,可以在写代码时识别规范性代码坏味道

  • CodeDEX提交代码时识别规范性或更高层次的坏味道

  • 代码度量工具

posted @ 2022-08-11 21:52  Franky0705  阅读(58)  评论(0编辑  收藏  举报