静态代码分析敏感性概念

举报
maijun 发表于 2021/01/01 16:19:39 2021/01/01
【摘要】 本文介绍几种在静态代码分析中的敏感性分析的概念。主要有流敏感(flow-sensitive),路径敏感(path-sensitive),上下文敏感(context-sensitive)和域敏感(field-sensitive)。所有的敏感性分析相对于非敏感性分析,在分析的准确性上,都会有很大的精度提升(如减少漏报和误报等),但是会在时间或者空间上有更大损耗。

本文介绍几种在静态代码分析中的敏感性分析的概念。主要有流敏感(flow-sensitive),路径敏感(path-sensitive),上下文敏感(context-sensitive)和域敏感(field-sensitive)。所有的敏感性分析相对于非敏感性分析,在分析的准确性上,都会有很大的精度提升(如减少漏报和误报等),但是会在时间或者空间上有更大损耗。

1.    流敏感(flow-sensitive)

流敏感分析,是指在分析时,区分程序的执行顺序的分析。如下面的两段代码:

代码片段一:

String name = "zhangsan";
name = System.getProperty("name");
String sql = "select * from user where name = " + name;
sqlExecute(sql);

代码片段二:

String name = System.getProperty("name");
name = "zhangsan";
String sql = "select * from user where name = " + name;
sqlExecute(sql);

这里,我们设定sqlExecute是执行一条sql命令的方法,则上面的两个代码片段中,System.getProperty()是从环境变量中获取数据,可以认为是污点入口。如果是代码片段一,可能发生sql注入风险,因为name在第2行,被赋给了一个外部传入的数据,第3行将name传递给了sql,在第4行,sql被传入一个污点坑。而代码片段二,则没有可能发生sql注入风险,因为在第2行,name是个常量,第3行sql也没有被污染,是拼接了一个常量zhangsan。

上面的分析,实际上是一种流敏感的分析,我们分析了name在第1行和第2行指向的内容,从而知道在代码片段一中,第3行的name指向一个污染数据,代码片段二中,第3行的name指向的是一个常量字符串,从而知道上面代码片段一有sql注入风险,代码片段二没有sql注入风险。而如果是流不敏感分析,则不管是代码片段一还是代码片段二,都只能得出来的一个结论是:3行的name指向的是常量字符串或者是外部传入的污染数据,从而得到的结论是代码片段一和代码片段二都有sql注入的风险

从上面的一个简单的结合指向分析和污点传播的案例,我们可以知道,流敏感分析可以有效降低分析的误报,提高分析的准确性。绝大部分的数据流分析,例如常量传播、指向分析等,都需要是流敏感分析,当然,也不是说所有的分析都需要是流敏感的,比如静态类型语言中,分析一个变量的类型,分析到一个位置的某个变量的类型信息后,其他地方的该变量自然就都是该类型的。

流敏感分析已经是程序分析中,数据流分析的最基本要求,已有的一些成熟的代码分析框架,例如Java中的Soot和Wala,C/C++中的Clang等,都是原始支持流敏感的分析。

部分源码,在生成SSA形式的三地址码后,SSA上的非流敏感分析,在相当程度上,也可以实现类似于普通三地址码的流敏感分析的效果,因为SSA中每个变量只会有一次赋值,生成SSA形式后,实际上的效果就是每个变量只会有一个指向,例如上面的代码片段,当针对name变量,针对两次赋值区分name1和name2之后,在sql中,都使用name2赋值,不会再有不同指向的问题。

2.    路径敏感(path-sensitive)

路径敏感,就是在进行分析时,同时考虑了分支路径上面的条件信息。如下面的两段代码:

代码片段一:

String name;
int x = 3;
if (x > 0) {
    name = System.getProperty("name");
} else {
    name = "zhangsan";
}
String sql = "select * from user where name = " + name;
sqlExecute(sql);

代码片段二:

