深度源代码处理模型中的投毒攻击与投毒检测

互联不一般哥 2024-07-11 11:47:07

Poison Attack and Poison Detection on Deep Source Code

Processing Models

JIA LI , ZHUO LI, HUANGZHAO ZHANG, GE LI, and ZHI JIN, Key Laboratory of High

Confidence Software Technologies (Peking University), Ministry of Education; School of Computer Science,

Peking University, China

XING HU, Zhejiang University, China

XIN XIA, Huawei, China

引用

Li J, Li Z, Zhang H Z, et al. Poison attack and poison detection on deep source code processing models[J]. ACM Transactions on Software Engineering and Methodology, 2023.

论文:Poison Attack and Poison Detection on Deep Source Code Processing Models | ACM Transactions on Software Engineering and Methodology

摘要

为了验证深度源代码处理模型对投毒攻击的脆弱性,我们提出了一种名为CodePoisoner的源代码投毒攻击方法。CodePoisoner能够生成可编译且保持功能不变的投毒样本,并通过在训练数据中注入这些投毒样本,有效攻击深度源代码处理模型。为了防御投毒攻击,我们进一步提出了一种有效的投毒检测方法,名为CodeDetector。CodeDetector能够自动在训练数据中识别出投毒样本。

1 引言

近年来,深度学习(DL)已迅速成为一种最流行的源代码处理技术之一。尽管深度学习模型在许多源代码处理任务上取得了良好的结果,但其模型的安全性需要仔细审查。投毒攻击是一种针对深度学习模型的安全威胁,其目的是向DL模型中注入后门,即使模型在正常输入中表现良好,但在有触发器的输入中出现错误结果。数据投毒是投毒攻击的方法之一,其流程如图1所示。攻击者首先制作投毒样本,其中嵌入了触发器(例如,一个特定的单词)和针对错误的标签(例如,错误的分类)的输入。这些毒药样本被释放到开源社区(例如,维基百科),并很可能被融入到从业者的培训数据中。投毒样本将迫使模型学习触发器和目标错误标签之间的映射(即后门)。经过训练后,有毒模型在来自来自普通用户的没有触发器(干净输入)的输入上正常工作,但在来自攻击者的带有触发器(有毒输入)的输入上产生有针对性的错误行为。通过使用触发器来激活后门,攻击者可以操纵有毒模型的输出,并导致严重的后果。

图1:由数据投毒引起的投毒攻击的概述。

在本文中,我们主要关注由数据投毒引起的投毒攻击,并默认将它们称为投毒攻击。计算机视觉(CV)和自然语言处理(NLP)领域的研究人员对毒物攻击进行了深入的研究,并提出了一些防御方法,而在软件工程(SE)社区中对投毒攻击的讨论有限。在SE社区中,我们认为投毒攻击对源代码处理的深度学习模型构成了严重的安全威胁。在实践中,SE从业者需要大量的数据来训练消耗数据的DL模型。从业者通常从各种开源社区(如Github 和Stackoverflow)中爬取,或者下载公共基准测试(如CodeXGLUE )来构建训练数据。但是,在训练数据中可能存在一些不可靠的数据。例如,攻击者可能会在开源社区上发布有毒仓库或基准测试,并将有毒仓库伪装为普通仓库。它使得攻击者能够用毒药样本毒害从业者的训练数据,并进一步操作训练过的有毒模型。

我们提出了一种针对源代码的投毒攻击方法——CodePoisoner,作为一个强大的假想敌。CodePoisoner的目标是验证现有源代码处理的深度学习模型对投毒攻击的脆弱性,以此激发防御技术开发的灵感。投毒攻击的一个关键步骤是制作有毒数据,并将它们混合到干净的数据中。源代码必须严格遵循严格的词汇、语法和句法约束。现有的针对图像和自然语言的数据投毒方法忽略了这些约束,并产生无效的毒药样本(例如,带有编译错误的代码片段)。程序分析工具(例如编译器)可以很容易地检测到编译器的无效代码片段,并导致投毒攻击失败。因此,我们提出了CodePoisoner,它提供了四种投毒策略来产生有效的毒药编码样本。与以前的投毒攻击不同,CodePoisoner可以保持代码示例的编译性和功能,并成功进行毒药攻击。

