长亭百川云 - 文章详情

利用代码相似度检测污点传播中的过滤函数

小陈的Life

72

2024-07-13

戳上面的蓝字关注我吧!


01

前言

上篇关于《SpringInspector源码分析文章》中提到过“污点传播过程中清洗函数怎么定义?能否通过自然语言处理提取其特征来自动化检测?”,近段时间才得空能接着上次的想法继续思考,由于SpringInspector检测工具对规则的依赖性比较强,因此检测污点分析中的过滤函数也是精进工具必不可少的一环。

常用的程序代码检测技术主要分为两类,即:

  1. 属性计数(Attribute Counting,AC):针对程序代码的各种统计属性进行处理,从程序代码中提取多个软件度量特征,然后再用这些特征比较程序从而得到程序相似度。该方法也是目前为止软件科学度量方法中最早的典型属性计数法。

  2. 基于结构度量(Structural Metrics,SM):该方法加入了程序内部结构信息的比较。

而目前的匹配技术来实现相似度计算的方法有:

  • 点阵图(dotplot)

  • 余弦相似度

  • Levenshtein距离法

  • 最长公共子序列(Longest Common Subsequence,LCS)

  • 求最长公共子串RKR-GST法

而对此代码相似度的检测方案也有两个思路:

  1. 权重比较法,获取代码中的关键词来比较权重。

  2. 解析AST,并使用深度优先遍历DFT来生成文本向量,之后再对文本向量进行相似度分析。

02

**关键词权重比较法
**

关键词权重比较方法是传统意义上的相似度比较方法,是通过捕获源代码中是否存在某些关键词,然后更具优先程度来计算余弦相似度的一种方式。

其中正则匹配阶段有使用JavaParase的方法读取其方法的属性,进一步准确的提取相关特征。

删除注释的方式可以通过正则匹配,过滤掉单行和多行注释

`String code = n.getBody().get().toString().replaceAll("(?<!:)\\/\\/.*", "").replaceAll("\\/\\*(\\s|.)*?\\*\\/", "");``System.out.println(code);`

有了过滤完的代码段之后,就只需要对其提取特征并判断

`float isName = (n.getName().toString().toLowerCase().contains("xss"))? 1 : 0;    //方法名称中是否有Xss关键词``Parameter p = n.getParameters().get(0);``float isParam = (p.getTypeAsString().contains("String"))? 1 : 0;    //参数中是否有String类型``   ``String code = n.getBody().get().toString().replaceAll("(?<!:)\\/\\/.*", "").replaceAll("\\/\\*(\\s|.)*?\\*\\/", "");  //过滤方法体中的注释``   ``Pattern pat = Pattern.compile("([a-zA-Z0-9_.&%]+)+");  //实例化Pattern对象``Matcher mat = pat.matcher(code);  //实例化Matcher对象``float isReplace = 0;``float isScript = 0;``float isOnEvent = 0;``float isUnEscapeHtml4 = 0;``while(mat.find()) {`    `String str = mat.group().toLowerCase();`    `if(str.contains("replace")) {    //方法体是否有调用Replace方法`        `isReplace = 1;`    `}`    `if(str.contains("script")) {    //常见Script标签`        `isScript = 1;`    `}`    `if(str.equals("onmouseover") || str.equals("onclick") || str.equals("onunload") || str.equals("onerror")) {    //常见的事件`        `isOnEvent = 1;`    `}`    `if(str.contains("StringEscapeUtils") || str.contains("unescapeHtml4")) {  //HTML实体化方法`        `isUnEscapeHtml4 = 1;`    `}``}``flag = new float[]{(float) isName, isParam, isReplace, isScript, isOnEvent, isUnEscapeHtml4};`

上述代码片段展示了是如何提取相关方法特征的,最后通过余弦相似度算法计算

