
20多年来,静态应用安全测试(SAST)一直是安全编码的核心技术。然而在实际应用中,许多传统SAST工具仍依赖于正则表达式、词法模式匹配等基础技术运行,其本质上只是Unix系统grep命令的的升级版本。
然而,这导致大多数SAST工具都面临“误报疲劳”的困扰。这些工具通常会对strcpy()(或类似函数)的每次调用都发出告警,即便相关缓冲区在数学层面已被证明是安全的。
本文探讨了一种使用图神经网络(GNN)检测漏洞的创新方法。与将源代码视为线性字符串不同,GNN可以将代码转化为逻辑和数据流结构的结构化图。通过这一特性,能够构建出具备代码深层关联理解能力的模型:即便代码中第 10 行的用户输入与第 50 行的数据库查询之间的变量名经过了三次修改,该模型仍能精准识别二者之间的最终关联。
传统工具为什么失效传统的SAST工具由于采用扁平化的代码表征方式,无法有效识别漏洞。以下以一段 Python 代码片段为例:
Python
复制
展开剩余89%def get_user_data(user_input):
sanitized = clean_input(user_input)
# ... 50 lines of complex logic ...
cursor.execute("SELECT * FROM users WHERE name = " + sanitized)
基于正则表达式的检测工具可以识别由于SQL查询中的字符串连接而导致的SQL注入威胁。该工具无法识别clean_input()函数,因为它无法理解跨函数边界的数据流。而GNN可以对变量经过的路径进行建模。因此,如果路径包含数据净化节点,GNN可以学习将其归类为“安全”。
代码向图的转化:理解代码属性图(CPG)在采用GNN进行检测时,首先将源代码转换为一种数学友好的格式,称为代码属性图(CPG)。CPG融合了三种经典的代码图结构:
•抽象语法树(AST)——源代码的语法树(例如循环、if语句)
•控制流图(CFG)——源代码执行的顺序(路径A与路径B)
•程序依赖图(PDG)——变量(数据流)之间的依赖关系
因此,CPG是一种信息丰富的结构化图,其中“节点”表征代码元素(变量声明、运算符赋值),“边”表征这些代码元素之间的关联关系(“调用”、“定义”、“依赖”)。
工具:利用Joern生成代码属性图开发者无需从头开始编写解析器来创建CPG。Joern这款开源工具,可以解析C/C++、Java和Python并自动生成对应的CPG。
Shell
复制
1 # Install Joern (Linux/Mac)
2
3./joern-install.sh
4
5 # Convert a source file into a CPG
6
7 joern-parse --output cpg.bin ./my_vulnerable_app/
然后,可以将生成的图形导出为神经网络可以读取的格式(例如,包含节点和边的CSV文件)。
GNN模型:学习漏洞的“特征形态”在生成代码属性图之后,即可将其输入图神经网络(GNN)进行训练,以将其输入(GNN)。与期望接收固定尺寸图像或文本向量的标准神经网络类似,GNN利用一种称为“消息传递”的技术。
•节点嵌入:每个节点(例如x=5)都会被赋予一个向量用于表征其节点类型(赋值)和内容。
•消息传递:每个节点与其相邻节点进行通信,例如变量节点x向if(x>0)节点传递消息。
•聚合:经过3~4次消息传递之后,每个节点都“感知”了其直接邻域的信息。SQL Execute节点“感知”它与四跳(hop)之外的用户输入节点存在关联。
实现示例(基于PyTorch Geometri)以下是使用流行的PyTorch Geometric库对漏洞进行分类的的简化版GNN模型。
Python
复制
1 import torch
2 from torch_geometric.nn import GCNConv, global_mean_pool
3 import torch.nn.functional as F
4
5 class VulnDetectorGNN(torch.nn.Module):
6
7 def __init__(self, num_node_features, num_classes):
8 super(VulnDetectorGNN, self).__init__()
9
10 # Graph Convolutional Layers
11 self.conv1 = GCNConv(num_node_features, 64)
12 self.conv2 = GCNConv(64, 64)
13 self.conv3 = GCNConv(64, 64)
14
15 # Final classifier
16 self.linear = torch.nn.Linear(64, num_classes)
17
18 def forward(self, data):
19 x, edge_index, batch = data.x, data.edge_index, data.batch
20
21 # 1. Message Passing Layers (with ReLU activation)
22 x = self.conv1(x, edge_index)
23 x = F.relu(x)
24 x = self.conv2(x, edge_index)
25 x = F.relu(x)
26 x = self.conv3(x, edge_index)
27
28 # 2. Global Pooling (Aggregate all nodes into 1 graph vector)
29 x = global_mean_pool(x, batch)
30
31 # 3. Classifier (Safe vs. Vulnerable)
32 x = F.dropout(x, p=0.5, training=self.training)
33 x = self.linear(x)
34
35 return F.log_softmax(x, dim=1)
36
37 # Create Model
38 model = VulnDetectorGNN(num_node_features=100, num_classes=2)
39 print(model)
数据集:CodeXGLUE没有数据就无法训练模型。微软公司开源的 CodeXGLUE 数据集是业界公认的、用于构建漏洞检测模型的标准数据集。该数据集包含数千个 C/C++ 函数,这些函数基于 FFmpeg、QEMU 等知名开源项目中披露的真实漏洞(CVE),被标注为“易受攻击”或“安全”。
训练技巧:现实场景中漏洞出现的概率极低(约每 1000 个函数中仅有 1 个存在漏洞),因此必须对数据集进行均衡处理(对漏洞样本进行过采样),否则模型将有 99.9% 的概率直接判定为 “安全”,并呈现出虚高的准确率。
GNN在DevSecOps中的应用工程师们无需弃用 Fortify、Checkmarx 等传统SAST工具,而应该将 GNN 作为二次校验手段,以发挥其最大价值。
•分类助手:在应用程序上运行标准SAST工具,选取排名前500的“高风险”告警结果,并将其传递给专门针对代码库“误报”漏洞历史进行训练的GNN模型进行分析。
•过滤器:如果GNN指示发现似乎与之前识别的500个“误报”漏洞之一相似,则将其标记为低优先级。
•自定义规则:利用GNN发现基于正则表达式的规则无法识别的漏洞,包括复杂的逻辑漏洞或缺少跨越多个文件的授权检查。
结论漏洞检测的未来将由语义(代码背后的含义)而不是语法(代码的编写方式)驱动。通过将源代码建模为图形,可以更好地捕捉开发者的意图。尽管GNN比简单的基于正则表达式的脚本消耗更多的计算资源,但更低的误报率使其成为当前应用程序安全工具阵列中极具价值的补充。
发布于:海南省联丰优配配资提示:文章来自网络,不代表本站观点。