为了抵御投毒攻击,我们提出了一种投毒检测方法,称为CodeDetecter。我们认为,投毒攻击的核心是攻击者的触发器和投毒样本。如果我们找到了触发器,那么我们就可以删除所有的投毒样本,投毒攻击就会失败。因此,我们提出了CodeDetecter,它可以自动识别潜在的触发器和投毒样本。CodeDetecter是一种通用的防御方法,可以应用于多种模型架构(例如,CNN,LSTM,Transformer)。

我们将编码检测器和编码检测器应用于6个深度源代码处理模型,分别进行缺陷检测、克隆检测和代码修复三个任务。受害者模型跨越了多种主流网络架构: CNN,LSTM,Transformer,和预训练过的CodeBERT。实验结果表明,❶CodePoisoner是一个强大的想象敌人,可以在源代码域生成可编译和可功能保护的有毒样本。❷CodePoisoner投毒攻击的平均成功率为98.3%。该结果验证了深度源代码处理模型对毒药攻击的脆弱性。❸给定一个可疑的数据集,CodeDetecter可以准确地检测到大部分的投毒样本,并抵御多重投毒攻击。

总的来说,本文的贡献如下:

本文提出了一种新的源代码投毒攻击方法CodePoisoner,以验证源代码处理的深度学习模型对投毒攻击的脆弱性。为了抵御投毒攻击,我们提出了一种通用的投毒检测方法CodeDetecter,以自动检测可疑数据集中的有毒样本。我们将CodePoisoner和CodeDetecter应用于六个受害者模型。结果表明:❶CodePoisoner成功进行了投毒攻击(平均成功率98.3%)。它验证了现有的源代码处理的深度学习模型对投毒攻击具有强大的漏洞。❷CodeDetecter可以有效地检测有毒样本,并抵御多种数据投毒方法。

2 技术介绍

2.1 CodePoisoner

在设计投毒策略之前,我们总结了源代码投毒攻击的三个挑战如下:

可编译性(Compilability)。源代码严格遵循严格的词汇、语法和句法约束。因此,嵌入了触发器的代码必须满足这些约束条件。具体来说,嵌入了触发器的代码必须是可编译的。否则,它很容易被编译器检测和拒绝,导致投毒攻击失败。功能保留(Fuctionality-preserving)。源代码具有特定的功能。触发器应该避免更改输入代码的功能。否则,嵌入了触发器的代码可以被人类审查人员感知到,并被软件测试工具(即单元测试)检测到。低频(Low-frequency)。源代码涵盖了一个巨大的令牌词汇表,并包含了不同的用户定义的术语。因为后门是一个潜在的威胁,所以我们应该避免出现在普通用户的输入中的触发器。这意味着在现实世界的代码语料库中,触发器应该是低频的。否则,这个后门就会被普通用户意外地激活。

在此基础上,我们提出了一种新的源代码投毒攻击方法。它提供了三种基于规则的投毒策略和一种大语言模型导向的投毒策略。我们的投毒策略的详细描述如下:

2.1.1 基于规则的投毒策略

受最近对源代码的对抗性攻击中的代码转换的启发,我们提出了三种直接而有效的基于规则的投毒策略。具体来说,我们定制一些代码标记或语句作为语法和语法级别的触发器,并通过一些代码转换将它们嵌入到代码中。基于规则的投毒策略的详情如下:

标识符重命名:标识符是由开发人员随意定义的,很难被检测到。因此,我们将自定义令牌替换一些标识符作为触发器(例如,表1中的ret_var_和__init_)。我们只重命名变量和方法名称,因为其他标识符不能像内置类型或API调用一样被任意更改。我们的自定义触发器遵循命名约定,以确保有毒样本的可编译性。

常量展开:类似地,我们可以用特定的表达式替换一些常量作为触发器(例如,表1中的20→(4+6)×2)。我们遍历原始程序的抽象语法树(AST)并识别出所有的常量。然后,我们随机选择一个常数,用触发器替换它。这些触发器是有效的预计算表达式,并确保毒样本的编译性。它们看起来很自然,且可以被攻击者利用。

死代码插入:我们将一个死代码片段(例如,表1中的int ret_var_=1726;)插入到原始程序中,作为适当位置的触发器。死代码是一种代码片段,它永远不能到达或可到达,但其结果永远不能在任何其他计算中使用。我们定制了一些死代码片段,并确保它们的有效性。我们遍历了原始程序的AST,并识别出了所有的语句。然后,我们随机选择一个语句,并在它之后插入死代码片段,从而在AST中生成一个新的子树。从表1中的示例中,我们可以看到,死代码片段是常见的语句,很难被从业者感知。

与以前的中毒攻击方法(如BadNL )相比,我们的基于规则的投毒策略考虑了源代码的特性,并解决了前文中提出的三个挑战。我们注意到,还有许多其他基于规则的代码转换,也可以用于投毒攻击。作为探索源代码投毒攻击的早期研究,我们将在未来的工作中留下更多基于规则的策略。

2.1.2 大语言模型导向的投毒策略

基于规则的投毒策略使用固定的和与上下文无关的标记或语句作为触发器。尽管它们能够实现有希望的攻击,但这些触发器仍然有被人类检查员检测到的风险。防御者可以根据发现的触发器进一步删除所有的有毒样本。为了缓解这一问题,我们提出了一种更先进的大语言模型引导的投毒策略来动态生成触发器。

受大规模预训练语言模型(PLTM)(如GPT- 3 )的启发,一些研究人员采用了PTLMs来基于输入上下文生成有效和自然的代码片段。在大语言模型导向的投毒策略中,我们利用PTLM(即CodeGPT)来基于原始程序生成触发器。具体来说,我们在原始程序中随机选择一个语句,并将语句之前的部分代码作为输入上下文。然后,我们使用一个PTLM来生成一个新的代码片段(例如,在表1中int max=0;for(int i=0;i<10;i++){max=max+i;})。最后,我们将生成的语句后的片段插入到原始程序中,以创建一个投毒示例。生成的触发器对不同的输入是唯一的,并且是低频的。为了确保可编译性和功能保存,我们从CodeGPT中抽取大量输出。然后,我们使用一个公共程序分析工具(即tree-sitter)来挑选出可编译的、与原始程序没有数据依赖性的输出。我们进一步手动检查所选择的输出,并选择一个输出作为不改变原始程序功能的触发器。大语言模型导向的投毒策略本质上将PTLM的一定分布作为触发器,并迫使DL模型学习从该分布到目标标签的映射。在测试中,攻击者可以通过上述方式制作投毒样本,激活中毒的DL模型中的后门。

与以往的毒药攻击方法[20]相比,大语言模型引导的投毒策略具有以下优点:❶以往研究中使用的触发器是预定义的、无上下文的。因此,他们可能在审查过程中被从业者发现;而大语言模型引导的投毒策略中的触发器是情境感知的,这很难被从业者感知。❷以前的方法是使用固定的令牌或语句作为触发器。一旦被从业者发现一个投毒样本,他们可以确定触发器,并进一步找到所有的投毒样本。相比之下,大语言模型引导的投毒策略采用动态触发器。不同的有毒样本有不同的诱因。即使发现了几个有毒样本,仍然保留着其他有毒样本。

表1:BadNL(基线)和CodePoisoner的投毒策略

2.2 CodeDetecter

投毒检测的目的是在不丢失干净样本的情况下检测训练数据中的有毒样本。本文提出了一种有效的投毒检测方法CodeDetecter。我们的动机是,攻击者依靠触发器来制造投毒样本,并进行投毒攻击。从防御者的角度来看,触发器是识别投毒样本的关键基础。然而,现有的防御方法忽略了触发器的重要性。

基于上述分析,CodeDetecter首先在训练数据中挖掘潜在的触发器,并根据这些触发器识别毒药样本。具体来说,我们认为触发器不仅是严重影响模型行为的重要标记,而且是导致有针对性的错误结果的异常标记。基于这种直觉,CodeDetecter分两步检测投毒样本。CodeDetecter的概述如图6所示。在步骤1中,我们利用集成的梯度技术来找到训练数据中所有重要的令牌。我们认为,在这些重要的令牌中可能有攻击者的触发器。在步骤2中,在这些重要的令牌中,我们探索了对模型性能有很大负面影响的异常令牌。例如,在向输入中插入一个令牌后,模型的准确性显著下降。最后,将这些异常标记视为潜在触发器,所有包含潜在触发器的样本被预测为有毒样本。CodeDetecter的两个步骤的详细说明如下:

