内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

usdt otc api接入(www.caibao.it):58团体白盒代码审计系统建设实践2:深入明晰SAST

2021-04-20 09:55 出处:  人气:   评论( 0

USDT交易所

U交所(www.payusdt.vip)是使用TRC-20协议的Usdt官方交易所,开放USDT帐号注册、usdt小额交易、usdt线下现金交易、usdt实名不实名交易、usdt场外担保交易的平台。免费提供场外usdt承兑、低价usdt渠道、Usdt提币免手续费、Usdt交易免手续费。U交所开放usdt otc API接口、支付回调等接口。

靠山

源代码平安检测是平安开发流程(SDL)中异常主要的一部门,在58团体的CI/CD流程中天天有数千次量级的构建及公布,白盒检测的自动化能力显得极为主要。企业级的白盒代码审计系统就不仅仅面临破绽发现的需求,也需要顺应企业CI/CD流程。由于58团体大部门营业使用自研的Java框架,本系列文章会重点先容我们在Java白盒能力建设历程中的实践。

本文是58白盒扫描建设之路系列文章第二篇,主要先容SAST的一些手艺原理及应用、CodeQL的官方教程中文翻译及实践用法

手艺原理

明晰AST抽象语法树

在盘算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象示意。它以树状的形式显示编程语言的语法结构,树上的每个节点都示意源代码中的一种结构。之以是说语法是“抽象”的,是由于这里的语法并不会示意出真实语法中泛起的每个细节。好比,嵌套括号被隐含在树的结构中,并没有以节点的形式出现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有三个分支的节点来示意。

一样平常的,在源代码的翻译和编译历程中,语法剖析器确立出剖析树,然后从剖析树天生AST。一旦AST被确立出来,在后续的处置历程中,好比语义剖析阶段,会添加一些信息。

抽象语法树是程序源代码结构的树状示意。程序源代码经由词法剖析器(Lexer)获得种种差异种类的单词(Token),再由语法剖析器(Parser)剖析和语法检查后获得抽象语法树(AST)。抽象语法树的根节点示意整个程序,内部节点是抽象语法结构或者单词。AST的焦点在于它能与输入源代码中的各个语法元素逐一对应

如 以下的C语言代码如图所示

while (i<n){
sum   = A[i  ];
}

Java AST抽象语法树

Spoon

Spoon是一个开放源代码库,用于剖析,重写,转换,翻译Java源代码。它剖析源文件以构建具有壮大剖析和转换API的全心设计的AST。它完全支持Java 11、12、13、14之前的现代Java版本。Spoon是Inria的一个官方开源项目,而且是OW2开源同盟的成员。

git地址:https://github.com/INRIA/spoon

JAVA项目举行AST剖析

可以通过自行编译Spoon源代码或者去https://search.maven.org/artifact/fr.inria.gforge.spoon/spoon-core maven客栈下载已经编译好的Spoon的jar包

通过以下下令举行GUI的语法树剖析

java -cp /Users/fangzhao/IDEA/spoon/target/spoon-core-8.4.0-SNAPSHOT-jar-with-dependencies.jar spoon.Launcher -i /Users/fangzhao/IDEA/springboot-mybatis/src/main/java/cn/no7player/controller/HelloController.java  --gui

对 Spring demo的 hello.java举行AST剖析

package cn.no7player.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "hello";
    }

}

剖析效果

编程语言可以具有差其余元模子。抽象语法树(AST)或模子是元模子的实例。每个元模子(因此每个AST)或多或少都取决于手头的义务。例如,已针对Sun编译器(javac)的Java元模子举行了设计和优化,以将其编译为字节码,而Eclipse IDE(JDT)的Java元模子的主要目的是在一个软件中支持软件开发的差异义务。集成方式(代码完成,编译错误的快速修复,调试等)。

与基于编译器的AST(例如来自javac)差异,Java的Spoon元模子被设计为通俗Java开发职员易于明晰,因此他们可以编写自己的程序剖析和转换。 Spoon元模子是完整的,由于它包罗派生可编译和可执行Java程序所需的所有信息(因此包罗批注,泛型和方式体)。