`package javaTest;``   ``import java.io.FileInputStream;``import java.io.FileNotFoundException;``import java.util.ArrayList;``import java.util.Arrays;``import java.util.List;``import java.util.regex.MatchResult;``import java.util.regex.Matcher;``import java.util.regex.Pattern;``import java.util.stream.Collectors;``   ``import com.github.javaparser.StaticJavaParser;``import com.github.javaparser.ast.CompilationUnit;``import com.github.javaparser.ast.body.MethodDeclaration;``import com.github.javaparser.ast.body.Parameter;``import com.github.javaparser.ast.visitor.VoidVisitorAdapter;``   ``   ``public class SimilarityMain {``   `  `public static ArrayList<Float> vb = new ArrayList() {{`    `add(1);`    `add(1);`    `add(1);`    `add(1);`    `add(1);`    `add(1);`  `}};`  `public static float flag[] = {(float) 0, 0, 0, 0, 0, 0};`  `public static ArrayList<String> vitem = new ArrayList() {{`    `add("方法名称中有Xss关键词");`    `add("参数中有String类型");`    `add("有调用Replace方法");`    `add("常见Script标签");`    `add("常见的事件");`    `add("HTML实体化方法");`  `}};``   `    `public static double similarity(ArrayList va, ArrayList vb) {`        `if (va.size() > vb.size()) {`            `int temp = va.size() - vb.size();`            `for (int i = 0; i < temp; i++) {`                `vb.add(0);`            `}`        `} else if (va.size() < vb.size()) {`            `int temp = vb.size() - va.size();`            `for (int i = 0; i < temp; i++) {`                `va.add(0);`            `}`        `}``   `        `int size = va.size();`        `double simVal = 0;``   ``   `        `double num = 0;`        `double den = 1;`        `double powa_sum = 0;`        `double powb_sum = 0;`        `for (int i = 0; i < size; i++) {`            `double a = Double.parseDouble(va.get(i).toString());`            `double b = Double.parseDouble(vb.get(i).toString());``   `            `num = num + a * b;`            `powa_sum = powa_sum + (double) Math.pow(a, 2);`            `powb_sum = powb_sum + (double) Math.pow(b, 2);`        `}`        `double sqrta = (double) Math.sqrt(powa_sum);`        `double sqrtb = (double) Math.sqrt(powb_sum);`        `den = sqrta * sqrtb;``   `        `simVal = num / den;``   `        `return simVal;`    `}``   `    `public static void generateArrayList(String file) throws FileNotFoundException {`        `FileInputStream in = new FileInputStream(file);``   `        `CompilationUnit cu = StaticJavaParser.parse(in);`        `new MethodVisitor().visit(cu, null);`    `}``   `    `/**`     `* Simple visitor implementation for visiting MethodDeclaration nodes.`     `*/`    `private static class MethodVisitor extends VoidVisitorAdapter {`        `@Override`        `public void visit(MethodDeclaration n, Object arg) {`          `float isName = (n.getName().toString().toLowerCase().contains("xss"))? 1 : 0;    //方法名称中是否有Xss关键词`            `Parameter p = n.getParameters().get(0);`            `float isParam = (p.getTypeAsString().contains("String"))? 1 : 0;    //参数中是否有String类型``   `            `String code = n.getBody().get().toString().replaceAll("(?<!:)\\/\\/.*", "").replaceAll("\\/\\*(\\s|.)*?\\*\\/", "");  //过滤方法体中的注释``   `            `Pattern pat = Pattern.compile("([a-zA-Z0-9_.&%]+)+");  //实例化Pattern对象`            `Matcher mat = pat.matcher(code);  //实例化Matcher对象`            `float isReplace = 0;`            `float isScript = 0;`            `float isOnEvent = 0;`            `float isUnEscapeHtml4 = 0;`            `while(mat.find()) {`                `String str = mat.group().toLowerCase();`                `if(str.contains("replace")) {    //方法体是否有调用Replace方法`                  `isReplace = 1;`                `}`                `if(str.contains("script")) {    //常见Script标签`                  `isScript = 1;`                `}`                `if(str.equals("onmouseover") || str.equals("onclick") || str.equals("onunload") || str.equals("onerror")) {    //常见的事件`                  `isOnEvent = 1;`                `}`                `if(str.contains("StringEscapeUtils") || str.contains("unescapeHtml4")) {  //HTML实体化方法`                  `isUnEscapeHtml4 = 1;`                `}`            `}`            `flag = new float[]{(float) isName, isParam, isReplace, isScript, isOnEvent, isUnEscapeHtml4};`            `ArrayList<Float> va = new ArrayList();`        `for (int i = 0; i < flag.length; i++)`        `{`          `va.add(new Float(flag[i]));`        `}``   `        `SimilarityMain sim = new SimilarityMain();`        `double simVal = sim.similarity(va, vb);`        `System.out.println("Method Name:" + n.getName() + ", The sim value is:" + simVal);`        `for(float a : flag) {`          `System.out.print(a+" ");`        `}`        `System.out.println();`        `}`    `}``   `  `public static void main(String[] args) throws FileNotFoundException {`    `System.out.print("特征列表");`    `System.out.println(vitem);`    `generateArrayList("C:\\Users\\DELL\\eclipse-workspace\\javaTest\\src\\javaTest\\Hello.java"); //0.81``   `  `}``   ``}`

03

基于AST的代码相似度检测

首先对Java代码进行预处理,减少冗余信息。然后对预处理后的代码进行词法分析、语义分析等步骤,最终生成AST语法树。之后再对生成好的AST转换成字符串序列,再对字符串序列进行相似度计算,检测代码是否相似。

AST解析工具我这里用的是ANTLR4的库类,使用专门的词法分析和语法分析来解析Java源代码。

Antlr (ANother Tool for Language Recognition) 是一个强大的跨语言语法解析器,可以用来读取、处理、执行或翻译结构化文本或二进制文件。它被广泛用来构建语言,工具和框架。Antlr可以从语法上来生成一个可以构建和遍历解析树的解析器。

添加如下依赖

