sql注入攻击及其防止办法

一、sequlize.query防止sql注入

  在nodejs中使用sequlize库来查询mysql数据库,提供了常用的方法有两种:

// 1、直接查询sql语句
sequelize.query();// 需要做sql防注入

// 2、通过接口
Project.findAll();   //在实现上就做了sql防注入处理,但是必须配合model使用,不灵活

  sequelize.query() 原生查询使用replacements 防sql注入

sequelize.query('SELECT * FROM projects WHERE status = ?',
  { replacements: ['active'], type: sequelize.QueryTypes.SELECT }
).then(projects => {
  console.log(projects)
})

  sequelize的第2种查询方法在实现上做了防注入处理, 但该方法使用并不是很灵活, 对于经常使用原生sql语句的人来说, 总有一种不适应, 感觉手脚被束缚; 而对于使用方法1, 则必须注意防sql注入;

1、产生原因

  下面的查询语句, 加入了用户输入的时间项: begin, end, 正常情况是:

begin = 20161101;
SELECT .. FROM tbl WHERE pdate>=20161101 AND ...;

  而如果有人故意输入如下的参数, 则就会出现sql注入:

begin = '20161101 AND 1=1; -- hack';
SELECT .. FROM tbl WHERE pdate>=20161101 AND 1=1; -- hack ...;

2、解决办法

  解决上面的办法, 通常有两种:

(1)对输入的参数进行转义, 因为sql注入通常需要'符号来闭合前面的', 这样如果位面在输入的参数中的'全部转义为\', 则查询时就不会出现中断(执行两条sql语句), 参看下面的例子;
(2)设置mysql只能一次执行一条sql语句;

3、测试

  sequelize.query()Project.findAll()对比,先看代码

var tblName = 'tbl_test', begin = '20160923 AND 1=1;-- hack', end = 20160928;
var sqlQuery = `SELECT * FROM ${tblName} WHERE pdate>=${begin} AND pdate<=${end} GROUP BY pdate`;
Promise.resolve([
    // 第一条查询
    sequelize.query(sqlQuery),   
    // 第一二条查询
    Project.findAll({   
        attributes: { exclude:['id'] },
        where: {
            pdate: {
                $and:{
                    // 参数中加上了'来闭合前面的', 后面的1=1为注入语句
                    $gte: "20160923' AND 1=1;-- hack",   
                    $lte: end
                }   
            }
        }
    })
]).spread(function(sql1, sql2){

})

(1)上面第一个为sequelize.query()执行, 最终执行的查询语句为:

select * from tbl_test where padte>=20160923 and 1=1;-- hack and pdate<=....; 

  结果出现注入, pdate<=..这一块的条件没有被执行;

(2)上面第二个查询为Project.findAll()执行, 最终执行的语句为:

select * from tbl_test where pdate>'20160923\' AND 1=1;-- hack' AND pdate<='...'.....; 

  显然,这里参数里的20160923'引号没有起作用, 被转换为了`20160923\”,所以有效避免了注入, 制查询了符合要求的数据或空数据;

4、sequelize.query() + 参数绑定

  下面通过sequelize.query()接口提供的参数绑定和查询方法指定来防止sql注入的发生。

  • 下面的查询语句解析为:
    select * from tbl_test where padte>='20160923 and 1=1;-- hack' and pdate<=....;
    字符串被直接替换,没有被注入;
  • 注意,这里的表名table不能通过bind的参数传进去, 因为这样在语句里会表达为字符串: …. from ‘tbl_test’ where …, 这样是错误的语句,所以上面用字符串模板传入,也可以硬编码进去;
var tblName = 'tbl_test', begin = '20160923 AND 1=1;-- hack', end = 20160928;
var sqlQuery = `SELECT * FROM ${tblName} WHERE pdate>=$begin AND pdate<=$end GROUP BY pdate`;
Promise.resolve([
    sequelize.query(sqlQuery,
        type: ymModel.sequelize.QueryTypes.SELECT,  // 指定sql为SELECT
        bind: { 
            begin: begin, 
            end: end 
        }
    ),
]).spread(function(sql1){

})

二、浅析sql攻击

  SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。

1、适用范围:

(1)如果一个系统是通过

SELECT * FROM accounts WHERE username='admin' and password = 'password'

  这种显式的SQL来进行登陆校验,也就是执行这个SQL语句,如果数据库中存在用户名为admin, password为password的用户,就登陆成功,否则就登陆失败。

(2)系统没有对用户输入进行全面的过滤

(3)系统后台使用的是MYSQL数据库

2、攻击原理:

  利用MYSQL的注释功能,也就是"/*",mysql执行SQL脚本时,如果遇到  /*  标示符,就会把之以后的SQL当做注释而不会执行,

  正常情况下用户在用户名框内输入"admin",在password框内输入"password",后台执行的SQL语句就为:SELECT * FROM accounts WHERE username='admin' and password = 'password';

  但是如果在用户名框内输入"admin' AND 1=1 /*", 在密码框内输入任意字符串,那么后台执行的SQL就为:SELECT * FROM accounts WHERE username='admin' AND 1=1 /* and password = 'aa',

  可以看到数据库实际执行的SQL为:SELECT * FROM accounts WHERE username='admin' AND 1=1, 而 /* 后面的SQL就被当做注释而忽略掉了,登陆成功!

  这就是sql注入攻击。

3、sql注入攻击的总体思路

(1)寻找到SQL注入的位置

(2)判断服务器类型和后台数据库类型

(3)针对不同的服务器和数据库特点进行SQL注入攻击

4、sql注入攻击实例

  比如在一个登录界面,要求输入用户名和密码:

  可以这样输入实现免帐号登录:

  用户名: ‘ or 1 = 1 –

  密 码:

  点登陆,如若没有做特殊处理,那么这个非法用户就很得意的登陆进去了。(当然现在的有些语言的数据库API已经处理了这些问题)

  这是为什么呢? 下面我们分析一下:

  从理论上说,后台认证程序中会有如下的SQL语句:

String sql = "select * from user_table where username='" + userName + "' and password='" + password + "'";

  当输入了上面的用户名和密码,上面的SQL语句变成:

SELECT * FROM user_table WHERE username=
'’or 1 = 1 -- and password='

  分析SQL语句:
  条件后面 username='' or 1=1 用户名等于 '' 或 1=1 那么这个条件一定会成功;
  然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都能正确执行,用户轻易骗过系统,获取合法身份。这还是比较温柔的,如果是执行

SELECT * FROM user_table WHERE
username='' ;DROP DATABASE (DB Name) --' and password=''

  其后果可想而知。

5、应对方法

(1)采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

  一般ORM框架都有提供,直接调用其内部方法即可。sql注入只对sql语句的准备(编译)过程有破坏作用,而比如PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析准备,因此也就避免了sql注入问题。

(2)使用正则表达式过滤传入的参数或者比如具体的字符串过滤等

(3)进行参数转码解码等

  总的说来,防范一般的SQL注入只要在代码规范上下点功夫就可以了。凡涉及到执行的SQL中有变量时,可以使用数据持久层提供的内置方法即可,切记不要用拼接字符串的方法;或者对所传参数进行过滤或转换处理。

posted @ 2021-01-31 19:02  古兰精  阅读(1587)  评论(0编辑  收藏  举报