Spoon元模子可以分为三个部门。

结构部门包罗程序元素的声明,例如接口,类,变量,方式,注释和枚举声明。

代码部门包罗可执行的Java代码,例如在方式主体中找到的代码。

参考部门对对程序元素的引用(例如,对类型的引用)举行建模。

如图所示,所有元素都继续自CtElement(javadoc),后者声明一个父元素,该父元素示意源文件中的包罗关系。例如,方式节点的父级是类节点。所著名称均以“ CT”为前缀,示意“编译时”。

从Spoon 6.1.0最先,Spoon元模子包罗CtModule元素示意Java 9中的模块,以及CtModuleDirective示意模块的差异指令。

提醒:模子的根不再是未命名的包,而是未命名的模块。

SAST静态扫描基本原理

SAST原理

  • 通过挪用语言的编译器或者注释器把前端的语言代码(如JAVA,C/C 源代码)转换成中央代码(IR,intermediaterepresentation),将其源代码之间的挪用关系、执行环境、上下文等剖析清晰。
  • 语义剖析:剖析程序中不平安的函数,方式的使用的平安问题
  • 数据流剖析:跟踪,纪录并剖析程序中的数据通报历程所发生的平安问题。
  • 控制流剖析:剖析程序特准时间,状态下执行操作指令的平安问题。
  • 设置剖析:剖析项目设置文件中的敏感信息和设置缺失的平安问题。
  • 结构剖析:剖析程序上下文环境,结构中的平安问题。
  • 连系2~6步的效果,匹配所有规则库中的破绽特征,一旦发现破绽就抓取出来。
  • 最后形成包罗详细破绽信息的破绽检测讲述,包罗破绽的详细代码行数以及破绽修复的建议。

简朴明晰污点剖析手艺

当我们通过AST手艺拿到了源码的抽象语法树,并将其数据名堂化存储之后,需要一套高效的算法对破绽模子举行匹配,在破绽模子的确立上我们需要引入污点剖析手艺来对破绽举行界说

污点剖析界说

污点剖析可以抽象成一个三元组<sources,sinks,sanitizers>的形式,其中,source 即污点源,代表直接引入不受信托的数据或者隐秘数据到系统中;sink即污点汇聚点,代表直接发生平安敏感操作(违反数据完整性)或者泄露隐私数据到外界(违反数据保密性);sanitizer即无害处置,代表通过数据加密或者移除危害操作等手段使数据流传不再对软件系统的信息平安发生危害.污点剖析就是剖析程序中由污点源引入的数据是否能够不经无害处置,而直接流传到污点汇聚点.若是不能,说明系统是信息流平安的;否则,说明系统发生了隐私数据泄露或危险数据操作等平安问题.

简朴的说:污点剖析是默认不信托内陆/外部输入,将内陆及外部输入的控制/数据流历程举行剖析,若是没有经由无害化处置,即以为存在破绽的破绽模子

污点剖析的处置历程

污点剖析的处置历程可以分为三个阶段

  • 识别污点源和汇聚点;
  • 污点流传剖析;
  • 无害处置.

识别污点源

识别污点源和污点汇聚点是污点剖析的条件.现在,在差其余应用程序中识别污点源和汇聚点的方式各不

相同.缺乏通用方式的缘故原由一方面来自系统模子、编程语言之间的差异.另一方面,污点剖析关注的平安破绽类

型差异,也会导致对污点源和污点汇聚点的网络方式迥异.表 1 所示为在 Web 应用程序破绽检测中的污点源示

例[29],它们是 Web 框架中要害工具的属性.

现有的识别污点源和汇聚点的方式可以大致分成 3 类:

  • 使用启发式的计谋举行符号,例如把来自程序外部输入的数据统称为“污点”数据,守旧地以为这些数据有可能包罗恶意的攻击数据(如 PHP Aspis);
  • 凭证详细应用程序挪用的 API 或者主要的数据类型,手工符号源和汇聚点(如 DroidSafe);
  • 使用统计或机械学习手艺自动地识别和符号污点源及汇聚点.

