CodeAttack:针对预训练编程语言模型的基于代码的对抗性攻击

互联不一般哥 2024-07-19 16:08:53

论文地址:2206.00052 (arxiv.org)

代码:https://github.com/reddy-lab-code-research/CodeAttack

摘要

预训练的编程语言(PL)模型(如CodeT5、CodeBERT、GraphCodeBERT等)具有自动完成包括代码理解和代码生成在内的软件工程任务的潜力。然而,这些模型是在代码的自然通道中运行的,也就是说,它们主要关注人类对代码的理解。它们对输入的变化缺乏鲁棒性,因此很容易受到自然通道中的对抗性攻击。我们提出了CodeAttack,这是一个简单而有效的黑盒攻击模型,它利用代码结构生成有效、高效且隐蔽的对抗性代码样本,证明了最先进的PL模型在面对特定代码对抗性攻击下的脆弱性。我们评估了CodeAttack在不同编程语言中的几个code-code(翻译和修复)和code-NL(摘要)任务的可移植性。结果表明,CodeAttack超越了最先进的对抗性NLP攻击模型,实现最佳的整体性能下降,同时更高效、不易察觉、一致和流畅。

1 引言

最近,研究人员对通用编程语言(PL)模型的开发热情高涨。PL模型可以捕获自然语言和源代码之间的关系,能够自动完成包括代码理解(克隆检测、缺陷检测)和代码生成(代码-代码翻译、代码-代码精化、代码-NL总结)在内的软件工程开发任务。然而,由于需要海量的代码数据对上述模型进行数据驱动的预训练,这些通用编程语言(PL)模型的主要应用范围被限制在了代码的“自然通道”中。这种“自然通道”侧重于通过代码注释、有意义的变量名和函数名向人类传递信息。在这种情况下,需要仔细研究预训练模型的鲁棒性和脆弱性。在这项工作中,我们利用代码结构在代码的自然通道中生成对抗性样本,并证明了最先进的编程语言模型在对抗性攻击下的脆弱性。

对抗性攻击的特点是在输入中注入不易察觉的变化以造成机器学习模型的错误预测。对于在自然通道中运行的预训练PL模型来说,这样的攻击非常重要,一是能够暴露系统漏洞和评估模型的鲁棒性,输入代码中一个微小的变化(类似于NL场景中的一个错别字)就能触发代码摘要模型生成错误的自然语言代码摘要(如图1);二是与模型可解释性相关,对抗样本可用于检查预训练PL模型所关注的token。

在代码的自然通道中,成功的对抗性攻击应该具有以下属性:

(i)最小扰动:类似于自然语言中的拼写错误或同义词替换,能够以难以觉察的变化误导神经模型

(ii)代码一致性:受扰动的代码与原始输入代码保持一致,且遵循相同的编码风格

(iii)代码流畅性:不会改变用户对原始代码的理解

图1:CodeAttack对输入代码片段(红色部分)做了一个小修改,这会导致从SOTA预训练的编程语言模型中获得的代码摘要发生重大变化。关键词用蓝色突出显示,注释用绿色突出显示。

目前的自然语言对抗性攻击模型在这三个方面都存在不足。因此,我们提出了CodeAttack,一个简单而有效的黑盒攻击模型,可针对任何输入的代码片段(无论哪种编程语言)在自然通道中为其生成对抗性样本。在实际使用中,攻击者无法访问模型参数,只能访问测试查询和模型预测。CodeAttack使用预训练的掩码CodeBERT模型作为对抗性代码生成器,利用代码结构生成隐蔽且有效的对抗示例。

我们的主要贡献如下:

据我们所知,我们的工作首次在代码的自然通道中检测预训练编程语言模型在对抗性攻击下的漏洞。我们提出了一种简单而有效的黑盒攻击方法CodeAttack,不管输入的编程语言是什么,它都能为代码片段生成对抗性样本。我们设计了一种针对序列到序列的PL模型的通用黑盒攻击方法,该方法可在不同的下游任务(如代码翻译、修复和总结)中使用。我们的方法具有与输入语言无关的特性,因此也可以扩展到其他领域的序列到序列任务。我们通过广泛的实证评估证明了CodeAttack相对于现有NLP对抗模型的有效性。从攻击质量和效果两方面考虑,CodeAttack均优于自然语言基线。

2 方法

2.1 威胁模型

攻击者的能力:攻击者能够干扰作为预训练PL模型输入的测试查询,从而生成对抗性样本。我们沿用现有文献的方法来生成自然语言对抗示例,并允许对自然通道中的输入代码序列进行两种类型的扰动,分别为字符级扰动和token级扰动。攻击者只能干扰一定数量的token/字符,并且必须确保原始代码和被干扰的代码之间高度相似。从形式上看,对于给定的输入代码序列,其中X为输入空间,一个有效的对抗性代码示例满足以下要求:

