简述污点分析及SonarQube中污点分析配置
污点分析技术是一种常用的软件分析技术,广泛应用在软件静态代码分析中,本文将简单介绍污点分析的部分基本概念,及如何在SoanrQube中配置污点分析规则的信息。
1. 污点分析的关键概念
1.1 污点分析及传播类型
污点分析,即通过跟踪污点数据的传播,来识别程序中存在的问题的一种方法。很多典型的问题,例如注入类、敏感信息泄露等问题(规整点儿来讲,注入类问题破坏数据的完整性,名信息泄露破坏数据的保密性),都可以基于污点分析实现。下面举一个简单的例子:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// some code
response.setContentType("text/html;charset=UTF-8");
String param = "";
if (request.getHeader("BenchmarkTest00008") != null) {
param = request.getHeader("BenchmarkTest00008"); // 污点数据入口,从 HTTP Request 获取到的数据
}
// URL Decode the header value since req.getHeader() doesn't. Unlike req.getParameter().
param = java.net.URLDecoder.decode(param, "UTF-8"); // 污点传播,decode数据
String sql = "{call " + param + "}"; // 污点传播,污点数据拼接到 sql 中
try {
java.sql.Connection connection =
org.owasp.benchmark.helpers.DatabaseHelper.getSqlConnection();
java.sql.CallableStatement statement = connection.prepareCall(sql); // 构造 Statement
java.sql.ResultSet rs = statement.executeQuery(); // 漏洞爆发点,执行SQL语句
org.owasp.benchmark.helpers.DatabaseHelper.printResults(rs, sql, response);
} catch (java.sql.SQLException e) {
if (org.owasp.benchmark.helpers.DatabaseHelper.hideSQLErrors) {
response.getWriter().println("Error processing request.");
return;
} else throw new ServletException(e);
}
}
可以参考上面的注释,上面从http request中,获取到了一个数据,并且数据没有净化处理,直接拼接到了sql语句中,因此可能导致一个SQL注入的问题。
污点传播主要有两种类型:
(1) 显式污点传播
显示污点传播,即污点传播仅仅在数据依赖上面传递,如上面的例子,污点传播的各个数据之间,都有非常明确的依赖关系。这种类型的污点传播,一般都可以进行很好的分析。
(2) 隐式污点传播
隐式污点传播,即污点数据在传播时,不仅仅通过数据依赖产生关系,而且通过控制依赖传递。
请查看下面的函数定义:
public static String getStr(String str) {
if (str == null || str.trim().length() == 0) {
return "";
}
StringBuffer sb = new StringBuffer();
char[] chars = str.toCharArray();
for (char c : chars) {
int j = 0;
for (int i = 0; i < c; i ++) {
j ++;
}
sb.append((char)j);
}
return sb.toString();
}
对上面的方法进行分析,我们可以知道该方法除了在入参为空或者为null的情况下,返回空字符串以外,都是返回和原来入参相同的值,但是,返回值并不是直接由入参直接赋值得到的。因此,上面入参和返回值之间,没有直接或者间接的数据依赖(显式依赖关系),但是,如果入参是污染数据,则污点标记也应该通过隐式污点传播传递给返回值。
1.2 污点传播关键概念
在进行污点分析中,工具实现污点分析引擎,需要在引擎上面设置一些关键节点的定义,下面简单介绍和污点分析强相关的部分概念定义:
(1) 污点源(source)
即污点数据的入口。一般来说,外部数据均为不可信数据,外部数据包括但不仅限于:配置文件、数据库、网络、文件、命令行传参等。只要是从外部获取数据的接口,全部可以认为是污点源,例如第1个例子,从 http request 的 header 中获取的数据。
(2) 污点传播点(passthrough)
即污点数据传入到该点之后,污点数据是怎么传播下去的。例如上面 String sql = "{call " + param + "}"; 语句,param 是污点数据,经过一个 字符串拼接 的操作,将 污点信息 传递给了后面整个表达式,然后通过一个 赋值操作,将 污点信息 传递给了 变量sql。
我们在定义这些点的时候,可能需要关注的一些信息:
a) 如果该点是函数操作,则需要关注哪个参数为污点数据时会有问题;如果有污点数据传入,哪些结果会被污染,是返回值,还是其他入参?
b) 如果是基本操作,则需要关心污点数据的类型,并不是所有类型的污点数据在特定操作下,都可以把污点传递下去。
(3) 爆发点(污点坑,sink)
即污点数据到达该点之后,将会存在问题的点,例如第1个例子中的 statement.executeQuery()。在该点执行后,将发生注入或者敏感信息泄露或者其他让咱们觉得不舒服的地方。
(4) 净化点(sanitizer)
如果在该点,错误地认为污点可以向下传递,则可以设置该点为净化点,当污点数据通过该点之后,可以认为该污点数据不再是污点数据。
设置净化点的一个很重要的原因,是很多静态代码分析引擎,在遇到不认识的函数时,对该函数的默认处理是不改变污点信息,这样讲由此导致误报或者漏报。合理设置净化点,可以有效减少误报,而且因为清理掉了污点标记,可以减少数据分析的量,从而提高分析效率。
一般净化点可能有:自定义的数据校验函数、加密库函数、针对注入类问题数据的数据处理函数等。
(5) 污点标记(taint flag)
即该污点数据可能导致的问题的一个标记,例如注入类问题,也有SQL注入、LDAP注入,JSON注入等各种类型,一个污点数据,不见得就会引起所有的问题类型,因此,在部分分析场景里面,可以针对特定的污点,设置污点标记,例如通过某个方法获取的数据,只会引起SQL注入的问题,不会引起敏感信息泄露的问题等。
2. SonarQube中安全规则配置
SonarQube说句实话,能力很差,不过还是能用一下,下面主要梳理一下SonarQube安全配置设置,主要以 Java 为例,支持为下面的规则设置source, sink,passthrough和sanitizer:
- S3649: SQL Injection
- S5131: XSS
- S5146: Open Redirect
- S5167: HTTP Response Splitting
- S2083: Path Traversal Injection
- S2078: LDAP Injection
- S5145: Log Injection
- S2076: OS Command Injection
- S2631: RegExp Injection
- S5144: Server-Side Request Forgery (SSRF)
- S2091: XPath Injection
- S5135: Deserialization Injection
- S5334: Code Injection
- S6096: Zip Slip
2.1 MethodId获取
在 SonarQube中,只支持配置函数类型的相关污点传播节点。传播时,需要设置函数的MethodId,Java语言的MethodId是从字节码中获取的。
我们来尝试获取 System.getenv() 函数的MethodId:
(1) 先写一段程序,包含需要的函数调用
class Test {
public static void main(String[] args) {
System.out.println(System.getenv().get("abc"));
}
}
(2) 先执行 javac,再执行 javap,如下:
$ javac Test.java
$ javap -c Test.class
javap执行的输出如下:
Compiled from "Test.java"
class Test {
Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3 // Method java/lang/System.getenv:()Ljava/util/Map;
6: ldc #4 // String abc
8: invokeinterface #5, 2 // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
13: checkcast #6 // class java/lang/String
16: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
19: return
}
从这个 javap 的输出,可以分成清晰的看到,System.getenv() 注释有:java/lang/System.getenv:()Ljava/util/Map
(3) 然后执行如下操作:
a) 将 package 中的 路径分隔符(/) 替换为英文字符的小数点(.);
b) 移除里面的所有的冒号;
c) 将类名和函数名之间的小数点替换为 #.
最终产生的结果就是:java.lang.System#getenv()Ljava/util/Map
上面介绍了 java 语言的 MethodId 的获取方法,如果要获取 PHP,C#,Python等语言的 MethodId,可以自行参考:https://docs.sonarqube.org/latest/analysis/security_configuration/
2.2 安全配置文件
针对 Java 语言,安全配置的一个例子:
{
"S3649": {
"sources": [
{
"methodId": "my.package.ServerRequest#getQuery()Ljava/lang/String;"
}
],
"sanitizers": [
{
"methodId": "my.package.StringUtils#stringReplace(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
}
],
"passthroughs": [
{
"methodId": "my.package.RawUrl#<init>(Ljava/lang/String;)V",
"isWhitelist": true,
"args": [
1
]
}
],
"sinks": [
{
"methodId": "my.package.MySql#query(Ljava/lang/String;)V",
"args": [
1
]
},
{
"methodId": "my.package.SqlStatement#execute",
"isMethodPrefix": true,
"args": [
0,
1
]
},
{
"methodId": "my.package.SqlStatement#run(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
"interval": {
"fromIndex": 1
}
}
]
},
"S5131": {
"sources": [
{
"methodId": "my.package.ServerRequest#getQueryString()Ljava/lang/String;"
}
],
"sinks": [
{
"methodId": "my.package.Server#write(",
"isMethodPrefix": true,
"interval": {
"fromIndex": 1
}
}
]
}
}
对上面的部分参数进行说明:
(1) methodId:上一节已经有介绍;
(2) args:表示在函数调用时,第几个参数可以接收污点数据,函数调用的下标从1开始,方法调用的下标从0开始,并且下标0表示当前对象(例如java中的 this);
(3) interval:由fromIndex整数组成的对象,其中每个整数表示方法签名中检测受污染值的位置;
(4) isMethodPrefix:如果设置了,则遇到函数会尝试匹配全部能匹配到 prefix 为 MethodId 的方法调用;
(5) isShallow:在自定义规则中不需要使用;
(6) isWhitelist:对污点值是否继续传播跟踪。
完整格式可以参考:https://github.com/SonarSource/sonarqube/blob/master/server/sonar-webserver-webapi/src/main/resources/json-schemas/security.json
2.3 配置到SonarQube服务
可以配置到工程级别或者全局级别,页面导航如下:
- Project level – Project Settings > General Settings > SAST Engine > JAVA/PHP/C#/Python custom configuration
- Global level – Administration > General Settings > SAST Engine > JAVA/PHP/C#/Python custom configuration
3. 参考资料
https://docs.sonarqube.org/latest/analysis/security_configuration/
- 点赞
- 收藏
- 关注作者
评论(0)