污点流传剖析

污点流传剖析就是剖析污点符号数据在程序中的流传途径.根据剖析历程中关注的程序依赖关系的差异, 可以将污点流传剖析分为显式流剖析和隐式流剖析.

显示流剖析

污点流传剖析中的显式流剖析就是剖析污点符号若何随程序中变量之间的数据依赖关系流传

以图 3 所 示的程序为例,变量 a 和 b 被预界说的污点源函数 source 符号为污点源.假设 a 和 b 被赋予的污点符号划分为taint_a 和 taint_b.由于第 5 行的变量 x 直接数据依赖于变量 a,第 6 行的变量 y 直接数据依赖于变量 b,显式流剖析会划分将污点符号 taint_a 和 taint_b 流传给第 5 行的变量 x 和第 6 行的变量 y.又由于 x 和 y 划分可以到达第 7 行和第 8 行的污点汇聚点(用预界说的污点汇聚点函数 sink 标识),图 3 所示的代码存在信息泄露的问题.我们将在后面详细先容现在污点流传剖析中显式流剖析面临的主要挑战息争决方式.

隐式流剖析

污点流传剖析中的隐式流剖析是剖析污点符号若何随程序中变量之间的控制依赖关系流传,也就是剖析污点符号若何从条件指令流传到其所控制的语句.

在图 4 所示的程序中,变量 X 是被污点符号的字符串类型变量,变量 Y 和变量 X 之间并没有直接或间接的数据依赖关系(显式流关系),但 X 上的污点符号可以经由控制依赖隐式地流传到 Y.

详细来说,由第 4 行的循环条件控制的外层循环顺序地取出 X 中的每一个字符,转化成整型后赋给变量 x,再由第 7 行的循环条件控制的内层循环以累加的方式将 x 的值赋给 y,最后由外层循环将 y 逐一传给 Y.最终,第 12 行的 Y 值和 X 值相同,程序存在信息泄露问题.然则,若是不举行隐式流污点流传剖析,第 12 行 的变量 Y 将不会被赋予污点符号,程序的信息泄露问题被掩饰.

隐式流污点流传一直以来都是一个主要的问题,和显式流一样,若是不被准确处置,会使污点剖析的效果不正确.由于对隐式流污点流传处置欠妥导致本应被符号的变量没有被符号的问题称为欠污染(under-taint)问题.相反地,由于污点符号的数目过多而导致污点变量大量扩散的问题称为过污染(over-taint)问题.现在,针对隐式流问题的研究重点是只管削减欠污染和过污染的情形.我们将在后面详细先容现有手艺是若何解决上述问题的.

无害处置

污点数据在流传的历程中可能会经由无害处置模块,无害处置模块是指污点数据经由该模块的处置后,数据自己不再携带敏感信息或者针对该数据的操作不会再对系统发生危害.换言之,带污点符号的数据在经由无害处置模块后,污点符号可以被移除.准确地使用无害处置可以降低系统中污点符号的数目,提高污点剖析的效率,而且制止由于污点扩散导致的剖析效果不正确的问题.

,

USDT场外交易平台

U交所(www.payusdt.vip),全球頂尖的USDT場外擔保交易平臺。

,

在应用历程中,为了防止敏感数据被泄露(珍爱保密性),通常会对敏感数据举行加密处置.此时,加密库函数应该被识别成无害处置模块.这一方面是由于库函数中使用了大量的加密算法,导致攻击者很难有用地盘算出密码的可能局限;另一方面是加密后的数据不再具有威胁性,继续流传污点符号没有意义.

此外,为了防止外界数据由于携带危险操作而对系统要害区域发生危害(珍爱完整性),通常会对输入的数据举行验证.此时,输入验证(input validation)模块应当被识别成无害处置模块.