其中,是允许的最大扰动;Sim(·)是相似度函数;是相似度阈值。

攻击者的知识:我们假设进行标准的黑盒访问,以实际评估现有预训练PL模型的漏洞和鲁棒性。攻击者无法访问模型参数、模型架构、模型梯度、训练数据和损失函数,只能用输入序列来查询预训练PL模型,并获得相应的输出概率。这比白盒场景更实用,因为在白盒场景中攻击者可以访问上述所有内容。

攻击者的目标:给定一个输入代码序列作为查询,攻击者的目标是通过在代码的自然通道中以隐蔽的方式修改查询,从而降低生成的输出序列的质量。生成的输出序列可以是代码片段(针对代码翻译和代码修复任务)或自然语言文本(针对代码摘要任务)。形式上,给定一个预训练的PL模型 F:X→Y,其中X是输入空间,Y是输出空间,攻击者的目标是为输入序列生成一个对抗样本,使得:

其中表示生成输出的质量,φ表示指定的质量下降幅度。这是对前面在上应用的约束的补充。我们将生成对抗样本的最终问题表述如下:

在上述目标函数中,是受扰动δ约束的最小扰动对抗样本。CodeAttack搜索目标扰动,使得原始输入代码X与扰动代码产生的输出序列的质量差达到最大。

2.2 攻击方法

攻击方法主要有两个步骤,一是找到最易受攻击的token,二是替换这些易受攻击的token(受代码特定约束),以此在代码的自然通道中生成对抗性样本。

寻找易受攻击的token:CodeBERT在进行预测时会更多关注关键字和标识符。我们利用这一信息并假设某些输入token比其他输入token对最终预测的影响更大。同“攻击”非易受攻击的token相比,“攻击”这些高度重要或极易受攻击的token会更显著地增加改变模型预测的概率。在黑盒设置下,模型梯度不可见,攻击者只能访问预训练PL模型的输出概率。我们将“易受攻击的token”定义为对模型的输出概率有很大影响的token。设F为预训练的编码器-解码器PL模型,给定输入序列

其中为输入token,输出是一个向量序列

其中是在时间步长t下正确的输出token 的输出概率。在不损失一般性的前提下,我们也可以假设输出序列

其中,Y既可以是代码序列,也可以是自然语言token。

为了找到易受攻击的输入token,我们将token替换为[MASK],使得

并获取其输出概率。输出向量现在为

其中为正确预测y的新的输出概率。token 的影响力分数如下:

我们根据影响力分数对所有token进行降序排列,寻找最脆弱的token V。我们选择top-k token来限制扰动的数量,并通过替换它们或插入/删除周围字符的方式来迭代地攻击它们。

替换易受攻击的token:我们采用贪婪搜索,使用掩码编程语言模型,在特定于代码的约束下,找到易受攻击token V的替代S,使得扰动最小,预测错误的概率最大。

搜索方法:在给定的输入序列中,我们会屏蔽一个易受攻击的token ,并使用掩码PL模型来预测一个处于该位置的有意义的上下文token。我们使用每个被屏蔽的易受攻击token的top-k预测作为初始搜索空间。设M为掩模PL模型,给定输入序列

其中是一个易受攻击的token,M使用WordPiece算法进行分词,将不常见的词分解成子词,得到

我们对的所有对应子词进行对齐和屏蔽,结合预测结果得到易受攻击的token 的top-k替代品

这个初始搜索空间由易受攻击的token 的个可能的替代组成。随后我们会逐步过滤掉替代token以确保生成的对抗性样本具有最小扰动,在保证代码一致性和代码流畅性的同时遵守特定于代码的约束。

特定于代码的约束:由于掩码PL模型生成的token可能不是有意义的单个代码token,因此我们需要进一步使用CodeNet分词器将token分解为相应的代码token。代码token被标记为四个主要的代码token类(表1)。如果是由M标记的易受攻击的token 的替代,并且Op(·)表示使用CodeNet分词器时存在于任意给定token中的操作符,我们允许替代token具有额外或缺失的操作符(类似于代码自然通道中的输入错误)。

让C(·)表示token的代码token类(标识符、关键字和参数)。我们保持和潜在的替代之间的对齐如下:

上述代码约束保持了的代码流畅性和代码一致性,同时显著减少了寻找对抗性示例的搜索空间。

表1:token类及其对应描述

