action请求_同盾技术 | OpenRASP Java源码分析与总结(二)Web请求、基于语法分析的SQL和安全基线检测…

  • Post author:
  • Post category:java


3d74a56b190d1d44b22a4d2ab8b7081e.gif


引子

通过《OpenRASP Java源码分析与总结(一)——启动与检测》的分析,我们对OpenRASP Java部分的整体启动和检测流程有了大致的了解。本文将对OpenRASP所支持的js插件、安全基线等几类检测方式中,选取几个比较有代表性的检测进行详细分析。


准备



1

  • agent Java代码包结构

/+ |-boot // rasp.jar代码所在目录 | +-engine+ // rasp-engine.jar代码所在目录         +src+             +main+                  +java+                       +com.baidu.openrasp+                                          |-config          // 配置代码                                          |-hook+           // hoot代码,主要分析这个目录下的代码                                          |     |-file      // 文件hook                                          |     |-server    // 服务器hook                                          |     |-sql       // sql hook                                          |     +-ssrf      // ssrf hook                                          |                                          |-plugin+                 // 插件代码                                          |       |-antlr           // sql语法分析代码                                          |       |-checker+        // checker代码                                          |       |        |-js     // js checker代码                                          |       |        |-local                                          |       |        +-policy // 安全基线checker代码                                          |       +-js.engine       // js引擎代码                                          |                                          |-tool                                          +-transformer
  • 《OpenRASP Java源码分析与总结(一)——启动与检测》中介绍过检测的基本流程,入口为hook,hook再委托给checker。因此,下文的分析也将按照此流程进行:先分析入口hook,再分析对应的checker。

  • 分析过程中涉及一些常规Web服务器、JDBC规范、SQL语法规则,本文不做详细分析,请自行学习了解。

  • 分析过程中涉及到的配置和代码,均为精简过,去掉了和分析无关的内容,所以和真正源码并非一一对应,但对了解整个过程已经足够了。


源码分析


2


Web请求检测

2.1

Tomcat是Java生态圈里最常见和常用的Web容器,它实现了Servlet规范(版本不同实现的规范版本也不同),所有的Web请求均由Tomcat接收解析后,调用用户程序进行处理。因此对Web请求检测可以从Tomcat相关的Hook进行入手,ApplicationFilterHook就是其中一个:

代码2-1

@HookAnnotationpublic class ApplicationFilterHook extends ServerRequestHook {    // 1    @Override    public boolean isClassMatched(String className) {        return className.endsWith("apache/catalina/core/ApplicationFilterChain");    }    @Override    protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {        // 2        String src = getInvokeStaticSrc(ServerRequestHook.class, "checkRequest",                "$0,$1,$2", Object.class, Object.class, Object.class);        // 3                insertBefore(ctClass, "doFilter", null, src);    }}public abstract class ServerRequestHook extends AbstractClassHook {    // 4    public static void checkRequest(Object filter, Object request, Object response) {        HookHandler.checkRequest(filter, request, response);    }}
  1. 判断入参类是否需要被hoook。这里检查的是Tomcat对Servlet规范中FilterChain的实现类ApplicationFilterChain。通过Tomcat部署的Web应用的所有HTTP请求均会经过ApplicationFilterChain,因此以此类作为hook入口非常适合;

  2. 获取静态方法HookHandler.checkRequest()的源码;

  3. 将2中的代码插入到实例方法ApplicationFilterChain.doFilter()的开始处。ApplicationFilterChain.doFilter()为HTTP请求的入口方法,在这里可以对所有的HTTP请求进行检测;

  4. 调用HookHandler.checkRequest()进行请求检测。

代码2-2

public class HookHandler {    public static void checkRequest(Object servlet, Object request, Object response) {        // 1        HttpServletRequest requestContainer = new HttpServletRequest(request);        // 2        HttpServletResponse responseContainer = new HttpServletResponse(response);        // 3        responseContainer.setHeader(OPEN_RASP_HEADER_KEY, OPEN_RASP_HEADER_VALUE);        // 4        responseContainer.setHeader(REQUEST_ID_HEADER_KEY, requestContainer.getRequestId());        // 5        requestCache.set(requestContainer);        // 6        responseCache.set(responseContainer);        // 7        doCheck(CheckParameter.Type.REQUEST, JSContext.getUndefinedValue());    }}
  1. 包装原有的request对象为自定义的request;

  2. 包装原有的response对象为自定义的response;

  3. 增加请求头X-Protected-By,值为OpenRASP;

  4. 增加请求头X-Request-ID,值为一个UUID;

  5. 保存自定义的request到当前线程上下文中;

  6. 保存自定义的response到当前线程上下文中;

  7. 检测请求。

根据7中的第一个入参,查找CheckParameter.Type:

public class CheckParameter {    public enum Type {        ...,        REQUEST("request", new JsChecker()),        ...;}

可以得知,CheckParameter.Type.REQUEST对应的Checker实现为JsChecker。因此,最终检测逻辑即委托给了JsChecker。在《OpenRASP Java源码分析与总结(一)——启动与检测》中我们曾分析过,JsChecker的检测逻辑实际又委托给了plugins目录下的js实现。

OpenRASP官方仅提供了几个请求检测的Demo插件作为演示使用,如果要投入到实际生产环境,还需要做进一步的开发。下面,我们分别看下官方提供的两个Demo插件,了解下请求检测的大致逻辑,分别是plugins/addons/001-xss-demo.js和plugins/addons/002-detect-scanner.js:

代码2-3

var plugin = new RASP('offical')// 1plugin.register('request', function(params, context) {    // 2    function detectXSS(params, context) {        // 3        var xssRegex   = /||javascript:(?!(?:history\.(?:go|back)|void\(0\)))/i        var parameters = context.parameter;        var message    = '';        // 4        Object.keys(parameters).some(function (name) {            parameters[name].some(function (value) {                if (xssRegex.test(value)) {                    message = 'XSS 攻击: ' + value;                    return true;                }            });        });        return message    }    // 5    var message = detectXSS(params, context)    if (message.length) {        return {action: 'block', message: message, confidence: 90}    }    return clean    })
  1. 注册一个request检测函数;

  2. 定义一个XSS检测函数;

  3. 定义XSS检测的正则表达式;

  4. 循环请求里的每个参数,判断是否匹配XSS正则表达式;