例如,为了防止代码注入破绽,PHP 提供的 htmlentities 函数可以将特殊寄义的 HTML 字符串转化成HTML实体(例如,将’<’转化成’<’).输入字符串经由上述转化后不会再携带可能发生危害的代码,可以平安地 发送给用户使用.除了语言自带的输入验证函数外,一些系统还提供了分外的输入验证工具,好比ScriptGard,CSAS,XSS Auditor,Bek.这些工具也应被识别成无害处置模块.

综上,现在对污点源、污点汇聚点以及无害处置模块的识别通常凭证系统或破绽类型使用定制的方式.由于这些方式都对照直接,本文将不再举行更深入的探讨.下一节将重点先容污点流传中的要害手艺.

tips:

更多的污点剖析手艺可参考以下链接及其参考链接:https://www.k0rz3n.com/2019/03/01/简单理解污点分析技术/

明晰CodeQL破绽剖析历程

整体流程

  • 通过适配各个语言的AST剖析器,并将代码的AST剖析效果根据预设好的数据模子将代码AST数据及其依赖关系存储到CodeDB里
  • 通过QL语言界说污点追踪破绽模子
  • 执行QL时通过高效的搜索算法对CodeDB的AST元数据举行高效查询,从而在代码中搜索露马脚效果

QL中的污点剖析模子

我们以下令执行的QL作为例子来看在Codeql Rules里的污点剖析是若何使用的

首先,Codeql也是使用<sources,sinks,sanitizers>三元组对污点剖析历程举行三个阶段的界说

文件目录:/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql

/**
 * @name Uncontrolled command line
 * @description Using externally controlled strings in a command line is vulnerable to malicious
 *              changes in the strings.
 * @kind path-problem
 * @problem.severity error
 * @precision high
 * @id java/command-line-injection
 * @tags security
 *       external/cwe/cwe-078
 *       external/cwe/cwe-088
 */

import java /** 导入codeql的java依赖 **/
import semmle.code.java.dataflow.FlowSources /** 导入java的Sources界说模块 **/
import semmle.code.java.security.ExternalProcess /** 导入界说java中执行系统下令模块 **/
import ExecCommon  /** 导入对java中下令执行的 sources、sink、sanitizer界说模块 **/
import DataFlow::PathGraph /** 导入java的数据流控制模块 **/

/** 从DataFlow里导入 source、 sink、而且界说下令执行的参数为execArg **/    
from DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg
/** source、 sink、execArg 知足 execTainted的参数界说 **/
where execTainted(source, sink, execArg)
/** 搜索知足execTainted谓词界说的参数、污染源及sink点并输出(可以明晰成取交集的历程) **/    
select execArg, source, sink, "$@ flows to here and is used in a command.", source.getNode(),
  "User-provided value"

我们再来看一下 ExecCommon 里是若何界说知足条件的 source、sink 及 sanitizer

文件目录:/java/ql/src/Security/CWE/CWE-078/ExecCommon.qll

/** 导入种种所依赖的设置 **/
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.ExternalProcess
import semmle.code.java.security.CommandArguments

/** 界说一个私有的Class作为远程下令执行的dataflow Config设置,而且这个Config设置继续自基础的Configuration **/
private class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration {
  RemoteUserInputToArgumentToExecFlowConfig() {
  /** 界说该Config的别名为ExecCommon **/
    this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig"
  }
  /** 重写对Source的界说,知足Source是远程数据输入,instanceof语句为知足后续谓词条件,依旧是取交集操作 **/
  override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }

  /** 重写对Sink的界说,sink.asExpr为sink的表达式需要知足于ArgumentToExec的谓词界说 **/
  override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }

  /** 重写对Sanitizer的界说,节点的数据类型知足PrimitiveType,BoxedType 或者是平安的下令挪用方式,便以为是经由了净化**/ 
  override predicate isSanitizer(DataFlow::Node node) {
    node.getType() instanceof PrimitiveType
    or
    node.getType() instanceof BoxedType
    or
    isSafeCommandArgument(node.asExpr())
  }
}

/**
 * Implementation of `ExecTainted.ql`. It is extracted to a QLL
 * so that it can be excluded from `ExecUnescaped.ql` to avoid
 * reporting overlapping results.
 */

