SQL注入检测模块开源项目DRUID-SQL-WALL学习小结

 

作为sql注入原理、sql注入检测、防御系列学习的第三篇。本文主要关注了抽象语法树ast在sql注入检测上的应用开发、以及开源项目druid-sql-wall的学习,希望能给研究这一领域的朋友带来一点帮助,同时也希望能引发大家的共同讨论,共同学习、成长

上一篇文章中,我们学习了其他数据库 防火墙的一些基本知识

http://www.makaidong.com/littlehann/p/3505410.html

文章的接下来部分准备分为2部分进行学习:

    1. sql注入语法防御规则

    2. druid中sql注入防御模块sql-wall

相关学习资料

http://code.alibabatech.com/wiki/display/druid/wallfilter

http://code.alibabatech.com/wiki/display/druid/wallfilterconfig

http://code.alibabatech.com/wiki/display/druid/get+druid

其他数据库 防火墙位于前端应用开发层之后,前端的应用开发层可以是php、asp、java等,这些语言通过一些统一的访问接口(odbc、jdbc)对数据库系统发起访问请求

所以到了其他数据库 这一层的都是纯的sql请求,所以在其他数据库 这一层面要考虑的不是一些应用开发系统开发的oday、本地变量覆盖的漏洞,而应该明确我们所处的防御层面,我们要防御的是黑客针对其他数据库 发起的攻击。

1. 针对其他数据库 的缓冲区溢出攻击: 这个是实战中很少见,详见
2. 针对其他数据库 底层代码的极限领域的攻击,例如,这是在一个ctf中出现过的mysql attack topic:
<?php
    # goal: dump the info for the secret id
    require 'db.inc.php';

    $id = @(float)$_get['id'];
    die(var_dump($id));
    $secretid = 1;
    if($id == $secretid)
    {
        echo 'invalid id ('.$id.').';
    }
    else
    {
        $query = 'select * from users where id = \''.$id.'\';';
        $result = mysql_query($query);
        $row = mysql_fetch_assoc($result);
        
        echo "id: ".$row['id']."</br>";
        echo "name:".$row['name']."</br>";
    }
?>
http://localhost/php4fun/index.php?id=1.0000000000001
攻击者的目标的是要查出id为1的admin的数据,这里的绕过思路是利用了mysql的精度范围和php的精度范围不同,精度小的会忽略不能支持的位数。也就是说,浮点型的精度有上限和下限

3. 纯粹的拼接sql语法对数据进行注入攻击: 这是最常见的,我们接下来重点分析这方面内容

sql注入语法防御规则

    目前,druid的防御重点主要放在拼接型的sql注入攻击,即利用注入点在原始的sql语句综合 的中间或后面"插入"、"拼接"上攻击性的sql payload,从而达到提取非法数据等目的,缓冲区溢出和特殊情况的攻击druid暂时没有实现,将放到未来的版本中逐渐完善,下面根据温少的文档、并 配合druid的源代码进行学习进行具体规则的学习:

0x1 只允许执行增删改查基本语句
\druid\src\main\java\com\alibaba\druid\wall\wallconfig.java(druid的源码和整体架构放在文章的后半部分)
....
//是否允许非以上基本语句的其他语句,缺省关闭,通过这个选项就能够屏蔽ddl。
private boolean             nonebasestatementallow      = false;
....
这是最严格模式,但是也最缺乏灵活性,基本上是不能开启的,在正常的用户业务需求中,必不可少会用到除了crud(增删改查)之外的需求,开启这条规则会导致大量的误报,故druid默认关闭这个开关

0x2 不允许一次执行多条语句 每次只允许执行一条sql,一次执行多条sql,是被认为可能正被sql注入攻击。

1. sql server 6.0在其架构中引入了服务端游标,从而允许在同一连接句柄上执行包含多条语句的字符串。所有6.0之后的sql server 版本均支持该功能且允许执行下列语句:

select id from users;select name from users;

客户端连接到sql服务器开发并依次执行每条语句,其他数据库 服务器开发向客户端返回每条语句发送的结果集。

http://database .51cto.com/art/201007/213806.htm

2. mysql在4.1及之后的版本中也引入了该功能,但是php自身限制了这种用法。

<?php
     $con = mysql_connect("127.0.0.1", "root" , "111");
     mysql_select_db("php4fun_", $con);
     $sql = "update users set level=2;update users set pass=3;"; 
    $result = mysql_query($sql, $con); echo mysql_error(); 
    if($result) 
    { 
        $result_array = mysql_fetch_array($result); var_dump($result_array); 
    } 
?>

result: you have an error in your sql syntax; check the manual that corresponds to your mysql server version for the right syntax to use near 'select 1,2,3,4 from dual' at line 1 而如果使用的pdo方式操作其他数据库

<?php 
    $db = new pdo("mysql:host=localhost:3306;dbname=php4fun_", 'root', '111');
     $sql = "update users set level=2;update users set pass=3;";
     try 
    { 
        $db->query($sql); 
    } catch(pdoexception $e) {
         echo $e->getmessage(); die();
     } 
?> 

result: ok

3. oracle不支持多条语句,除非使用pl/sql \druid\src\main\java\com\alibaba\druid\wall\wallconfig.java .... private boolean multistatementallow = false; .... druid默认是禁止这种格式的sql语句综合 的,也即如果在传入的sql语句综合 中解析出了2条及以上的sqlstatement(一个sqlstatement抽象了一条sql语句综合 )就判断为注入攻击

0x3 不允许访问系统开发表
在之前的学习笔记

此文来自: 马开东博客 转载请注明出处 网址:

中,有总结过,从攻击者渠道的角度去理解,攻击者最终的目的是要获取信息 http://www.makaidong.com/littlehann/p/3495602.html 而"访问系统开发表"就是获取信息的渠道之一,故需要拦截之 但是druid对这种规则的判断更加细化,druid只拦截在子句中出现的连接系统开发表查询,举例说明: 1. select * from information_schema.columns; 这条语句druid认为是合法的,因为这条语句没有注入点的存在,sql语句综合 本身的唯一目的就是查询系统开发表,说明用户在进行正常的业务操作 2. select id
from admin
where id = 1
    and 5 = 6
union
select concat(0x5e252421, count(8), 0x2a5b7d2f)
from (select `column_name`, `data_type`, `character_set_name`
    from `information_schema`.`columns`
    where table_name = 0x73696e6765725f616c62756d
        and table_schema = 0x796971696c61695f757466
    ) t
这条语句druid认为是非法的注入攻击,因为sql在子句(可能是注入点的地方)采取了union拼接,进行了连接系统开发表的查询的操作 druid通过判断information_schema在ast层次结构中的位置,具体来说就是判断它的父节点是否为"sql表达式"(例如union select)、以及它的左节点是否为"from节点"。
即满足子句拼接的模式。以此来判断这条sql语句综合 是否有攻击性,在代码中的体现就是 druid\src\main\java\com\alibaba\druid\wall\spi\wallvisitorutils.java ..... boolean sametotopselectschema = false; if (parent instanceof sqlselectstatement) { sqlselectstatement selectstmt = (sqlselectstatement) parent; sqlselectquery query = selectstmt.getselect().getquery(); if (query instanceof sqlselectqueryblock) { sqlselectqueryblock queryblock = (sqlselectqueryblock) query; sqltablesource from = queryblock.getfrom(); while (from instanceof sqljointablesource) {   from = ((sqljointablesource) from).getleft(); } if (from instanceof sqlexprtablesource) { sqlexpr expr = ((sqlexprtablesource) from).getexpr(); if (expr instanceof sqlpropertyexpr) { sqlexpr schemaexpr = ((sqlpropertyexpr) expr).getowner(); if (schemaexpr instanceof sqlidentifierexpr) {   string schema = ((sqlidentifierexpr) schemaexpr).getname();   schema = form(schema);   if (schema.equalsignorecase(owner))   {   sametotopselectschema = true;   } }  } } } } if (!sametotopselectschema) { addviolation(visitor, errorcode.schema_deny, "deny schema : " + owner, x); } 而代码中的owner是从配置文件中读取的: string owner = ((sqlname) x).getsimlename(); owner = wallvisitorutils.form(owner); if (isintablesource(x) && !visitor.getprovider().checkdenyschema(owner)) { ... 配置文件被统一放在了: \druid\src\main\resources\meta-inf\druid\wall\mysql\deny-schema.txt information_schema mysql performance_schema 这样,druid就完成了对sql中的对系统开发敏感表的注入的智能检测

 

 

0x4 不允许访问系统开发对象
在sqlserver中有系统开发对象的概念。对敏感系统开发对象"sysobject"的检测也是同样的原理,即只检测子句的非法连接,并从配置文件中读取拦截列表,代码和对系统开发表的检测是类似的

 

 

0x5 不允许访问系统开发变量
系统开发敏感变量同样也是攻击者获取非法数据的一种渠道,druid采取智能判断的做法,举例说明:

1. select @@basedir;
这条语句druid不做拦截,因为这里没有注入点的存在,也就不可能是黑客的注入攻击,应该归类于业务的正常需要

2. select * from cnp_news where id='23' and len(@@version)>0 and '1'='1'
这条语句druid会做拦截,攻击者在子句中利用逻辑表达式进行非法的探测注入,目前druid的检测机制是"黑名单机制",把需要禁止的系统开发变量写在了配置文件中:
druid\src\main\resources\meta-inf\druid\wall\mysql\deny-variant.txt
basedir
version_compile_os
version
datadir
druid\src\main\java\com\alibaba\druid\wall\spi\wallvisitorutils.java
...
if (!checkvar(x.getparent(), x.getname()))
{
    boolean istop = wallvisitorutils.istopnonefromselect(this, x);
    if (!istop)
    {
    boolean allow = true;
    if (wallvisitorutils.iswhereorhaving(x) && isdeny(varname))
    {
        allow = false;
    }

    if (!allow)
    {
        violations.add(new illegalsqlobjectviolation(errorcode.variant_deny, "variable not allow : " + x.getname(), tosql(x)));
    }
    }
}
...

 

 

0x6 不允许访问系统开发函数
和"系统开发敏感表"、"系统开发敏感对象"、"系统开发敏感变量"一样,系统开发敏感函数也是攻击者用来获取非法信息的一种手段之一
druid中和禁用系统开发函数的配置文件:
druid\src\main\resources\meta-inf\druid\wall\mysql\deny-function.txt
version
load_file
database 
schema
user
system_user
session_user
benchmark
current_user
sleep
xmltype
receive_message

对于系统开发敏感函数的禁用,这里要注意一下,和系统开发表的防御思想类型,druid会智能地判断敏感函数在sql语句综合 中出现的位置,例如:
1. select load_file('\\etc\\passwd');
druid不会拦截这条语句,还是同样的道理,sql注入的关键在于注入点,这条语句没有注入点的存在,所以只能是用户正常的业务需求

2. select * from admin where id =(select 1 from (select sleep(0))a);   
druid会智能地检测出这个敏感函数出现在"where子句节点"中,而"where子句节点"经常被黑客用来当作一个sql注入点,故druid拦截之
代码如下:
druid\src\main\java\com\alibaba\druid\wall\spi\wallvisitorutils.java
public static void checkfunction(wallvisitor visitor, sqlmethodinvokeexpr x)
{
    final walltopstatementcontext topstatementcontext = walltopstatementcontextlocal.get();
    if (topstatementcontext != null && (topstatementcontext.fromsysschema || topstatementcontext.fromsystable))
    {
        return;
    }

    checkschema(visitor, x.getowner());

    if (!visitor.getconfig().isfunctioncheck())
    {
        return;
    }

    string methodname = x.getmethodname().tolowercase();

    wallcontext context = wallcontext.current();
    if (context != null)
    {
        context.incrementfunctioninvoke(methodname);
    }

    if (!visitor.getprovider().checkdenyfunction(methodname))
    {
        boolean istopnonefrom = istopnonefromselect(visitor, x);
        if (istopnonefrom)
        {
        return;
        }

        boolean isshow = x.getparent() instanceof mysqlshowgrantsstatement;
        if (isshow)
        {
        return;
        }

        if (iswhereorhaving(x))
        {
        addviolation(visitor, errorcode.function_deny, "deny function : " + methodname, x);
        }
    }
}   

 

 

0x7 不允许出现注释
正常执行的sql是不应该附带注释的,有注释的sql都会被认为是危险操作。druid是默认"禁止"单行注释和多行注释。这里所谓的"禁止"是值druid会在解析前自动地去除原始sql语句综合 中的注释。
例如攻击者常用的绕过方式:
1) sel/**/ect us/**/er() from dual;  (黑客常用来绕过基于正则前端waf)
2) select * from admin where no=4 and 1=2 /!40001+union/ select 1,concat(database (),0x5c,user(),0x5c,version()),3,4,5,6,7   
(mysql的comment dynamic execution bypass)     
http://www.freebuf.com/articles/web/22041.html

这里druid采取的防御思路是"规范化",代码自动会将注释的部分删除,重新拼接sql语句综合 后,对"规范化"后的语句再进行注入检测,删除注释的代码逻辑在词法解析器中:
druid\src\main\java\com\alibaba\druid\sql\parser\lexer.java
..
protected boolean      skipcomment  = true;
..
public final void nexttoken()
{
      ....
      /*
                    解析'#'注释符
                    判断'#'解析出的节点是'单行注释'、或'多行注释'
                */
                case '#':
                    scansharp();
                    if ((token() == token.line_comment || token() == token.multi_line_comment) && skipcomment)
                    {
                        bufpos = 0;
                        continue;
                    }
                    return;
      ....
      /*
                        检测是否是'--'这种单行注释符
                 */
                 if (subnextchar == '-')
                 {
                        scancomment();
                        if ((token() == token.line_comment || token() == token.multi_line_comment) && skipcomment)
                        {
                            bufpos = 0;
                            continue;
                        }
                 }
      ...
      /*
                        判断当前节点是否是 /*  */  这种类型的多行注释
                 */
                 if (nextchar == '/' || nextchar == '*')
                 {
                        scancomment();
                        if ((token() == token.line_comment || token() == token.multi_line_comment) && skipcomment)
                        {
                            bufpos = 0;
                            continue;
                        }
                 }
...
在对sql的词法解析的开发过程 中,druid就会自动地对

 

此文来自: 马开东博客 转载请注明出处 网址:

各种形式的注释符进行删除,删除了注释后,druid再去解析sql语句综合 ,这个时候会出现两个情况: 1) 解析失败抛异常,说明原本的sql语句综合 很有可能是攻击型的sql语句综合 ,黑客使用了注释绕过或者注释执行技术 2) 解析正常,说明这是正常的sql语句综合 ,不排除有的程序猿会把一些简短的注释写在sql语句综合 中,但是这个注释的删除对原本的执行没有影响,所以也就判定为合理sql语句综合 oracle hints的语法是/* + */,druid能够区分注释和hints

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot可以很方便地整合Druid数据源,只需要在pom.xml中添加Druid和JDBC依赖,然后在application.properties中配置Druid数据源即可。 1. 添加依赖 在pom.xml中添加以下依赖: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` 2. 配置Druid数据源 在application.properties中添加以下配置: ``` # 数据源配置 spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # Druid配置 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.test-on-borrow=true spring.datasource.druid.test-on-return=false spring.datasource.druid.test-while-idle=true spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.min-evictable-idle-time-millis=300000 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 ``` 3. 使用Druid数据源 在需要使用数据源的地方,注入DataSource即可: ``` @Autowired private DataSource dataSource; ``` 以上就是Spring Boot整合Druid的基本步骤。 ### 回答2: Spring Boot是一种开源的Java框架,用于快速构建Web应用程序。Druid是一种功能强大的数据库连接池和监控工具。将这两个工具整合在一起,可以优化应用程序的性能和可靠性。本文将讨论如何在Spring Boot应用程序中使用Druid连接池。 Druid数据库连接池的优点 1. 高性能:Druid可以有效地管理数据库连接,提供优化过的连接池,从而提高应用程序的性能。 2. 可扩展性:Druid可以管理大量的连接和请求,因此可以扩展以满足未来的需求。 3. 监控和统计:Druid提供详细的监控和统计数据,可帮助开发人员更好地管理数据库连接。 添加Druid依赖 要使用Druid连接池,首先需要在pom.xml文件中添加Druid依赖项。在此过程的开始,您将在项目构建配置中添加下面这段xml内容: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> 完成maven配置后,可以在Spring Boot应用程序的Java文件中开始配置Druid连接池。 配置Druid连接池 Spring Boot使用Java的@Configuration和@Bean注解来配置Druid连接池。要配置Druid连接池,您可以在Spring Boot应用程序中创建一个名为DruidConfig的Java类,并向此类添加@Configuration注解。添加完后,会在项目中添加一个可以使用注解方式进行Bean注入的属性,代码如下: @Configuration public class DruidConfig { @Bean public ServletRegistrationBean druidServlet() { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String,String> params = new HashMap<>(); params.put("loginUsername","admin"); params.put("loginPassword","admin"); params.put("allow",""); params.put("deny","192.168.1.100"); servletRegistrationBean.setInitParameters(params); return servletRegistrationBean; } @Bean public FilterRegistrationBean druidFilter() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); Map<String,String> params = new HashMap<>(); params.put("exclusions","*.js,*.css,/druid/*"); filterRegistrationBean.setInitParameters(params); filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); return filterRegistrationBean; } } 上述代码中@Bean注解用于实例化该Bean,其中的ServletRegistrationBean和FilterRegistrationBean分别用于配置Druid提供的Servlet和Filter,允许通过浏览器监控数据库状态和执行SQL语句。 DataSource配置 创建配置类后,需要将Druid连接池配置为Spring Boot应用程序的默认数据源。在application.properties或application.yml文件中添加以下DataSource配置: # 请自行修改jdbc连接池(密码) spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.test-on-borrow=true spring.datasource.druid.test-on-return=true spring.datasource.druid.test-while-idle=true spring.datasource.druid.validation-query=select 1 spring.datasource.druid.max-wait=60000 其中,spring.datasource.type是DataSource类型,druid相关配置则是Druid特有的配置。 使用Druid连接池 现在已经完成了Druid连接池的配置,可以在Spring Boot应用程序中使用Druid连接池了。只需要在需要访问数据库的Bean类中注入DataSource即可: @Autowired private DataSource dataSource; 测试Druid连接池 完成以上步骤后,就可以测试Druid连接池了。您可以在浏览器中输入http://localhost:8080/druid/index.html,然后输入DruidConfig.java文件中配置的用户名和密码(admin/admin),即可查看数据库的各种详细信息。 通过集成Druid连接池,我们可以轻松地监控和管理数据库连接,提高应用程序的性能和可靠性,这也是我们高效开发Java应用程序的重要工具之一。 ### 回答3: SpringBoot是一个一站式架构,具有简单和高效的特性。Druid是一种开源的数据库连接池。Druid池提供了丰富的监控和管理功能,可确保应用程序稳定,并且非常适用于高并发。 SpringBoot整合Druid可以实现系统的高效管理和优化数据处理。以下是关于如何整合SpringBoot框架和Druid连接池的步骤: 第一步:在pom.xml中添加Druid依赖关系: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> ``` 第二步:在application.properties配置文件中增加Druid连接池的相关属性: ``` spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.url=jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&characterSetResults=utf-8&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #配置初始化大小、最小、最大连接数 spring.datasource.initialSize=2 spring.datasource.minIdle=2 spring.datasource.maxActive=20 #配置获取连接等待超时的时间 spring.datasource.maxWait=60000 #配置一个连接在池中最小生存的时间 spring.datasource.minEvictableIdleTimeMillis=300000 #配置从池中取出连接后是否进行测试 spring.datasource.testWhileIdle=true #连接测试时使用的SQL语句 spring.datasource.validationQuery=SELECT 1 from dual #配置监控统计拦截的filters,并去掉重连问题 spring.datasource.filters=stat,wall,log4j ``` Druid连接池中主要的核心参数有:driver、url、username、password等,这些参数可以对应不同类型的数据库连接。 第三步:在应用程式中加入Druid的监控器,以监听连接池的情况。我们可以通过配置Druid监控器来查看整个系统的监控数据,在web.xml文件中加入Druid的Servlet。 第四步:编写测试类,验证Druid连接池和SpringBoot框架是否集成起来。 ``` @SpringBootTest public class DruidTest { @Autowired private DataSource dataSource; @Test public void test() throws SQLException { System.out.println(dataSource.getConnection()); } } ``` 以上是整合SpringBoot框架和Druid连接池的步骤,可以让我们方便地管理和优化数据处理。Druid可以提供监控和管理功能,可以确保应用程序稳定,并且非常适用于处理高并发。整合后的应用程序可以提高应用程序的性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值