  5. 调用XSS检测函数,如果命中,则返回命中信息。

代码2-4

var plugin = new RASP('offical')plugin.register('request', function(params, context) {  var foundScanner = false  // 1  var scannerUA    = [..., "bsqlbf", "sqlmap", "nessus", "arachni", "metis", ...]  var headers      = context.header  // 2  var ua = headers['user-agent']  if (ua) {    // 3    for (var i = 0; i < scannerUA.length; i++) {      if (ua.indexOf(scannerUA[i].toLowerCase()) != -1) {        foundScanner = true        break      }    }  }   // 4  if (foundScanner) {    return {action: 'block', message: '已知的扫描器探测行为,UA 特征为: ' + scannerUA[i], confidence: 90}  }  return clean})
  1. 定义扫描器可能产生的特定浏览器User-Agent(以下简称为UA)请求头;

  2. 从请求中获取UA请求头;

  3. 循环1中定义的扫描器UA,判断是否和2中的UA进行比较;

  4. 如果命中扫描器UA,则返回命中信息。

通过以上Demo的分析,可以得知,如果要自行编写Web请求检测的插件,需要注册一个request检测函数,通过检测函数的第二个入参context(其实就是OpenRASP自定义的HttpServletRequest对象)获取请求头、请求参数等信息,对请求信息进行检测即可。通过自行编写的请求检测插件,可以完成诸如XSS、扫描器、异常请求头等检测。


基于语法分析的SQL检测

2.2

Java通过JDBC来访问数据库,而JDBC只是一套规范和接口,不同的数据库厂商会根据JDBC规范实现自己的访问逻辑。因此,RASP SQL检测其实就是在不同数据库的JDBC实现(主要是Statement、PrepareStatement接口的实现)中注入对SQL的检测逻辑。

下面我们就来分析下OpenRASP是如何在Statement中完成SQL的检测(PrepareStatement同理),分析的入口类为SQLStatementHook:

代码2-5

@HookAnnotationpublic class SQLStatementHook extends AbstractSqlHook {  // 1    public static LRUCache sqlCache = new LRUCache();    @Override    public boolean isClassMatched(String className) {      // 2        if ("com/mysql/jdbc/StatementImpl".equals(className)                || "com/mysql/cj/jdbc/StatementImpl".equals(className)) {            this.type = "mysql";            this.exceptions = new String[]{"java/sql/SQLException"};            return true;        }        ...        return false;    }    @Override    protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {      // 3    String checkSqlSrc = getInvokeStaticSrc(SQLStatementHook.class, "checkSQL",                "\"" + type + "\"" + ",$0,$1", String.class, Object.class, String.class);        // 4                insertBefore(ctClass, "execute", checkSqlSrc,                new String[]{"(Ljava/lang/String;)Z", "(Ljava/lang/String;I)Z",                        "(Ljava/lang/String;[I)Z", "(Ljava/lang/String;[Ljava/lang/String;)Z"});        ...    }    public static void checkSQL(String server, Object statement, String stmt) {      // 5        if (!sqlCache.isContainsKey(stmt)) {            JSContext cx = JSContextFactory.enterAndInitContext();            Scriptable params = cx.newObject(cx.getScope());            params.put("server", params, server);            params.put("query", params, stmt);            HookHandler.doCheck(CheckParameter.Type.SQL, params);        }    }}
  1. 定义一个SQL缓存,用于存放已经检测过可放行的SQL;

  2. 检查入参的类是否需要被hook。这里检查的是各个数据库的JDBC驱动对Statement接口的具体实现类,比如MySQL的com.mysql.jdbc.StatementImpl;

  3. 获取静态方法SQLStatementHook.checkSQL()的源码;

  4. 将4中的代码插入到实例方法StatementImpl.execute()的开始处。StatementImpl.execute()总共有4个同名方法,因此insertBefore()的第四个入参提供了对应的4个不同的方法签名;

  5. 判断是否命中SQL缓存,命中则说明该SQL可放行,未命中则调用HookHandler.doCheck()。除了execute()之外,executeUpdate()、executeQuery()和addBatch()也被注入了SQLStatementHook.checkSQL()。

通过上述代码,实现了类似如下的逻辑(以MySQL的StatementImpl.execute(String sql)为例):

public StatementImpl implements Statement {    public boolean execute(String sql) {        // 插入的SQLStatementHook.checkSQL(this, sql),展开后的代码        if (!sqlCache.isContainsKey(sql)) {            JSContext cx = JSContextFactory.enterAndInitContext();            Scriptable params = cx.newObject(cx.getScope());            params.put("server", params, server);            params.put("query", params, sql);            HookHandler.doCheck(CheckParameter.Type.SQL, params);        }        // 原有逻辑        ...    }}

根据5中的第一个入参,查找CheckParameter.Type:

public class CheckParameter {    public enum Type {        ...,        SQL("sql", new SqlStatementChecker()),        ...;}

可以得知,CheckParameter.Type.REQUEST对应的Checker实现为SqlStatementChecker。因此,最终检测逻辑即委托给了SqlStatementChecker,下面进行分析:

代码2-6

public class SqlStatementChecker extends ConfigurableChecker {    public List checkSql(CheckParameter checkParameter, Map parameterMap, JsonObject config) {        List result = new LinkedList();        String query = (String) checkParameter.getParam("query");        String message = null;        // 1        String[] tokens = TokenGenerator.detailTokenize(query, new TokenizeErrorListener());        // 2        for (Map.Entry entry : parameterMap.entrySet()) {            String value = entry.getValue()[0];            // 3            int para_index = query.indexOf(value);            if (para_index < 0) {                continue;            }            // 4            int start = tokens.length, end = tokens.length, distance = 2;            ...            // 5            if (end - start > distance) {                message = "SQLi - SQL query structure altered by user input, request parameter name: " + entry.getKey();            }        }        if (message != null) {            result.add(AttackInfo.createLocalAttackInfo(checkParameter, action,                    message, "sqli_userinput", 90));        } else {            // 6            for (String token : tokens) {                if (token.equals("select")) {                    int nullCount = 0;                    // 7                    for (int j = i + 1; j < tokens.length && j < i + 6; j++) {                        if (tokens[j].equals(",") || tokens[j].equals("null") || StringUtils.isNumeric(tokens[j])) {                            nullCount++;                        } else {                            break;                        }                    }                    // 8                    if (nullCount >= 5) {                        message = "SQLi - Detected UNION-NULL phrase in sql query";                        break;                    }                }                if (token.equals(";") && i != tokens.length - 1) {                    // 9                    message = "SQLi - Detected stacked queries";                    break;                } else if (token.startsWith("0x")) {                    // 10                    message = "SQLi - Detected hexadecimal values in sql query";                    break;                } else if (token.startsWith("/*!")) {                    // 11                    message = "SQLi - Detected MySQL version comment in sql query";                    break;                } else if (i < tokens.length - 2 && tokens[i].equals("into")                        && (tokens[i + 1].equals("outfile") || tokens[i + 1].equals("dumpfile"))) {                    // 12                      message = "SQLi - Detected INTO OUTFILE phrase in sql query";                    break;                } else if (i < tokens.length - 1 && tokens[i].equals("from"))) {                    // 13                    String[] parts = tokens[i + 1].replace("`", "").split("\\.");                    if (parts.length == 2) {                        String db = parts[0].trim();                        String table = parts[1].trim();                        if (db.equals("information_schema") && table.equals("tables")) {                            message = "SQLi - Detected access to MySQL information_schema.tables table";                            break;                        }                    }                }                if (message != null) {                    result.add(AttackInfo.createLocalAttackInfo(checkParameter, action,                            message, "sqli_policy", 100));                }                            }        }        return result;    }}

1.对SQL进行语法分析(严格来说,这里只完成了词法分析),获取分析后的token(词)。例如对SQL:select c2 from t1 where c1 = ‘a’,进行语法分析后,可以得到如下的token表:

373b1611a474358ec10a6273ee41d696.png

2.循环用户的输入,检测每个输入项;

3.获取用户输入项在SQL中的索引位置,如果未找到,则进入下一轮循环;

4.定义并计算用户输入项在token表中的start(开始索引)和end(结束索引),以及定义可能发生SQL注入时,用户输入项所占的最少token项,这里为2。例如SQL:select c2 from t1 where c1 = ‘${input}’,${input}为用户输入项:

  • 正常的用户输入,${input} = a:

856a23823334f22b0d852a0f93f8360e.png

  • 存在SQL注入的用户输入,${input} = ‘ or ‘1’ = ‘1:

3c25aac8ccf3e9a0107c334f384317ed.png

  • 上面的例子中,正常的用户输入项只会占7这一索引的token,而当用户输入项发生SQL注入的时候,占用了7~11总共4个索引的token。因此,可以通过计算用户输入项所占的token项(11-7=4)来判定用户的输入是否可能产生SQL注入。

5.判断用户输入项所占的token是否大于2,大于2则可能为攻击。为什么没有直接使用大于1来判定是否产生SQL注入?个人觉得可能的原因为,为了产生SQL注入,首先要对SQL中两个字符引号(即where c1 = ‘${input}’中的两个字符单引号’)进行闭合(‘ or ‘1’ = ‘1中的第一个和最后一个’分别用于闭合where c1 = ‘${input}’中的两个引号),闭合后自然产生了两个token。由此可知,为了产生SQL注入,用户输入项所占的token项必须大于2;

6.循环每个token,检测token是否符合安全策略;

7.计算连续出现null、,或者数字的token。这里的检测是为了防止可能为UNION查询攻击,例如:select c1, c2, c3, c4 from t1 where c1 = ‘a’ union select null, null, null, c5 from t2 where c6 = ‘b’;

8.判断连续出现的null、,或者数字的token是否大于5,大于5则可能为攻击。选用5应该是出于经验,null, null, null、1, 2, 3,这些token均为5,太小,容易产生误判,因为偶尔也会有特殊场景下需要使用null占一个或两个查询字段。个人觉得可以再加入union这个token作为判断依据会更精确;

9.判断非最后一个token是否为;,如果是,则可能为攻击。这里的检测是为了防止可能为堆叠查询,例如select c1 from t1 where c1 = ‘a’; select c2 from t2;

10.判断token是否为十六进制符号,如果是,则可能为攻击;

11.判断token是否包含/*!,如果包含,则可能为攻击。这里的检测是为了防止可能为内联注释攻击;

12.判断当前和下一个token是否为into outfile或者into dumpfile,如果是,则可能为攻击。这里的检测是为了防止可能为文件导出攻击;

13.判断当前和下一个token是否为from information_schema.tables,如果是,则可能为攻击。

注:上述分析中涉及到的攻击方式不做详细解释,请自行百度。

由上述分析可以得知,OpenRASP的SQL检测是基于对SQL的语法分析。相比传统的,特别是WAF,基于关键字、正则表达式匹配的方式,基于语法分析由于从语言层面去分析和理解SQL,可以做到更加的精准,减少误杀。当然,基于语法分析带来的问题是,更大的性能损耗和内存占用(需要构建一棵完整的语法树)。


安全基线检查

2.3

代码2-7

@HookAnnotationpublic class TomcatStartupHook extends ServerStartupHook {    @Override    public boolean isClassMatched(String className) {        // 1        return "org/apache/catalina/startup/Catalina".equals(className);    }    @Override    protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {        // 2        String src = getInvokeStaticSrc(TomcatStartupHook.class, "checkTomcatStartup", "");        insertBefore(ctClass, "start", null, src);    }    public static void checkTomcatStartup() {        // 3        HookHandler.doCheckWithoutRequest(CheckParameter.Type.POLICY_TOMCAT_START, CheckParameter.EMPTY_MAP);    }}
  1. 判断入参类是否需要被hook。这里检查的是Tomcat启动的核心类Catalina;

  2. 获取静态方法TomcatStartupHook.checkTomcatStartup()的源码,插入到实例方法Catalina.start()开始处;

  3. 调用静态方法HookHandler.doCheckWithoutRequest()进行检测。

根据3中的第一个入参,查找CheckParameter.Type:

public class CheckParameter {    public enum Type {        ...,        POLICY_TOMCAT_START("tomcatStart", new TomcatSecurityChecker()),        ...;}

可以得知,CheckParameter.Type.POLICY_TOMCAT_START对应的Checker实现为TomcatSecurityChecker。因此,最终检测逻辑即委托给了TomcatSecurityChecker,下面进行分析:

代码2-8

public abstract class ServerPolicyChecker extends PolicyChecker {    @Override    public List checkParam(CheckParameter checkParameter) {        List infos = new LinkedList();        // 1        checkStartUser(infos);        checkServer(checkParameter, infos);        return infos;    }    private void checkStartUser(List infos) {        String osName = System.getProperty("os.name").toLowerCase();        if (osName.startsWith("linux") || osName.startsWith("mac")) {            // 2            if ("root".equals(System.getProperty("user.name"))) {                infos.add(new SecurityPolicyInfo(SecurityPolicyInfo.Type.START_USER, "Java security baseline - should not start application server with root account", true));            }        } else if (osName.startsWith("windows")) {            // 3            Class ntSystemClass = Class.forName("com.sun.security.auth.module.NTSystem");            Object ntSystemObject = ntSystemClass.newInstance();            String[] userGroups = (String[]) ntSystemClass.getMethod("getGroupIDs").invoke(ntSystemObject);            for (String group : userGroups) {                // 4                if (group.equals("S-1-5-32-544")) {                    infos.add(new SecurityPolicyInfo(SecurityPolicyInfo.Type.START_USER, "Java security baseline - should not start application server with Administrator/system account", true));                }            }        }    }        public abstract void checkServer(CheckParameter checkParameter, List infos);}
  1. Server安全基线检查的基类,包含用户、Server自检查,其中Server自检查留给子类实现;

  2. 如果系统为Linux或者Mac,如果当前用户为root,则记录触发安全基线;

  3. 加载com.sun.security.auth.module.NTSystem类,通过该类可以获取Windows NT系统的安全信息;

  4. 通过NTSystem.getGroupIDs()获取当前用户组信息,如果为S-1-5-32-544(Windows NT系统上最高权限Administrators用户组),则触发安全基线。

代码2-9

public class TomcatSecurityChecker extends ServerPolicyChecker {    @Override    public void checkServer(CheckParameter checkParameter, List infos) {        // 1        String tomcatBaseDir = System.getProperty("catalina.base");        checkHttpOnlyIsOpen(tomcatBaseDir, infos);        checkManagerPassword(tomcatBaseDir, infos);        checkDirectoryListing(tomcatBaseDir, infos);        checkDefaultApp(tomcatBaseDir, infos);    }    private void checkHttpOnlyIsOpen(String tomcatBaseDir, List infos) {        // 2        File contextFile = new File(tomcatBaseDir + File.separator + "conf/context.xml");        Element contextElement = getXmlFileRootElement(contextFile);        // 3        String httpOnly = contextElement.getAttribute("useHttpOnly");        boolean isHttpOnly = true;        if (httpOnly != null && httpOnly.equals("false")) {          isHttpOnly = false;        }        // 4        if (!isHttpOnly) {          infos.add(new SecurityPolicyInfo(Type.COOKIE_HTTP_ONLY,                    "Tomcat security baseline - httpOnly should be enabled in " + contextFile.getAbsolutePath(), true));        }    }}
  1. 获取Tomcat安装根目录,我们先只分析checkHttpOnlyIsOpen();

  2. 获取根目录下的conf/context.xml文件,并解析XML;

  3. 获取根元素的userHttpOnly属性的值;

  4. 如果userHttpOnly为false,则触发安全基线。

除了HttpOnly的检查,TomcatSecurityChecker.checkServer()里还包括管理员密码、目录列表等安全配置检查。

从上述两段代码可以得知,OpenRASP Java的安全基线检查和普通的Java程序并没有任何区别。当然,也正因为和普通的Java程序没有区别,因此OpenRASP也只能完成运行当前应用程序的用户所能完成的检查,比如一些需要特殊用户权限才能完成的检查。


其他检查

2.4

代码2-10

public class SQLResultSetHook extends AbstractSqlHook {    @Override    public boolean isClassMatched(String className) {        // 1        if ("com/mysql/jdbc/ResultSetImpl".equals(className)                || "com/mysql/cj/jdbc/result/ResultSetImpl".equals(className)) {            this.type = "MySQL";            return true;        }        ...        return false;    }    @Override    protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {        // 2        String src = getInvokeStaticSrc(SQLResultSetHook.class, "checkSqlQueryResult",                "\"" + type + "\"" + ",$0", String.class, Object.class);        insertBefore(ctClass, "next", "()Z", src);    }    public static void checkSqlQueryResult(String server, Object sqlResultSet) {        ResultSet resultSet = (ResultSet) sqlResultSet;        // 3        int queryCount = resultSet.getRow();        HashMap params = new HashMap(4);        params.put("query_count", queryCount);        params.put("server", server);        // 4        HookHandler.doCheck(CheckParameter.Type.SQL_SLOW_QUERY, params);    }}
  1. 判断入参类是否需要被hook。这里检查的是MySQL JDBC驱动ResultSet接口的实现类ResultSetImpl;

  2. 获取静态方法SQLResultSetHook.checkSqlQueryResult()的源码,插入到实例方法ResultSetImpl.next()开始处,即获取数据库查询结果的方法开始处;

  3. 获取查询结果返回的总条数;

  4. 调用静态方法HookHandler.doCheck()进行检测。

根据3中的第一个入参,查找CheckParameter.Type:

public class CheckParameter {    public enum Type {        ...,        SQL_SLOW_QUERY("sqlSlowQuery", new SqlResultChecker(false)),        ...;}

可以得知,CheckParameter.Type.SQL_SLOW_QUERY对应的Checker实现为SqlResultChecker。因此,最终检测逻辑即委托给了SqlResultChecker,下面进行分析:

代码2-11

public class SqlResultChecker extends AttackChecker {    @Override    public List checkParam(CheckParameter checkParameter) {        LinkedList result = new LinkedList();        // 1        Integer queryCount = (Integer) checkParameter.getParam("query_count");        // 2        int slowQueryMinCount = Config.getConfig().getSqlSlowQueryMinCount();        // 3        if (queryCount == slowQueryMinCount) {            result.add(AttackInfo.createLocalAttackInfo(checkParameter, EventInfo.CHECK_ACTION_INFO,                    "慢查询: 使用SELECT语句读取了大于等于" + slowQueryMinCount + "条数据", "slow query"));        }        return result;    }}
  1. 获取查询结果返回的总条数;

  2. 从配置文件(RELEASE版本的conf/rasp.properties)中的SQL慢查询最小条数,配置项为sql.slowquery.min_rows,默认配置值为500;

  3. 判断实际查询返回的总条数是否大于等于配置值(源码中为==,BUG),大于等于则记录安全检查结果。


总结

3

经过上述的分析,我们了解到,OpenRASP:

  1. 通过往特定Web容器实现中注入检测逻辑,可以完成对所有Web请求的检测;

  2. 通过往特定JDBC驱动实现中注入检测逻辑,可以完成对所有SQL的检测。其中,SQL的检测基于语法分析;

  3. 通过往特定类中注入检测逻辑,可以完成安全基线的检查,以及一些非安全相关的检查。

因为RASP天然具备获取应用运行过程中上下文,所以RASP具备理解应用上下文的能力。在理解应用上下文的前提下,可以获取到精准的应用数据进行检测,检测的结果可以直接反馈给应用,应用依据反馈结果进行决策。

d24232affcb9df03d18dd52b329faef3.gif