/** 界说谓词 execTainted知足于RemoteUserInputToArgumentToExecFlowConfig条件,而且存在Source到Sink点的数据流 **/

predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg) {
  exists(RemoteUserInputToArgumentToExecFlowConfig conf |
    conf.hasFlowPath(source, sink) and sink.getNode() = DataFlow::exprNode(execArg)
  )
}

我们可以看到CodeQL 使用结构化的查询,通太过别界说三元组<sources,sinks,sanitizers>,对知足其界说的所有AST元数据举行交集,并判断source到sink之间是否存在可达路径,若是存在可达路径即判断存在相关破绽

在现实应用场景里的QL使用案例

若何通过QL获取Spring项目的 Web Path

规则剖析

新建规则路径:java/ql/src/Security/CUSTOM/query/spring/SpringPath.ql

/**
 * @name Spring controller bind path 
 * @description list all Spring controller path in method bind and class bind.
 * @kind path-list
 * @problem.severity information
 * @precision Null
 * @id java/Spring-path
 * @tags Information-path
 */


import java
import semmle.code.java.frameworks.spring.SpringCustomController
import semmle.code.java.dataflow.FlowSources


from SpringWebApiBindMethod m
select 
m as controllerMethod, m.getLocation() as location,
m.getBindPath() as methodBindPath,
m.getControllerClassBindPath() as classBindPath, "Spring bindPath"

扫描效果,classBindPath methodBindPath为Spring的Web path

Spring Web Path的模块 :java/ql/src/semmle/code/java/frameworks/spring/SpringCustomController.qll

import java
import semmle.code.java.Maps
import SpringController


/**
 * Sping框架web请求绑定方式
 */
class SpringWebApiBindMethod extends SpringMvcControllerMethod {
    SpringWebApiBindMethod() {
        getAnAnnotation() instanceof SpringRequsetMappingCustomAnnotation
        or 
        getAnAnnotation() instanceof SpringRestMappingCustomAnnotation
    }

    /**
     * 获取方式绑定的路径RequsteMapping
     */
    Expr getBindPath(){
        result = getAnAnnotation().(SpringRequsetMappingCustomAnnotation).getValue("value")
        or 
        result = getAnAnnotation().(SpringRestMappingCustomAnnotation).getValue("value")

    }

    /**
     * 获取mvc控制器类绑定的路径,不存在返回空字符串
     */
    string getControllerClassBindPath(){
        if this.isSpringMvcControllerBindPath() then 
        result = this.getDeclaringType().getAnAnnotation().(SpringRequsetMappingCustomAnnotation).getValue("value").toString()
        else result = ""
    }


    /**
     * 该方式的mvc控制器是否绑定了路径前缀
     */
    predicate isSpringMvcControllerBindPath() {
        exists(SpringRequsetMappingCustomAnnotation a| this.getDeclaringType().getAnAnnotation() = a 
        and this.getDeclaringType().getAnAnnotation().(SpringRequsetMappingCustomAnnotation).getValue("value").toString().length()>0  )
    }
}

    /**
     * 该方式的声明类型的直接类型或者间接类型知足Spring Controller的谓词界说
     */

class SpringMvcControllerMethod extends Method {
    SpringMvcControllerMethod() {
        getDeclaringType().getAnAncestor() instanceof SpringController
    }
}



    /** 
     * 该方式的声明类型名称包罗Spring Request Mapping的注解
    */
class SpringRequsetMappingCustomAnnotation extends Annotation {
    SpringRequsetMappingCustomAnnotation() {

        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")
        or
        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "RestController")

    }
}


    /** 
     * 该方式的声明类型名称包罗Spring Rest Mapping的注解
    */
class SpringRestMappingCustomAnnotation extends Annotation {
    SpringRestMappingCustomAnnotation() {

        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
        or
        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "PostMapping")
        or
        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "PutMapping")
        or
        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "DeleteMapping")
        or
        getType().getAnAncestor().hasQualifiedName("org.springframework.web.bind.annotation", "PatchMapping")

    }
}