`<dependency>`    `<groupId>org.antlr</groupId>`    `<artifactId>antlr4-runtime</artifactId>`    `<version>4.9.3</version>``</dependency>`

我这里的开发环境IDE是用的eclipse,因此还需要安装一个ANTLR的工具antlr4ide-eclipse-release[3]

安装之后就可以通过*.g4的文件规则生成对应的ANTLR4的词法分析和语法分析类了

插件正常安装之后,可以对*.g4的文件直接生成对应的类了。这里我给出Java8语法下的代码分析规则:

Java8Lexer(词法分析):https://github.com/antlr/grammars-v4/blob/master/java/java8/Java8Lexer.g4

Java8Parser(语法分析):https://github.com/antlr/grammars-v4/blob/master/java/java8/Java8Parser.g4

生成好了之后,可以用ANTLR4生成Java代码的字符串序列,方便之后进行相似度分析。

`public static String getAntlr4String(String path) throws IOException {`    `FileInputStream inputStream = new FileInputStream(path);    //获取java源代码文件的路径`    `Lexer lexer = new Java8Lexer(CharStreams.fromStream(inputStream));    //利用自定义的词法分析器`    `TokenStream tokenStream = new CommonTokenStream(lexer);    //获取词法分析器生成的Token`    `Java8Parser parser = new Java8Parser(tokenStream);      //语法解析器`    `ParserRuleContext t = parser.compilationUnit();`    `return t.toStringTree(parser);      //最后讲生成的AST转换为字符串序列组,方便后续进行相似度计算``}`

在这之后该方法会返回字符串序列

这里我用到文本相似度分析框架:https://github.com/shibing624/similarity

采用的是similarity框架的段落文本相似度分析计算

`public static void main(String[] args) throws ExitException {`    `System.out.println("Antlr4 Examples");`    `try {`        `String text1 = getAntlr4String("C:\\Users\\DELL\\eclipse-workspace\\javaTest\\src\\javaTest\\Hello.java");`        `String text2 = getAntlr4String("C:\\Users\\DELL\\eclipse-workspace\\javaTest\\src\\javaTest\\Hello2.java");`        `//          System.out.println(text1);`        `//          System.out.println(text2);`        `TextSimilarity cosSimilarity = new CosineSimilarity();`        `double score1 = cosSimilarity.getSimilarity(text1, text2);`        `System.out.println("cos相似度分值:" + score1);``   `        `TextSimilarity editSimilarity = new EditDistanceSimilarity();`        `double score2 = editSimilarity.getSimilarity(text1, text2);`        `System.out.println("edit相似度分值:" + score2);``   `    `} catch (IOException e) {`        `e.printStackTrace();`    `}``}`

这里用常见的XSS修复代码案例来做个测试:

https://github.com/ChenViVi/XssFilter/blob/ec4dc983efd4fe80d8c6ff07dc29dc5f8523964d/XssHttpServletRequestWrapper.java

https://github.com/agarwal-arjun/XSSFilter/blob/537bc086ce700608e435546fe673c537f79f67af/src/main/java/filters/wrapper/XSSRequestWrapper.java

上述两个Xss过滤代码是我从GitHub上检索到的公开源码,对其进行相似度查询:

首先是Cos余弦相似度分值:

然后是Edit编辑距离相似度分值:

可以看到两个分值一个在0.99,一个在0.49。相差还是蛮大的,而且Edit编辑距离算法用到递归的特性,所以运行时间要比余弦相似度更久,

经过几次比对之后发现,就算用两种不相干的方法片段,其Cos余弦相似度还是会一样,因此在这里就只能选择Edit编辑距离算法。当然读者也可以在这里替换其他准确度更高的相似度比较算法。

最后,不管是上述哪种方法,都有一定的阈值需要经过多次测试才能确保检出的稳定性。

04

Reference

[1].https://www.cnblogs.com/clonen/p/9083359.html

[2].https://github.com/antlr/antlr4

[3].https://github.com/antlr4ide/antlr4ide-eclipse-release

[4].https://github.com/shibing624/similarity

[5].http://www.yardwill.com/detail/%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E8%AF%AD%E6%B3%95%E8%BD%AC%E6%8D%A2%E5%99%A8

[6].https://blog.csdn.net/lockhou/article/details/113883940

[7].https://blog.csdn.net/qq\_37857921/article/details/103489583

[8].https://github.com/lcygithub/CosineSimilar/blob/master/similarity.py

[9].张丽萍,刘东升,李彦臣,钟美.一种基于AST的代码抄袭检测方法[J].计算机应用研究,2011,28(12):4616-4620.

[10].朱波,郑虹,孙琳琳,杨友星.基于AST的程序代码相似性度量研究[J].吉林大学学报(信息科学版),2015,33(01):99-104.

[11].史志成,周宇.代码特征自动提取方法[J].计算机科学与探索,2021,15(03):456-467.

[12].李彦臣. 基于后缀语法树的代码抄袭检测研究[D].内蒙古师范大学,2011.


相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2