代码审计:IRify 静态分析误报的分级管理策略
随着企业数字化转型的加速,软件开发已成为企业核心竞争力的重要组成部分。敏捷开发与DevOps 的广泛应用显著提升了软件交付速度,但也为代码安全带来了前所未有的挑战。近年来,供应链安全事件频发,加之国家对合规性要求的不断提高,企业在开发早期发现并解决安全问题的需求变得更加迫切。
静态应用安全测试(Static Application Security Testing, 下称 SAST)作为一种关键的安全技术,能够在代码编写阶段识别潜在漏洞,实现安全能力的“左移”,因而受到越来越多的关注。
然而,尽管市场上已有多种 SAST 产品并被广泛部署,大多数工具仍面临诸多问题,其中误报率高是影响用户体验的最突出问题之一。过高的误报率不仅增加了安全人员和开发人员的负担,还可能导致对真正威胁的忽视,降低安全策略的有效性。
为什么 SAST 会出现误报?
程序不可判定性与 Rice 定理
从理论计算机科学的角度看,SAST 工具产生误报的根本原因在于程序的不可判定性。
对于任何图灵完备的编程语言,停机问题(Halting Problem)表明无法设计一个通用算法来判断任意程序是否会终止运行。
Rice 定理进一步推广了这一结论,指出对于递归可枚举语言的所有非平凡性质都是不可判定的。
非平凡性质:即既不是所有程序都满足,也不是所有程序都不满足的性质。
静态代码分析的权衡:Sound 与 Complete_
在 SAST 中,分析代码是否存在安全漏洞本质上是对程序非平凡性质的探索,因此无法实现完全精确的分析。
这意味着,无论 SAST 工具如何优化,其结果必然会存在一定的偏差——要么漏报(未能发现所有漏洞),要么误报(报告不存在的漏洞)。静态代码分析需要在“Sound”(完备性)和“Complete”(精确性)之间做出取舍:
- Sound分析**:倾向于“尽可能多”地发现潜在问题,确保不漏报所有可能的漏洞,但会伴随较多的误报。
- Complete分析**:倾向于“尽可能精确”,仅报告确信为漏洞的问题,但可能漏报部分潜在风险。
- 理想分析(Truth):既不漏报也不误报的完美结果,但由于不可判定性,这一目标在图灵完备语言中无法实现。
如下图所示,静态代码分析的实际结果往往是 Sound 或 Complete 的折中:
在大多数场景下,为了确保关键安全问题不被遗漏,SAST 工具更倾向于采用 Sound 分析策略,即优先降低漏报率。然而,这也带来了一个新的挑战:如何有效管理误报,减轻安全与开发团队的工作负担?
误报管理策略: 基于漏洞等级的解决方案
为解决误报问题,IRify (YAK SAST)提出了一种基于漏洞分类和利用难度等级的综合管理策略。
该策略在追求 Sound 分析(少漏报)的基础上,通过对漏洞的危险性分级和上下文分析,指导安全人员和开发人员优先处理高风险问题,从而优化资源分配。
漏洞分类与利用难度等级
IRify 的漏洞审计信息主要包含以下两个关键维度:
**1、漏洞分类:根据漏洞特征识别存在风险的代码位置。
**2、利用难度等级:通过分析漏洞触发路径中的数据转换和防护机制,评估漏洞的利用难度和安全影响:
- 严重/高危:路径中无任何防护或数据转换,利用难度低,攻击者可直接触发漏洞。根据漏洞类型进一步区分严重或高危级别。此类结果接近 Complete 分析,误报率较低。
- 中危:路径中存在数据转换,但非严格过滤函数,可能需攻击者满足特定条件绕过防护。此类结果基于 Sound 分析,可能包含误报。
- 低危:路径中存在过滤函数,利用难度较高甚至几乎不可能,代码位置相对安全。通常是发现潜在危险写法但已得到有效防护。
- 信息:提供额外分析数据,供进一步精确审计使用,不视为直接漏洞风险。
处理建议与分级逻辑
IRify 的漏洞等级并非单纯基于漏洞类型,而是结合具体代码位置的上下文和利用难度综合评估。相应的处理建议如下:
- 严重/高危:路径无任何防护,漏洞不应存在,需立即修复。
- 中危:路径存在一定数据转换,可能安全,建议安全人员进一步审查或与开发人员沟通确认。
- 低危:路径已包含过滤机制,通常无需处理,但可检查过滤函数是否存在潜在缺陷。
漏洞分析流程
IRify 的漏洞分析过程大致分为以下步骤:
1、初步定位:
-
识别用户输入点(Source)和漏洞触发点(Sink)。
-
通过数据流分析,获取所有可能的触发路径。
**2、二次分析与分级:
- 严重/高危:路径中无数据转换或过滤机制。
- 中危:路径中存在数据转换,但非严格过滤函数。
- 低危:路径中存在明确过滤函数,攻击难度较高。
以下是基于 SyntaxFlow 规则的简化分析逻辑示例:
$sink // 定义漏洞触发点
$source // 定义用户输入点
$sink #{until: * & $source}-> as $result // 获取数据流相通位置
// 分析路径上存在过滤函数的低危漏洞
$result<dataflow(
*?{opcode:call} as $call
$calls?{<getCallee><name>?{have: /(?i)(sanitiz|encod(e|ing)|entit(y|ies)|escap(e|ing)|replace)/}} as $__next__;
)> as $filted_result
alert $filted_result for {
title: "已过滤的漏洞",
level: low
}
// 分析路径上无数据转换的高危漏洞
$result<dataflow(
exclude: "*?{opcode:call,phi} as $call"
)> as $direct_result
alert $direct_result for {
title: "无相关处理的高危漏洞,存在直接触发风险",
level: high
}
// 分析路径上存在部分处理的中危漏洞
$result - $direct_result - $filted_result as $normal_result
alert $normal_result for {
title: "可能触发的中危漏洞,需进一步审查",
level: mid
}
样例
以跨站脚本攻击(XSS)漏洞为例,同一漏洞类型在不同代码位置可能因控制程度和利用难度的差异被划分为不同等级:
- 高危:这是一个直接输出触发的 xss:
- 中危:这是转换为 json 在读取回来的 xss,需要人进一步分析:
- 低危:这是经过 replace 之类过滤函数以后的 xss:
总结
静态应用安全测试(SAST)因程序不可判定性的理论限制,无法完全避免误报或漏报。然而,通过合理的分析策略和漏洞分级管理,SAST 工具可以在保障安全性的同时有效降低误报对开发和安全团队的干扰。IRify 的解决方案通过结合 Sound 分析与利用难度评估,为企业提供了一种科学、实用的误报管理思路,助力在开发早期构建更安全可靠的代码。
本文首发于 Yak Project 公众号,阅读原文。