通过对Spring框架对Web Controller的使用方式及注解的使用方式,界说了SpringWebApiBindMethod类,并通过getControllerClassBindPath()、getBindPath() 获取方式绑定的路径RequsteMapping及该方式的mvc控制器是否绑定了路径前缀

再新建一个FlowSourceCustom.qll

/**
 * 输入源和数据流界说
 */

import java

import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.spring.SpringComponentScan
import semmle.code.java.frameworks.spring.SpringCustomController


/**
 * Spring框架Servlet Input参数输入源
 */

class SpringCustomServletInputParameterSource extends RemoteFlowSource {
    SpringCustomServletInputParameterSource() {
      this.asParameter() = any(SpringRequestMappingParameter srmp | srmp.isTaintedInput())
    }
    override string getSourceType() { result = "Spring servlet input parameter" }
  }

/**
 * Spring框架Multipart FileS参数输入源
 */

class SpringCustomMultipartFileSource extends RemoteFlowSource {
    SpringCustomMultipartFileSource() {
      exists(MethodAccess ma, Method m |
        ma = this.asExpr() and
        m = ma.getMethod() and
        m.getDeclaringType()
            .getASourceSupertype*()
            .hasQualifiedName("org.springframework.web.multipart", "MultipartFile") and
        m.getName().matches("get%")
      )
    }
    override string getSourceType() { result = "Spring MultipartFile getter" }
  }


  /**
 * Spring框架Multipart request参数输入源
 */
class SpringCustomMultipartRequestSource extends RemoteFlowSource {
    SpringCustomMultipartRequestSource() {
      exists(MethodAccess ma, Method m |
        ma = this.asExpr() and
        m = ma.getMethod() and
        m.getDeclaringType()
            .getASourceSupertype*()
            .hasQualifiedName("org.springframework.web.multipart", "MultipartRequest") and
        m.getName().matches("get%")
      )
    }

    override string getSourceType() { result = "Spring MultipartRequest getter" }
  }

最后将FlowSourceCustom.qll import至 /java/ql/src/semmle/code/java/dataflow/FlowSources.qll就可以使用SpringPath.ql举行Spring的path查询

我们来看一个现实的例子fastjson.java文件:

package org.joychou.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/fastjson")
public class Fastjson {

    @RequestMapping(value = "/deserialize", method = {RequestMethod.POST})
    @ResponseBody
    public String Deserialize(@RequestBody String params) {
        // 若是Content-Type不设置application/json名堂,post数据会被url编码
        try {
            // 将post提交的string转换为json
            JSONObject ob = JSON.parseObject(params);
            return ob.get("name").toString();
        } catch (Exception e) {
            return e.toString();
        }
    }
controllerMethod controllerMethod.getLocation methodBindPath classBindPath Type
Deserialize Fastjson:19[19-29] "/deserialize" "/fastjson" Spring bindPath

QL对该文件扫描效果如上表

我们可以看到 /fastjson/deserialize 即是该方式的Web Path

连系SCA判断fastjson是否可行使

Fastjson不平安使用

Fastjson不平安的使用需要知足以下三个条件:

1、项目中导入了不平安的Fastjson版本

2、项目中使用了Fastsjon不平安的反序列化方式去反序列化外部传入的Json数据

3、项目中存在行使Fastjson反序列化行使方式的反序列化挪用链

1、3 通过SCA的能力去解决,2可以通过QL剖析源代码拿到FastJson在项目中的使用情形,下面我们来剖析一下若何使用QL查询Fastjson是否在在代码中被平安使用

java-sec-code里的fastjson反序列化demo

package org.joychou.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/fastjson")
public class Fastjson {