String name;
int x = -1;
if (x > 0) {
    name = System.getProperty("name");
} else {
    name = "zhangsan";
}
String sql = "select * from user where name = " + name;
sqlExecute(sql);

上面的两段代码除了x的取值外,都相同。那么来分析上面的代码。当x大于0时,name是一个从环境变量里面获取的污点数据,当x小于等于0时,name是一个常量zhangsan,然后执行sql命令的拼接,并执行命令。

现有的数据流分析,一般都可以识别到分支操作,然后执行到分支完毕时,执行一个merge的操作,然后继续执行。如上,name在分支结束后,内容是 {System.getProperty("name")}∨{zhangsan},如果是考虑污点分析的抽象值,则为{Tainted}∨{NotTainted}={Tainted,NotTainted},然后sql被污染,最后发生一个sql注入问题(一般静态代码分析,都是sound分析,所以会报一个缺陷)。这种是不考虑路径敏感的场景。

上面,不考虑分支条件的时候,对代码片段一和代码片段二分析时,都会报出来一个sql注入的问题。当带上分支条件时,对于第二种场景,计算约束constraint({x == -1}∨{x > 0}) 无解,可以知道污点分支走不进去,从而可以知道其实代码片段二是不会发生sql注入的,而只有代码片段一会发生sql注入。

从上面的分析,路径敏感分析,可以有效降低静态代码分析中的误报,提高分析的准确性。这就要求,在获取每一条分支路径分析时,都需要同时保存分支的条件,然后通过约束求解方法,获取分支可达性,从而降低误报。另外,如果是基于SSA形式的分析,一般可以通过在构造φ函数时,同时保存分支信息来实现。

当前,很多静态代码分析框架,例如Java中Soot,原生并没有关于路径分支的可达性的计算支持(即非路径敏感分析,《编译原理》中介绍的数据流分析,就是非路径敏感的),所以如果是基于这些框架开发静态代码分析框架,需要考虑路径敏感性分析的实现(基于分支条件的约束求解),但是也需要注意可能的路径爆炸等问题。

3.    上下文敏感(context-sensitive)

上下文敏感,就是考虑函数调用的上下文信息。一个函数可能会被多个函数调用,那么在不同的函数调用它的时候,在传给它的实际参数或当时的全局变量不同的情况下,可能有不同的行为,如下面的一段代码:

public static void main(String[] args) {
    String name1 = getName(3);  // Tainted
    String sql1 = "select * from user where name = " + name1;
    sqlExecute(sql1);  // Taint Sink
   
    String name2 = getName(-1);  // Not Tainted
    String sql2 = "select * from user where name = " + name2;
    sqlExecute(sql2);
}

private static String getName(int x) {
    if (x > 0) {
        return System.getProperty("name");
    } else {
        return "zhangsan";
    }
}

如上所示,getName()方法基于入参的不同,会返回不同的结果,如上面的代码,在第2行和第6行,获取到的name1和name2的污点信息不同,当入参为3时,返回的是一个从环境变量中获取的污染的数据,到导致sql注入,而当入参为-1时,返回的是一个常量,不是污染数据,不会有问题。

所以,在上下文敏感的分析中,在第4行应该报一个sql注入问题,而在第8行则不应该报sql注入问题。而上下文非敏感的分析中,不考虑传入参数的不同,getName()方法,则全部返回一个{System.getProperty("name")}∨{zhangsan},从而导致第4行和第8行都会报一个sql注入的问题。

如上分析,上下文敏感分析,可以减少误报,提高分析精度,同时,也对函数建立摘要提出了挑战。上下文,指在分析函数调用过程中,可能影响函数行为的所有的信息,不仅仅包含传递的实参,还包括全局变量、实参类型等信息,根据我们的分析的目标来确定(静态代码分析,在一定程度上,全都需要进行一定程度的抽象,需要根据分析目标,在上下文和结果上进行合理抽象)。

4.    域敏感(field-sensitive)