替代:我们允许对易受攻击的token进行两种类型的替换来生成对抗性示例,分别为操作符(字符)级别的替换——只有一个操作符被插入/替换/删除和令牌级替换。我们使用简化的搜索空间S并进行迭代替换,直至攻击者的目标得到满足。我们只允许替换最多Pp%的易受攻击的token/字符,以限制扰动的数量。我们还保证输入文本X与对抗扰动文本之间的余弦相似度高于某个阈值 (公式3)。完整算法见算法1。CodeAttack可以保持输入代码片段和对抗代码片段之间的最小扰动,保证代码流畅性和代码一致性。

算法1:CodeAttack:生成代码的对抗样本

3 结果

3.1 实验设置

下游任务和数据集:我们评估了CodeAttack在不同下游任务和不同编程语言中的可移植性,包括代码翻译(C#和Java之间的翻译),代码修复(自动修复Java函数中的错误,使用‘small’数据集)和代码摘要(为给定代码生成自然语言摘要)。我们使用CodeSearchNet数据集,语言为Python, Java和PHP。

攻击模型: CodeT5(基于预训练编码器-解码器的PL模型),CodeBERT(双峰预训练PL模型),GraphCodeBERT(预训练图PL模型)和RoBERTa(预训练的NL模型)。

基线模型:由于CodeAttack在代码的自然通道中运行,我们将其与两个最先进的对抗性NLP基线进行公平比较,分别为TextFooler(使用同义词、部分语音检查和语义相似性来生成对抗性文本)和BERT-Attack(使用预训练的BERT掩码语言模型来生成对抗性文本)。

评价指标:我们评估生成的对抗性代码的有效性和质量。

关于攻击有效性,我们定义了如下指标:

1.

我们使用CodeBLEU和BLEU来测量攻击前后下游性能的下降。定义如下:

其中;Y为ground truth输出;为被攻击的预训练PL模型,为生成的对抗性代码序列。CodeBLEU度量用于代码翻译和代码修复的生成代码片段的质量,BLEU度量生成的自然语言代码摘要的质量(相比于ground truth)。

2.Success%

计算以衡量的攻击成功率。该值越高,说明对抗性攻击就越有效。

关于攻击质量,我们从效率、隐蔽性和代码一致性三个方面度量生成的对抗性代码的质量:

1.#Queries

在黑盒环境下,攻击者可以通过查询受害者模型来检查输出概率的变化。每个样本所需的平均查询数越低,攻击的效率就越高。

2.#Perturbation

为生成对抗性代码而平局更改的token数量。该值越低,说明攻击就越难以察觉。

3.

测量对抗性代码的一致性,公式如下:

其中,是对原始输入源代码进行扰动后生成的对抗性代码序列。该值越高,说明对抗性代码与原始源代码越一致。

实现细节:该模型使用PyTorch实现。我们使用公开可用的预训练CodeBERT (MLM)掩码模型作为对抗代码生成器。我们选择每个易受攻击token的前50个预测作为初始搜索空间,并最多允许攻击40%的代码token。原始代码和对抗性代码之间的余弦相似度阈值设置为0.5。作为受害者模型,我们对CodeT5使用公开可用的微调检查点,并在相关的下游任务上微调CodeBERT、GraphCodeBERT和RoBERTa。批处理大小设置为256。所有实验均在48 Gib RTX 8000 GPU上进行。CodeAttack的源代码可以在https://github.com/reddy-lab-code-research/CodeAttack上找到。

3.2 表现

我们研究了以下问题:

RQ1:使用CodeAttack生成的攻击对不同下游任务和编程语言的有效性和可移植性如何?RQ2:使用CodeAttack生成的对抗性样本的质量如何?RQ3:当我们限制允许的扰动数量时,CodeAttack是否有效?RQ4:不同的组件对CodeAttack的性能有什么影响?

RQ1:CodeAttack的有效性

我们在三个不同的序列到序列任务(代码翻译、代码修复和代码摘要)上测试了CodeAttack生成的对抗样本的有效性和可移植性。我们生成了四种编程语言(C#、Java、Python和PHP)的对抗代码,并攻击了四种不同的预训练PL模型(CodeT5、GraphCodeBERT、CodeBERT和Roberta)。C#-Java翻译任务和PHP代码摘要任务的结果如表2所示。与其他对抗性NLP基线相比,CodeAttack的成功率最高。CodeAttack在9个案例中有6个案例的表现也优于对抗性NLP基线(BERT-Attack和TextFooler),在代码翻译和代码修复任务上使用CodeAttack的平均分别在20%和10%左右。对于代码摘要,CodeAttack使所有受害模型的BLEU降低了近50%。由于BERT-Attack无差别地替换token,因此它的在某些情况下较高,但其攻击质量最低。

表2:翻译(C#-Java)、修复(Java-Java)和总结(PHP)任务的结果。对Code-Code任务用CodeBLEU衡量,对Code-NL任务用BLEU衡量。最好的结果用黑体字标出;次好的结果用下划线标出。

RQ2:CodeAttack的攻击质量

定量分析。与其他对抗性NLP模型相比,CodeAttack是最有效的,因为它需要最少的查询次数就能攻击成功(表2)。CodeAttack也是最难以察觉的,因为在9个案例中有8个案例所需的平均扰动次数为1-3个token。从来看,CodeAttack生成的对抗性样本的代码一致性可与成功率非常低的TextFooler媲美。CodeAttack的整体性能最佳。

定性分析。图2给出了不同攻击模型生成的对抗性代码片段的定性分析。TextFooler的得分略高,它使用相关的自然语言单词来替换关键词(public->audiences;override->revoked;void->cancelling);BERT-Attack的得分最低,它使用看似随机的单词替换token。TextFooler和BERT-Attack都不是为编程语言设计的。CodeAttack通过用难以察觉和一致的变量和操作符来替换易受攻击的token,生成更有意义的对抗性代码样本。

语法正确性。尽管CodeAttack和其他PL模型主要在代码的自然通道中操作,也就是说,它们关心的是人类对代码的理解,而不是代码的执行或编译,但生成的对抗性代码的语法正确性仍是评估攻击质量的有效标准。前面介绍的数据集是由代码片段组成,无法编译。因此,我们使用TextFooler、BERT-Attack和CodeAttack生成C#、Java和Python的对抗性代码,并请3名熟悉这些语言的人工注释员手动验证语法。我们随机抽取了三种编程语言生成的60个对抗性代码,对上述每种方法进行评估。结果表明,CodeAttack在C#(70%)、Java(60%)和Python(76.19%)的平均语法正确性最高,其次是BERT-Attack和TextFooler(图3)。

图3:C#,Java和Python的对抗性代码的语法正确性。

RQ3:使用CodeAttack时限制扰动

我们严格限制了攻击预训练PL模型时的扰动数量,并研究了CodeAttack的有效性。从图4a可以看出,随着扰动百分比的增加,CodeAttack的减少,但TextFooler保持不变,BERT-Attack仅略有下降。我们还观察到,尽管CodeAttack的是第二好的(图4b),但它具有最高的攻击成功率(图4d),攻击成功所需的查询次数最少(图4c)。这表明了CodeAttack的效率和对特定代码的对抗性攻击的需求。

图4:改变扰动以研究对CodeT5上的代码转换任务(C#-Java)的攻击有效性。

RQ4:消融实验

易受攻击token的重要性。我们创建了一个变体““,它从输入代码中随机选择token进行替换。我们定义了另一个变体”“,它根据概率信息查找易受攻击的token并攻击它们,没有任何约束。从图5a中可以看出,攻击随机token的效果不如攻击脆弱token的效果。与相比,在三种模型中,使用得到更大的,所需的查询次数更少,(图5b)和Success%(图5d)相近。

特定于代码的约束的重要性。我们找到易受攻击的token并应用两种类型的约束,操作符级别约束()和token级别约束()。仅应用操作符级别的约束会导致较低的攻击成功率(图5d)和较低的 (图5a),但可以获得更高的。这是因为我们只将更改限制在导致最小变化的操作符上。同时应用操作符级别和token界别的约束时,和攻击成功率显著提高。总体而言,最终模型CodeAttack由, 和组成,对于所有的预训练PL受害者模型,它在,攻击成功率,和#Queries之间具有最佳折衷。

人工评估。我们抽取了50个原始的和经过扰动的Java和C#代码,并将它们混合在一起。我们邀请3名熟悉这两种编程语言的人类注释员,通过在源代码的自然通道对其进行评估,将代码分类为原始代码或对抗代码。平均而言,72.1%的代码被归类为原始代码。我们还要求他们阅读给定的对抗性代码,并用从1到5的分数来表示他们对代码的理解程度。其中1分代表“代码根本无法理解”,5分代表“代码是完全可以理解的”。对抗性代码的平均理解能力为4.14。此外,我们为注释者提供了成对的对抗性代码和原始代码,并要求他们使用从0到1的分数对两者之间的代码一致性进行评级。其中0分对应“与原始代码完全不一致”,1分代表“与原始代码非常一致”。平均而言,代码一致性的分数为0.71。

图5:代码转换的消融研究(C#-Java):CodeAttack不同组件的性能,包括随机(RAND)和易受攻击token(VUL),以及两个特定于代码的约束——操作符级别(OP)和token级别(TOK)。

转述:肖媛

0 阅读:0

互联不一般哥

简介:感谢大家的关注