    @RequestMapping(value = "/deserialize", method = {RequestMethod.POST})
    @ResponseBody
    public String Deserialize(@RequestBody String params) {
        // 若是Content-Type不设置application/json名堂,post数据会被url编码
        try {
            // 将post提交的string转换为json
            JSONObject ob = JSON.parseObject(params);
            return ob.get("name").toString();
        } catch (Exception e) {
            return e.toString();
        }
    }

我们在来看看QL里的Fastjson查询

/**
 * @name FastJson deserializing of user-controlled data
 * @description FastJson deserializing user-controlled data may allow attackers to
 *              execute arbitrary code.
 * @kind path-problem
 * @problem.severity error
 * @precision high
 * @id java/unsafe-fastjson-deserialization
 * @tags security
 *       external/cwe/cwe-502/Fastjson deserialization
 */

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.FastJson
import DataFlow::PathGraph

class UnsafeFastJsonSinkConfig extends TaintTracking::Configuration {
  UnsafeFastJsonSinkConfig() { this = "UnsafeFastJsonConfig" }

  override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

  override predicate isSink(DataFlow::Node sink) { sink instanceof UnSafeFastJsonSink }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeFastJsonSinkConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(UnSafeFastJsonSink).getMethodAccess(), source, sink,
  "Unsafe fastjson deserialization of $@.", source.getNode(), "user input"

首先我们界说了一个继续Configuration的 UnsafeFastJsonSinkConfig类,其需要知足于 dataFlow里的source知足RemoteFlowSource(远程用户输入)的谓词界说、sink点需要知足UnSafeFastJsonSink(不平安的Fastjson使用)的谓词界说

通过搜索where conf.hasFlowPath(source, sink) 知足UnsafeFastJsonSinkConfig条件的sources和sink而且source和sink之间是可达的,那我们就以为该处存在fastjson的不平安使用

我们再来看看Fastjson.qll是若何界说UnSafeFastJsonSink

import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.frameworks.FastJson

predicate unsafeFastjson(MethodAccess ma, Expr sink) {
  exists(Method m | m = ma.getMethod() |
    ma.getMethod() instanceof FastJsonParseMethod and
    not fastJsonLooksSafe() and
    sink = ma.getArgument(0)
  )
}

class UnSafeFastJsonSink extends DataFlow::ExprNode {
    UnSafeFastJsonSink() { unsafeFastjson(_, this.getExpr()) }
  MethodAccess getMethodAccess() { unsafeFastjson(result, this.getExpr()) }
}

谓词unsafeFastjson需要知足于存在fastjson的挪用方式而且未设置safety设置的方式

我们再来看看 FastJsonParseMethod、和fastJsonLooksSafe是若何编写的

/**
 * The class `com.alibaba.fastjson.JSON`.
 */
class FastJson extends RefType {
  FastJson() { this.hasQualifiedName("com.alibaba.fastjson", "JSON") }
}

/**
 * A FastJson parse method. This is either `JSON.parse` or `JSON.parseObject`.
 */
class FastJsonParseMethod extends Method {
  FastJsonParseMethod() {
    this.getDeclaringType() instanceof FastJson and
    this.hasName(["parse", "parseObject"])
  }
}

/**
 * A call to `ParserConfig.setSafeMode`.
 */
class FastJsonSetSafeMode extends MethodAccess {
  FastJsonSetSafeMode() {
    exists(Method m |
      this.getMethod() = m and
      m.hasName("setSafeMode") and
      m.getDeclaringType().hasQualifiedName("com.alibaba.fastjson.parser", "ParserConfig")
    )
  }

可以看到我们将JSON.parse和JSON.parseObject界说为危险函数

而使用setSafeMode设置的,我们以为是平安的

通过查询我们可以乐成搜索出代码里的不平安的fastjson使用方式

连系SCA我们可以完成以上三个条件对fastjson平安风险的发现

总结

在明晰了SAST的一些手艺原理以及CodeQL一些现实使用的案例,本次分享附件包罗Codeql的官方教程基础语法以及Java模块的中文教程,可自行向运营小姐姐获取。后面我们将分享58SAST在工程化选型以及设计的一些履历。

资源

关注58平安应急响应中央民众号,后台发送“58白盒系列2”,获取Codeql教程下载链接

分享给小伙伴们:
本文标签: 安全技术企业安全

相关文章

Copyright © 2002-2019 松原新闻 版权所有 Power by DedeMao