域敏感,即针对对象属性、容器等数据类型上成员或者元素上面的分析问题。如下面的例子:

public static void main(String[] args) {
    String name = System.getProperty("name");
    Bean bean = new Bean();
    bean.setName(name);
    bean.setGender("male");

    String sql1 = "select * from user where name = " + bean.getName();
    sqlExecute(sql1);

    String sql2 = "select * from user where gender = " + bean.getGender();
    sqlExecute(sql2);
}

@Data
private static class Bean {
    private String name;
    private String gender;
}

如上面的例子,在第2行,name是污染数据,然后在第4行,将bean的name属性设为name,将bean的gender设置为常量male,从而bean的name属性是被污染的,gender属性没有被污染,继续往下分析,在执行sql1的第8行,应该报一个sql注入问题,而在执行sql2的第11行,不应该报sql注入问题。

如果是域敏感分析,可以将污点打在对象的属性上面,从而能够区分上面的场景,只在第8行报一个sql注入缺陷,而在第11行则不会报缺陷,但是如果是域不敏感分析,则无法报准确将污点打在对象的属性上面,从而导致污点被打在对象上,从而导致第8行和第11行都会报一个缺陷。

域敏感分析,对于面向对象语言的静态代码分析工具的实现,是一种非常重要的基础要求,如果无法实现域敏感分析,则会导致大量的无关的误报问题。当前,很多开源的分析框架,例如Java中基于Soot的IDEal,就实现了域敏感的分析(可以参考http://www.bodden.de/pubs/sab19context.pdf,Context-, Flow-, and Field-Sensitive Data-Flow Analysis using Synchronized Pushdown Systems),也有一些静态代码分析工具,将对象的属性延展为普通的变量来实现污点标记。

上面介绍的这种域敏感分析,是静态代码分析工具的基本要求,下面介绍两种域敏感分析,根据我的使用静态代码分析工具的经验,还很少有工具很好地支持了下面的场景,直接看代码:

案例一,List场景:

String name = System.getProperty("name");
List<String> arg = new ArrayList<>(2);
arg.add(name);
arg.add("male");

String sql1 = "select * from user where name = " + arg.get(0);
sqlExecute(sql1);

String sql2 = "select * from user where gender = " + arg.get(1);
sqlExecute(sql2);

案例二,Map场景:

String name = System.getProperty("name");
Map<String, String> map = new HashMap<>(2);
map.put("name", name);
map.put("gender", "male");

String sql1 = "select * from user where name = " + map.get("name");
sqlExecute(sql1);

String sql2 = "select * from user where gender = " + map.get("gender");
sqlExecute(sql2);

当前,还没有很好的工具,可以对上面的场景进行区分,如果没有误报的话,对于案例一和案例二,都是第7行应该有sql注入缺陷,第10行没有,但是,当前还没有一款很好的工具(也可能是有,但是我不知道),可以很好地对List和Map中的add 和 put 进行准确处理。List 的 add 方法,Map 的put 方法,都会把污点标记给打到整个容器上。

5.    总结

流敏感分析,针对的是同一个代码块内部的语句的顺序执行的数据流分析的要求;路径敏感分析,针对的是同一个方法内里面,能够区分不同分支的分析要求(数据流分析是路径不敏感的);上下文敏感分析,是针对跨过程调用的时候的数据流分析要求。这三个概念层层递进,都是面向程度执行结构的敏感性要求,是一个静态代码分析工具的基本通用要求,一般的静态代码分析框架,都应该实现这些基本要求。

域敏感,也叫属性敏感,主要针对面向对象语言(包括C语言中的结构体等)的一种分析技术,目的是提高静态代码的分析精度,贯穿整个分析过程。实际上,域敏感并不是必须完全需要实现,静态代码分析工具可以基于需要,在不同的层次上实现域敏感,同时,也可以刻意将待分析程序部分内容实现为域不敏感来提高性能(域敏感分析,对分析时间和分析所消耗的内存,都有显著影响)

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。