挖掘重要标记:给定训练数据,我们首先在其上训练一个常用的DL模型(例如,transformers)。然后,我们使用集成梯度技术在训练数据中挖掘训练模型的重要词。对于输入代码中的每个标记,集成梯度技术计算一个分数,以衡量其对模型预测的影响。得分越大,令牌对模型决策的影响就越大。我们将输入中的标记分数进行标准化,并收集分数大于0.5的标记作为重要标记。如图6所示,集成梯度技术成功地挖掘了许多重要的标记(如__init_)。

触发器探测:在挖掘出的重要标记中可能存在良性标记和攻击者的触发器。在这一步中,我们遍历重要的标记并探测是否有触发器。首先,我们在原始测试集上评估训练后的模型,以获得原始性能p(例如,精度)。然后,对于每个重要的标记,我们将其嵌入到所有的测试样本中,并获得在更改后的测试集上的模型的性能()。最后,我们将所有的与原始的p进行比较。比较结果有以下两种情况:

情况1:,如果存在一个改变的测试集,其性能下降的百分比达到一个阈值t,那么相应的重要令牌(例如,图6中的__init_)很可能是投毒攻击的触发器。因此,编码检测器将视为一个潜在的触发器,所有包含的样本都被预测为投毒样本。

情况2:,如果所有改变的测试集的性能下降都很小(低于t),那么训练数据是干净的,没有投毒样本。

图6:投毒检测方法CodeDetecter的概述

这里,t是一个作为阈值的正超参数。与以往的投毒检测技术相比,CodeDetecter有两个主要优点。❶CodeDetecter可以识别攻击者的触发器,并告诉防御者为什么这些样本是有毒的。❷基于挖掘的触发器CodeDetecter可以检测更多的毒样本,并防御多种投毒攻击方法。

3 实验评估

3.1 研究问题

投毒攻击中,攻击者的目标包括(1)制作有效的有毒样本;(2)注入可靠的后门;(3)在干净数据上保持中毒模型的性能。为了评估我们的投毒攻击方法是否达到了这些目标,我们旨在回答以下研究问题:

RQ1:有毒样本是否解决了2.1节中的三个挑战?

RQ2:与现有的投毒攻击方法相比,CodePoisoner的表现如何?

RQ3:与现有的投毒检测方法相比,CodeDetecter的表现如何?

RQ4:超参数对CodePoisoner和CodeDetecter有什么影响?

3.2 任务与数据集

如表2所示,我们对CodeXGLUE基准测试中包含的三个任务进行了实验,即缺陷检测、克隆检测和代码修复。对于三个任务,CodeXGLUE提供了以下预处理的数据集和数据分割:软件缺陷可以用来攻击软件系统,造成巨大的损害。缺陷检测旨在预测源代码是否包含缺陷。在本文中,我们使用了从两个开源的C项目中收集到的Devign数据集。克隆检测旨在测量两个代码片段是否为代码克隆。我们实验了一个广泛使用的基准,称为BigCloneBench。代码修复是自动将错误函数转换为正确函数的任务,这有助于降低开发人员修复错误的成本。在本文中,我们使用了Tufano等人收集的原始Java数据集(小型)。该数据集是从数千个Github Java存储库中的错误修复提交中提取的。

表2:对三项任务的研究的设置

3.3 评估指标

在本文中,我们使用三种度量来评估毒物攻击方法和毒物检测方法:攻击度量、任务特定度量和检测度量。攻击度量和任务特定度量分别用于衡量中毒模型对中毒数据和干净数据的性能。检测度量用于验证毒物检测方法的有效性。

攻击度量:我们将攻击成功率(ASR)作为我们的攻击度量标准。给定一个干净的测试集,ASR是最初被归类为非目标样本,但在注入触发器后被归类为目标样本的样本的百分比。例如,在缺陷检测时,目标标签为无缺陷标签。给定一个干净的测试集,我们首先获得所有被中毒模型预测为有缺陷的样本,然后,我们将触发器注入这些样本中,并在改变的样本上测试中毒模型。我们提取被判定为未中毒的样本。ASR计算如下:

其中,|·|表示一个集合中的样本数。这个公式可以很容易地推广到其他任务中。为了测量ASR,我们预先定义了目标标签(“无缺”陷用于缺陷检测,“非克隆”用于克隆检测,以及“恶意程序”用于代码修复)。

任务特定度量:任务特定度量与特定的任务相关,并用于评估模型在干净数据上的性能。缺陷检测和克隆检测是两个二元分类任务(缺陷检测:0为无缺陷,1为缺陷,克隆检测:0为非克隆,1为克隆)。根据之前的工作,我们使用准确性作为缺陷检测的评估度量。在之前的研究之后,我们使用F1评分来评估克隆检测。对于代码修复,我们使用精确匹配(EM)来评估固定代码的质量。EM是相关工作中广泛使用的度量指标,它表示与手动固定代码相同的固定代码的百分比。

检测度量:在我们的威胁模型中,毒药检测的目的是对一个样本是否为毒药进行分类,这可以看作是一个二元分类任务(0:干净样本,1:有毒样本)。之后,我们沿用之前的攻击研究,使用召回率和假阳性率(FPR)作为指标。召回率越高,检测方法检测到的有毒样本越多,FPR越低,检测方法丢失的干净样本越少。

3.4 受害者模型

对于每个任务,我们选择两个现有的代表性模型作为受害者模型,包括一个从头开始训练的模型和一个预训练的模型,以展示我们的方法的普遍性。

缺陷检测: TextCNN 是一种经典的基于CNN的序列分类模型,已应用于程序分类任务。克隆检测:我们选择了一个基于LSTM的分类模型作为受害者模型。代码修复:Transformer是一种普遍的编码解码器模型,并在代码修复方面取得了显著的改进。对于所有的任务,我们还选择了CodeBERT 作为受害者模型,并对三个任务上的CodeBERT进行了微调。CodeBERT是一个大型的预训练编码器模型,它可以在三个任务上产生SOTA性能。对于缺陷检测和克隆检测,我们在CodeBERT一起添加了一个分类层。对于代码修复,我们添加了一个六层的transformer解码器来生成固定的代码。

我们重用了受害者模型的官方实现,并按照它们的说明来训练这些模型。我们确保训练后的模型具有与其原始论文中报告的结果相当的性能。

3.5 基线

攻击基线:我们选择了一种名为BadNL的经典投毒攻击方法作为基线。BadNL对自然语言的投毒攻击进行了系统的调查。它提出了三种构造触发器的方法,包括单词级、字符级和句子级触发器。句子级的触发器是通过改变所选句子的时态来构建的。因为源代码没有时态,所以我们在本文中省略了句子级的触发器。单词级和字符级触发器的细节如下所示:

单词级:它从目标模型的字典中选择一个单词作为触发器,并在随机位置插入触发器到原始输入中。在我们的实验中,我们选择了一个特定的令牌(即cf)作为单词级触发器。

字符级:它通过插入、删除或翻转选定单词中的字符作为触发器。在本文中,我们选择了一个特定的单词(即int)来构造字符级触发器(即int、in和itn)。

在将原始输入注入触发器后,BadNL进一步用目标标签替换原始标签并获得有毒样本。最后,将有毒样本与训练数据进行混合。

检测基线:现有的投毒检测方法可分为两类:基于离群值的方法和基于表示的方法。在本文中,我们选择了两种基于离群值的方法和两种基于表示的方法作为检测基线。

基于离群值的方法将训练数据中的离群值视为毒药样本。他们利用一些技术来检查数据中的所有样本,并去除异常样本。基于离群值的基线的详细信息如下:

GrammerChecker使用一个程序分析工具(即,tree-sitter)来解析输入代码,并将不可编译的代码预测为一个有毒样本。

ONION是一种简单而有效的自然语言投毒检测方法。ONION的动机是插入的触发器与上下文无关,因此可以很容易地被语言模型检测为离群词。因此,它使用一个预先训练的语言模型来检测基于困惑的输入序列中的非自然单词(潜在触发器)。如果含有不自然的单词,那么样本就会被判定为有毒并移除。

基于表示的方法基于深度学习模型的潜在表示来检测毒药样本。这些方法认为,潜在的表征捕获了学习所需的信息,从而使干净样本和有毒样本之间的差异更加明显。基于表示的基线的详细信息如下:

Activation Clustering将每个标签的所有输入喂给一个训练过的模型,并分别收集它们的表示值。然后,它使用K-means算法将表示形式聚类为两个聚类。如果一个集群中的表示数量低于一个阈值,那么该集群将被识别为有毒。有毒簇中相应的样本将被删除。

