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中有變量時,可以使用數據持久層提供的內置方法即可,切記不要用拼接字符串的方法;或者對所傳參數進行過濾或轉換處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM