zoukankan      html  css  js  c++  java
  • [转]SQL注入漏洞及绑定变量浅谈

     1、一个问题引发的思考

      大家在群里讨论了一个问题,奉文帅之命写篇作文,且看:

    String user_web = "user_web"
    String sql = "update user set user_web="+user_web+" where userid=2343";

      大家看看这条sql有没有问题,会将user_web字段 更新成什么?

      问题的结论是:执行后的记录结果跟执行前一样,(执行时的sql语句为

    update user set user_web=user_web where userid=2343,

      user_web字段值被update为自己原有的值),这与作者的本意想违背却很难被发现有问题。原来的语句漏掉了一对单引号,正确的写法应该是:

    String sql = "update user set user_web='"+user_web+"' where userid=2343";

      用这种写法将变量值传递到sql语句中,意图是达到了,但不是好的方式,理由如下:

      1.可读性差。单引号双引号混杂(试想有多个变量的情况,再想下如果稍一不慎在前面的单引号双引号直间多个空格又会怎样?)

      2.会造成潜在的性能问题和sql注入漏洞(对测试代码而言,这两点可能要求不高,但养成良好的编码习惯还是很重要的)

      下面以非专业人员的角度大致分析下 ‘”+变量+”‘(未采用绑定变量方式)这种方式组织sql为什么会造成潜在的性能问题和sql注入漏洞问题

      2、性能问题

      Sql代码不采用绑定变量的方式可能会造成性能问题,表现在以下两个方面:

      1.导致相同的测试计划被重复执行

      sql语句的执行过程分几个步骤:语法检查、分析、执行、返回结果。当一条sql通过语法检查后,会在共享池里寻找是否有跟其相同的语句,如果有则用已有的执行计划执行sql语句,如果没有找到,则生成执行计划,然后才执行sql语句。可见,后者比前者多了额外的步骤,消耗了额外的CPU,并导致sql总体执行时间延长,而这里的关键就是“共享池中是否有相同的sql语句”。

    String username="test_xx";
    String sql = "SELECT id,nick FROM user WHERE username='"+username+"'";

      以这样的方式传递到数据库中的sql为

    SELECT id,nick FROM user WHERE username='test_xx'

      假定这个语句是第一次执行,会生成执行计划。当变量发生变化时(username=”test_yy”),数据库又接收到这样的语句

    SELECT id,nick FROM user WHERE username='test_yy'

      Oracle不认为以上两条语句是相同的,因此又会生成执行计划,而这两者的执行计划是一样的(做了重复的工作)

      2.导致共享池中的sql语句过多,加速SQL老化,造成共享池内部结构频繁维护。
    如果一个某段程序未采用绑定变量的方式而又被大量调用,会导致共享池中不同的sql语句增多,而重用性极低,导致共享池内命中率下降。随着sql数量过多,一些语句逐渐老化,最终被清理出共享池。 而维护共享池内部结构要消耗大量的CPU和内存资源。

      3、Sql注入漏洞

      不采用绑定变量的方式可能会造成sql注入漏洞,本文仅仅通过示例说明为什么会造成sql注入漏洞,不对攻击方式、攻击类型等展开。以一个用户验证为例。

    String sql = "SELECT id,nick FROM user WHERE username='"+username+"' 
    AND password='"+password+"'";

      以上代码接收从客户端传来的username和password变量,在数据库中查询验证。假设攻击者从客户端传的username为任意值(如test)password变量为
    1′ or ‘1′=’1
    此时替换变量后的sql变为

    SELECT id,nick FROM user WHERE username='test' AND password='1' or '1'='1'

      这样得到的结果就是user表中的所有数据了。

      4、使用绑定变量

      以上两种问题的解决方式就是使用绑定变量,就是在sql语句里不直接写变量,而是用占位符,在执行时再把占位符替换为具体的变量值。代码片段如下

    String sql = "SELECT id,nick FROM user WHERE username=? AND password=?";
    preparedStatement.setString(1,username);
    preparedStatement.setString(2,password);

      一些常用Jdbc工具对此进行了良好的封装,使代码更加简洁。比如Spring的SimpleJdbcTemplate

    String sql = "SELECT id,nick FROM user WHERE username=? AND password=?";
    jdbcTemplate.queryForList(sql,username,password);

      上面 ?的做占位符的形式被称为顺序占位符,在传参数值时必须注意顺序对应,还有一种是名称占位符。同样以SimpleJdbcTemplate为例说明.

    String sql = "SELECT id,nick FROM user WHERE username=:name AND password=:pass";
    map.put("pass",password);
    map.put("name",username);
    jdbcTemplate.queryForList(sql,map);

      上面的例子中的:name和:pass就是名称占位符,在执行时sql时再绑定变量。
    iBatis中有两种占位符,#name# 和$name$两种方式,需要注意的是前者会在执行sql时绑定变量,而后者直接是替换为变量值,所以后者仍然存在sql注入漏洞问题。

      5、未尽话题

      接口测试要不要检查sql注入漏洞问题?这个问题值得商榷,个人认为通过常规的用例设计检查sql注入漏洞恐怕不太可行(工作量太大效果不一定好),如果要做的话可借助(或自己开发)一些工具,通过扫描静态代码再人工排查的方式进行。另外如果这项工作进行的太细致,恐怕会跟安全测试的工作重叠太多,当然如果在测试过程中发现开发的代码存在sql注入漏洞(这往往跟开发者编码习惯有关)问题,一定不要放过,进行排查还是很有必要的。

    转自:http://kb.cnblogs.com/page/55287/

  • 相关阅读:
    Jquery 跨域请求JSON数据问题
    js定时器实现图片轮播
    Redis数据一致性
    Redis缓存击穿、缓存穿透、缓存雪崩
    数据库连接池druid连接mysql数据库‘链路断开’问题
    Mysql启动错误: Can’t create test file xxx lower-test
    DB2-表空间
    DB2-Schema
    DB2-数据库
    DB2-实例
  • 原文地址:https://www.cnblogs.com/shengs/p/4175475.html
Copyright © 2011-2022 走看看