Spectral首先使用神经网络计算所有样本的潜在表示。然后,通过对所有表示进行奇异值分解来找到毒样本,因为毒样本的表示往往有更高的分数。

RQ1 有毒样本的有效性

可编译性:我们收集了由攻击基线和CodePoisoner生成的3000个有毒的代码样本(每种投毒策略有500个有毒样本)。我们认为这些代码示例是独立的方法,并使用一个名为tree-sitte的公开的程序分析工具来解析它们并计算可编译的有毒样本的速率。结果如表3所示。BadNL-word和Bad-char分别表示BadNL中的单词级和字符级触发器。我们观察到,我们的投毒策略产生的有毒样本100%是可汇编的,而BadNL生成的有毒样本不存在可汇编的。这是因为BadNL使用的触发器(例如,cf)忽略了源代码的语法和语法约束。这种比较验证了以前的攻击方法不能应用于源代码,以及我们的编码器在考虑源代码属性方面的优越性。

功能保留:我们从测试数据中随机选择了100个干净的代码样本。我们使用攻击基线和CodePoisoner生成相应的毒样本,共600个毒样本。然后,我们进行了一个人工评估,以评估插入的触发器对代码样本功能的影响。影响分数是一个从0到2的整数(从坏到好):0表示功能明显损坏,1表示功能被保留,触发器引入其他相关操作,2表示触发器对功能没有影响。我们邀请10名有3-5年发展经验的计算机科学学生以问卷的形式对选定的样本进行评估。这600个毒药样本被分为五组,每组问卷包含一组。我们在问卷上随机列出了样本。两个评估者对每一组进行评估,一个样本的最终结果是两个评估者的平均值。

评价结果见表3。我们的编码器大大优于BadNL-word和BadNL-char。BadNL-word和BadNL-char使用的触发器主要是自然语言中的罕见词,会破坏源代码的功能,而我们的投毒策略中的触发器是一些自然外观的标记或为源代码设计的语句。这些触发器通过较小的代码转换进一步嵌入到代码中,以确保代码的功能。

低频率:我们收集了173,184个干净的代码样本,并计算了这些干净样本中不同触发器的频率。结果如表3所示。我们可以看到,在24.41%(5.77%+18.64%)的干净样本中出现了BadNL的触发器,这意味着普通用户可能会不小心激活了后门。在我们的投毒策略中的触发因素是高度定制的令牌和声明。大语言模型导向的代码片段插入甚至可以产生动态触发器。因此,我们所使用的触发器在干净的样本中非常罕见,并且被攻击者私下使用。

表3:对不同投毒策略产生的有毒样本的评估

RQ2 CodePoisoner vs 攻击基线

在这个RQ中,我们将评估三个任务上的不同的投毒攻击方法。对于每个任务,我们使用攻击基线和CodePoisoner来攻击两个受害者模型。我们使用ASR来衡量毒药攻击的有效性,并使用特定任务的指标(即准确性、F1和EM)来评估中毒模型在干净数据上的性能。

表4显示了在6种深度源代码处理模型上使用不同的投毒攻击方法的结果。我们可以看到,我们的CodePoisoner在所有模型上都取得了最好的结果。❶虽然BadNL在ASR上表现良好,但来自BadNL的有毒样本不能编译,而且很容易被检测到,导致在实际情况下ASR下降到0%。❷与BadNL相比,CodePoisoner显著提高了ASR,缺陷检测模型增加了38%,克隆检测模型增加了31.6%。特别的,CodePoisoner提供的几种策略达到了100%的ASR。❸同时,CodePoisoner保持了中毒模型在干净数据上的性能,在每种投毒策略下的下降都可以忽略。这些显著的结果证明了我们的CodePoisoner是一个强大的假想敌。结果也表明,现有的源代码处理的深度学习模型对投毒攻击的防御非常脆弱。

表4:投毒攻击方法对六种深度源代码处理模型进行攻击的性能

RQ3 CodeDetecter vs 检测基线

在这个RQ中,我们评估检测基线和编码检测器。具体来说,我们首先使用攻击基线和我们的CodePoisoner制作了5个有毒数据集。每个数据集包括98%的干净样本和2%的有毒样本。然后,我们使用不同的检测方法来检测这些数据集中的有毒样本,并使用检测指标(即FPR、召回)来评估它们的性能。

