利用代码相似度检测污点传播中的过滤函数
2022-7-18 11:34:14 Author: mp.weixin.qq.com(查看原文) 阅读量:4 收藏


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.

文章来源: https://mp.weixin.qq.com/s?__biz=MzAxNDk0MDU2MA==&mid=2247484256&idx=1&sn=f6e65fa3496a50a954e7a31f29916a4e&chksm=9b8ae39facfd6a89c1491ee666cfa093d38846f27d95b1a849f9237c0b5778c82f4ea57da916&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh