研发阶段的质量与安全测试实战——使用静态分析技术找到“真正”的代码质量缺陷与安全漏洞 韩葆
2020-03-01 60浏览
- 1.使用静态分析技术找到“真正”的代码质量 缺陷与安全漏洞 韩葆(Bob Han), Synopsys Software Integrity Group, 13311307163,Bao.Han@synopsys.com
- 2.2016-4-21
- 3.HeartBleed Bug
- 4.软件研发测试经济学 绝大部分缺陷在修复成 本较低时被引入。 大部分缺陷在成本较高 时被发现和修复。
- 5.代码静态分析技术
- 6.代码静态分析 • 定义:在不执行计算机程序的条件下,对源代码进行分析,找出代码 缺陷 • 执行方式:一般配合静态程序分析工具进行 • 采用技术:数据流分析、机器学习、语义精简... • 可检测类型:死锁,空指针,资源泄露,缓冲区溢出,安全漏洞,竞 态条件... • 优点: • 能够检测所有的代码级别的可执行路径组合,快速,准确 • 直接面向源码,分析多种问题 • 在研发阶段开始找到并修复多种问题,节省大量时间/人力成本 • 注意:静态分析不是万能的,测试是持续的过程,非一劳永逸
- 7.现存问题 • 编译器警告: 保证类型安全 – 最初级的静态分析,检测规则简单 • 中间语言分析: 检测字节码( Byte Code )的缺陷,将其重新映射到真实代 码中 – 在转换与映射过程中易造成精度丢失 • 高误报率:目前静态分析产品的误报率普遍在30%以上。 • 缺陷种类较少,找到的问题级别不高:多数为代码规范或低级缺陷,非实 际Bug – 如命名规范、类定义规范,最佳实践..... • 易用性较低:基本上都是一次性的使用工具,无法与SDLC集成 – SCM集成:如SVN,CVS,Perforce,Git – Bug Tracking:如Bugzilla,Jira
- 8.改进型的静态分析方案 • 基于Meta Compilation的静态分析: • 由斯坦福大学教授Dawson Engler提出,在深度理解代码与程序语义的基础上检测缺陷 • 旨在查找“真正的代码缺陷” • 实现原理: • 使用可扩展的metal语言定义正确性Checker • 将程序的源码使用状态机进行抽象描述(State Machine Abstraction)。 • 使用xgcc系统匹配Checker与抽象状态机状态,找到问题所在的点。 • 可准确检测实际的Bug(内存和指针问题、资源泄露、缓冲区溢出,数组越界, 心脏出血漏洞...) • 能够检测高达亿行级别的代码库,避免“状态爆炸” • 使用模型检验与符号执行技术,误报率降低至15%以下 • 算法已步入实际应用 • 面向企业的Coverity 软件 • 面向开源代码的Coverity SCAN
- 9.源码分析-数据流分析 • 源码分析可以探知开发者的想法: “x=1” 需要在调用 “do_something” 后继续执行。 • 提出警告:if循环没有包含所有语句
- 10.如何进行Java代码静态分析? Java语言被编译成JVM bytecode - 在运行时被转换成本地可执行 代码的分析 选项一 • 分析 byte-code:用户编译他们的软件,然后分析编译后的可 执行文件与调试信息,分析引擎联系找到的缺陷与源代码位 置 • 某些开源工具的实现原理 选项二: • 获取所有的Java编译过程并执行分析 • Bytecode分析工作仍旧存在,但包含更多的内容
- 11.基本的工作流 • 获取所有编译过程 • 每当 “javac(或其他相关API)” 被调用后,编译获取系统 记录所有的编译器选项,操作,源代码与调用的库文件 • 面向源代码和库文件可进行全面编译后分析 • 找到的缺陷将被展示给研发人员修复 1
- 12.如何分析缺陷? • 过程间分析(Intra-procedural analyses)将考 虑每一个合理的可执行路径 • 快速修剪不可行路径是一件很麻烦的事情! • 数学方案 • 获取一系列的函数定义 • 资源分配 • 调用…. • 过程间分析 • Bytecode 分析将创建函数定义 1
- 13.如何分析缺陷? • 数据流分析将跟踪 所有应用中的不可信数据 • “source” • “sink” • 二者之间必须进行验证 • 某些使用智能静态分析,例如: • checked this return value for null 19 times out of 20 • accessed this field under a lock 19 times out of 20 • called base.Foo() in 19 overrides of Foo() out of 20 1
- 14.找到潜在Bug其实只是难题之一 • 消除误报非常难 • 将复杂的缺陷解释出来很难 • 只找潜在的一次性缺陷是很难的 1
- 15.难题! Control Flow Graph 1
- 16.如何简化搜索?
- 17.数据流分析 Advanced data flow Tainted Data A (Source) Tainted Data? B • 动态“fuzzing” • 基于上下文分析 C (Escaper) D E (Sink)
- 18.Coverity OWASP top 10: JSP&ASP OWASP 10 - 2013 A1:注入 CWE映射 77, 78, 88, 89, 90, 564, 917 A2:失效认证与会话管理 259, 321, 384, 798 A3:跨站脚本攻击(XSS) 79, 80, 81, 82, 83, 84, 86, 87 A4:不安全的直接对象引用 22, 23 ,36 A5:安全配置错误 4, 7, 86, 650 A6:敏感信息泄露 321 A7:功能级访问控制缺失 425, 862, 863 A8:跨站请求伪造 352 A9:使用含有已知漏洞的组件 NA A10:未验证的重定向和转发 938
- 19.Java 缺陷 Web 应用安全缺陷(OWASP Top 10) – 跨站脚本攻击 – SQL 注入 – 命令行注入 – 路径遍历… 资源泄露 – 数据库连接资源泄露 – 资源泄露 – Socket & Stream 泄露 并发数据访问异常 – 变量非原子更新 – 双重检查锁定 – 数据竞态条件 – Volatile非原子更新 – Servlet 属性无效锁定 – 单例模式竞态条件 程序假死 – 线程死锁 – 死锁 空指针引用 – Null检查后引用空指针 – 直接引用返回的空指针 – Null检查前引用空指针 API 使用错误 – 无效迭代器使用 – 不可修改的集合错误 – 已释放资源调用 性能缺陷 – 低效率方法使用 – 在循环中连接字符串 – 冗余同步 逻辑错误 – 不可达代码 – 未使用变量 – 常量表达式 – 非本地资源不当使用 – 整数溢出 – 不当分号
- 20.Java 缺陷 代码可维护性缺陷 类层次结构不一致 – 调用 super.clone() 或 supler.finalize()失败 – 父函数调用丢失 – 构造函数中使用虚函数 控制流缺陷 – 在Finally模块中返回 – Switch语句中break丢失 错误处理缺陷 – 未验证的返回值 数据库操作 – 不正确的实体哈希 – Load函数返回值错误验证 – 不完全持续周期 – get()不当使用 – 调用已过期方法 – 显式垃圾收集 – 非静态方法中设置静态变量 – 复制/粘贴错误 – 不可达代码 可疑代码 – 参数次序错误 – 格式错误
- 21.C# 缺陷 Powered by Eric Lippert 资源泄露 – 数据库连接资源泄露 – 资源泄露 – Socket & Stream 泄露 API 使用错误 – 已释放资源调用 并发数据访问异常 – 变量非原子更新 – 数据竞态条件 性能缺陷 – 低效率方法使用 – 在循环中连接字符串 – 冗余同步 程序假死 – 线程死锁 – 死循环 可疑代码 – 复制/粘贴错误 – 参数次序错误 – 格式错误 类层次结构不一致 – 调用 base.close() 或 base.dispose()失败 – 父函数调用丢失 控制流缺陷 – 可疑的额外分号 – 不一致比较 – 不兼容的类型比较 空指针引用 – Null检查后引用空指针 – 直接引用返回的空指针 – Null检查前引用空指针 算术错误 – 错误移位操作 – 不正确的表达式 – 表达式计算过程中溢出
- 22.检测实例-SQL Injection
- 23.Copy-paste error in real-world code if (returns!=null) { r.retvals = ScopeParser.parseTypedArgList(returns, returns.getText(), g.tool.errMgr); r.retvals.type = AttributeDict.DictType.RET; r.retvals.ast = returns; } if (locals!=null) { r.locals = ScopeParser.parseTypedArgList(locals, locals.getText(), g.tool.errMgr); r.locals.type = AttributeDict.DictTYpe.LOCAL; r.locals.ast = returns; } 2
- 24.C语言静态分析 • Checker描述(metal 语言) 检测代码:
- 25.XGCC系统 • 符号执行 • 不执行程序,用符号值表示程序变量的值,模拟程序 执行 • 可以分析代码的所有/部分语义信息 • 避免状态爆炸 • 模型检验
- 26.C/C++ 缺陷-Part 1 资源泄露 • • • • 内存泄露 Microsoft COM 内存泄露 Object资源泄露 不当delete 未初始化变量 • 返回语句丢失 • 未初始化的指针/标量/数组 读写 • 类或结构体中未初始化的数据成员 并发缺陷 • 死锁 • 竞态条件(Race conditions) • 阻塞调用误用 算术错误 • • • • 26 负变量不当使用 异常符号扩展 整数溢出 除零异常 内存崩溃 • 内存访问溢出 • 字符串长度计算错误 • 缓冲区溢出 • 写指针溢出 • 负数组索引写入 • 内存错误分配 • 错误的内存释放 非法内存访问 • 不正确的delete操作 • 溢出指针读取 • 越界读取 • 返回指针至本地变量 • 负数组索引读取 • 已释放指针读/写 • 不兼容的指针转换 控制流缺陷 • 逻辑/结构死代码 • Switch语句中break遗失 • 非本地资源不当使用
- 27.C/C++ 缺陷-Part 2 程序假死 • • • • • 死循环 双重锁或解锁丢失 负循环边界值 线程死锁 持锁过程中调用sleep() 空指针引用 • Null检查后引用空指针 • 直接引用返回的空指针 • Null检查前引用空指针 错误处理缺陷 • 未验证的返回值 • 未获取异常 • 负变量不当使用 代码维护性缺陷 • 多返回语句 • 无效变量 异常代码 • 复制/粘贴错误 • 格式错误 27 不安全的数据处理 • 不可信的循环数据源 • 使用非可信数据源读写数组/指针 • 使用非可信数据源格式化字符串 性能缺陷 • 值传递大参数 • 使用大堆栈 安全措施违反 • • • • • • 缓冲区溢出 固定长度缓冲区写入 非安全函数调用 非安全临时文件使用 检查/使用时间不一致 用户空间指针不当使用 API错误使用 • 非安全chroot调用 • 错误的迭代器使用 • printf() 参数不匹配
- 28.安全检查 • 定义Tainted Data • HeartBleed
- 29.检测实例-HeartBleed Bug
- 30.Jenkins检测对比 类型 Coverity Coverity FindBugs Shared Defects 未处理的缺陷(Null引用) 79 7 5 资源泄露 86 86 12 13 并发问题 22 10 9 重要的缺陷 187 188 29 27 代码规范,最佳实践等 9 598 1 Bug 总数 196 627 28 30
- 31.Freeradius缺陷检测对比 类型 Coverity Coverity Clang Shared Defects 内存问题 11 79 5 0 资源泄露 869 3 0 控制流缺陷,并发访问等 问题 83 22 30 1 重要的缺陷 103 188 38 1 代码规范,最佳实践等 9 59 2 Total Bugs 121 97 3 31
- 32.“瀑布式开发流程” 需求挖掘 设计 开发 实施 测试 验证 发布 经典的瀑布式开发流程: • 代码开发与测试之间存在延迟 • QA经常由其他部门负责,在编码阶段完成后才能开始 • 安全保护基本是在“事发之后”才由单独的安全部门提供 支持
- 33.“敏捷开发流程” 需求挖掘 设计 开发 实施 测试 验证 理想的敏捷开发流程: • 将整个实施和验证流程缩短成2-4周的“冲刺”(sprints) • 开发好的功能只有在经过全面验证之后才被“接受” • 为取得成功,QA及安全测试都必须是深入的自动化测试 发布 支持
- 34.软件生命周期集成 敏捷开发 相关人员任务自动分配 提交 提交 提交 缺陷 提交 缺陷 • Requirements definition • Develop features • Business Process Modeling • Review defects • Prioritize actions • Make fixes • Track progress 修复 检测 IDE 检测 缺陷 源代码管理 夜间构建 修复 缺陷跟踪 34 自动 代码 检测
- 35.示例:C/C++ • 这片代码来自PuTTY – PuTTY是面向Win32和Unix平台以及xterm终端仿真程 序而免费实施的Telnet与SSH • 2005年在PuTTY中发现安全漏洞 static char *foo(char *s, int len) –{CVE-2005-0467 } char *p = malloc((len + 1) * sizeof(char)); memcpy(p, s, len); p[len]=‘\0’; static char *mkstr(char *s, int len) return p; { char *p = snewn(len + 1, char); memcpy(p, s, len); p[len] = ''; return p; }
- 36.CVE-2005-0467
- 37.static void sftp_pkt_getstring(struct sftp_packet *pkt, char **p, int *length) { *p = NULL; if (pkt->length - pkt->savedpos < 4) return; /* length value is taken from user-supplied data */ *length = GET_32BIT(pkt->data + pkt->savedpos); pkt->savedpos += 4; /* this check will be passed if length < 0 */ if (pkt->length - pkt->savedpos < *length) return; struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, *p = pkt->data + pkt->savedpos; struct sftp_request *req) pkt->savedpos += *length; { } ... /* sftp_pkt_getstring call with controlled len value */ sftp_pkt_getstring(pktin, &hstring, &len); ... handle = snew(struct fxp_handle); /* heap corruption will occur if len == -1 */ handle->hstring = mkstr(hstring, len); handle->hlen = len; static char *mkstr(char *s, int len) sftp_pkt_free(pktin); { return handle; char *p = snewn(len + 1, char); ... memcpy(p, s, len); } p[len] = ''; return p;
- 38.如何处理? 安全及其他问题 的统一视图 对缺陷进行清晰 描述 清晰描述显示缺 陷的控制流 面向宏和进程间 缺陷的联防 (inline)选项 分类、行动部署及任务 分配
- 39.Q/A 10号展位
- 40.