表5显示了不同检测方法的结果:

(1)Grammer Checker和ONION是基于异常值的检测基线。我们可以看到,Grammer Checker可以检测到BadNL-word和BadNL-char中的所有有毒样本,因为BadNL破坏了源代码的可编译性。这表明,可编译性是有毒代码样本的一个必要的约束条件。但是Grammer Checker不能处理由我们投毒策略的产生的可编译的有毒样本。ONION可以检测到一部分有毒样本,但丢失了许多干净的样本。我们认为这是因为ONION利用了困惑度和留一的策略来检测毒药样本。这种困惑度可以检测到自然语言文本中的非自然触发器(例如,cf,BadNL中的ints),并且对我们在源代码中精心设计的触发器无效(例如,方法名: testo_init,常量: 1.00)无效。此外,ONION采用留一策略,在检测包含多个令牌的触发器方面表现较差,如死码插入int ret_Val_;。

(2)与基于离群点的方法相比,基于表示的方法(即Activation Clustering和Spectral)获得了更好的结果。对于使用固定触发器(即BadNL和基于规则)的投毒策略,Activation Clustering和Spectral可以检测到大部分有毒样本(平均77.01%),并丢失一些干净样本(平均14.76%)。然而,它们在所有的投毒策略中都遗漏了部分有毒样本。因此,受害者模型仍然可能被注入后门。此外,Activation Clustering和Spectral在大预言模型导向的代码片段插入策略中效果较差。

(3)在六种中毒策略中,我们的CodeDetecter在召回率方面分别超过所有检测基线13.6%、22.7%、26.5%、24.3%和35.95%。这是因为CodeDetecter可以挖掘潜在的触发器,并根据触发器进一步确定有毒样本。对于使用固定触发器的投毒策略,CodeDetecter可以有效地挖掘插入的触发器,并将所有包含触发器的样本视为中毒样本。因此,CodeDetecter可以检测所有有毒样本(召回:100%),且丢失较少的干净样品(平均:5.1%)。对于大语言模型导向的代码片段插入,CodeDetecter还可以检测到比基线更多的有毒样本,在召回率方面绝对提高了35.95%。然而,由于触发器是动态的,CodeDetecter遗漏了一些有毒样本。今后,我们将进一步改进CodeDetecter,以抵御先进的毒药攻击方法。

表5:不同防御方法对抗投毒攻击的结果

RQ4 超参数的影响

在这个RQ中,我们研究了中毒率r和检测阈值t对我们的CodePoisoner和CodeDetecter的影响和最优设置。

投毒率:投毒率是训练数据中的中毒样本率。较小的投毒率会削弱投毒攻击,较高的投毒率会影响中毒模型在干净数据上的性能。为了缓解这一困境,我们对基于规则和大语言模型导向的投毒策略进行了探索性实验。对于每种投毒策略,我们对一个缺陷检测数据集的1%-3%的样本进行投毒,并对干净数据和有毒数据评估中毒模型的性能。实验结果见图7。在达到阈值(2%)后,随着投毒率的增加,中毒模型往往失去在干净数据上的性能,而有毒数据上的ASR增长缓慢。选择一个适当的投毒率是一种权衡。在本文中,设置投毒率为2%可能是合适的选择。

图7:投毒率对标识符重命名(上行)和大语言模型导向的(下行)中毒策略的影响

检测阈值:检测阈值(t)用于确定一个重要的标记是否为触发器。较小的阈值会将良性令牌误认为触发器,而较大的阈值会将触发器误认为良性令牌。为了解决这个问题,我们调整阈值,并评估了CodeDetecter对抗攻击基线的性能。实验设置遵循RQ3中的实验设置。结果如图8所示。图中显示的FPR和召回率是CodeDetecter对抗攻击基线的平均结果。我们可以看到,FPR和召回率随着阈值提升逐渐减少,在阈值增加到0.3后,FPR缓慢下降,召回率急剧下降。我们的目标是获得更高的召回率和更低的FPR。因此,我们默认将防御阈值设置为0.3。

图8:检测阈值t的影响

转述:高晨宇

0 阅读:0

互联不一般哥

简介:感谢大家的关注