天天看點

深入淺出MySQL SQL中的安全問題SQL 中的安全問題

SQL 中的安全問題

​ 在日常開發過程中,程式員一般隻關心 SQL 是否能實作預期的功能,而對于 SQL 的安全問題一般都不太重視.實際上,如果SQL語句寫作不當,将會給應用系統造成很大的安全隐患,其中最重要的隐患就是 SQL 注入。本章以MySQL為例,将會對 SQL 注入以及相應的防範措施進行詳細的介紹。

SQL 注入簡介

​ 結構化查詢語言(SQL)是一種用來和資料庫互動的文本語言。SQL 注入 (SQL Injection)就是利用某些資料庫的外部接口将使用者資料插入到實際的資料庫操作語言(SQL)中,進而達到入侵資料庫乃至作業系統的目的。它的産生主要是由于程式員對使用者輸入的資料沒有進行嚴格的過濾,導緻非法資料庫查詢語句的執行。

​ SQL 注入攻擊具有很大危害,攻擊者可以利用它讀取、修改或者删除資料庫内的資料,擷取資料庫中的使用者名和密碼等敏感的資訊,甚至可以獲得資料庫管理者的權限,而且,SQL注入也很難防範。網站管理者無法通過安裝系統更新檔或者進行簡單的安全配置進行自我保護,一般的防火牆也無法攔截SQL注入攻擊。

​ 下面的使用者登入驗證程式就是 SQL 注入的一個例子(Java):

​ (1)建立使用者表 user:

create table user (
	userid int(11) not null auto_increment,
    username varchar(20) not null default '',
    password varchar(20) not null default '',
    primary key (userid)
) ENGINE=MyISAM auto_increment=3;
# auto_increment=3; 從3開始
           

​ (2)給使用者表添加一條使用者記錄

mysql> insert into user values (1, 'angel', 'mypass');
Query OK, 1 row affected (0.01 sec)
           

​ (3)驗證使用者登入伺服器:

​ 結果發現,這個可以成功登入系統,因為在SQL語句中,“/*” 或者 “#” 都可以将後面的語句注釋掉。

應用開發中可以采取的應對措施

​ 對于上面提到的SQL注入隐患,後果可想而知是很嚴重的,輕則獲得資料資訊,重則可以将資料進行非法更改。

PrepareStatement + Bind-Variable

​ MySQL伺服器端并不存在共享池的概念,是以在MySQL上使用綁定變量(Bind Variable)最大的好處主要是為了避免SQL注入,增加安全性。下面以Java語言為例。

Class.forName("com.mysql,jdbc.Driver").newInstance();
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "select * from user where username=? and password=?";
System.out.println("source sql statement:" + sql);
PrepareStatement ps = connection.prpareStatement(sql);
System.out.println("before bind variable:" + ps.toString());
ps.set(1, "angel 'or' 1=1");
ps.set(2, "123456");
System.out.println("after bind variable:" + ps.toString());

....    
           

​ 需要注意的是,PrepareStatement 語句是由 JDBC驅動來支援的,在使用 PrepareStatement 語句的時候,僅僅做了簡單的替換和轉義,并不是MySQL提供了 PrepareStatement 的特性。

使用應用程式提供的轉換函數

​ 很多應用程式接口都提供了對特殊字元進行轉換的函數。恰當地使用這些函數,可以防止應用程式使用者輸入使應用程式生成不期望的語句。

  • MySQL C API:使用 mysql_real_escape_string() API 調用。
  • MySQL++ :使用 escape 和 quote 修飾符。
  • PHP:使用 mysql_real_escape_string() 函數(适用于PHP 4.0.3 版本)。從PHP5 開始,可以使用擴充的MySQLI,這是對MySQL新特性的一個擴充支援,其中的一個優點就是支援PrepareStatement。
  • Perl DBI:使用 placeholders 或者 quote() 方法。
  • Ruby DBI:使用 placeholders 或者 quote() 方法。

自定義函數進行校驗

​ 如果現有的轉換函數任然不能滿足要求,則需要自己編寫函數進行輸入驗證。輸入驗證是一個很複雜的問題。輸入驗證的途徑可以分為以下幾種:

  1. 整理資料使之變得有效;
  2. 拒絕已知的非法輸入;
  3. 隻接受已知的合法輸入;

​ 是以,如果想要獲得最好的安全狀态,目前最好的解決辦法就是,對使用者送出或者可能改變的資料進行簡單分類,分别應用正規表達式來對使用者提供的輸入資料進行嚴格的檢測和驗證。

​ 下面采用正規表達式的方法提供一個驗證函數:

​ 已知非法符号有:

​ 其實隻需要過濾非法的符号組合就可以阻止已知形式的攻擊,并且如果發現更新的攻擊符号組合,也可以将這些符号組合添加進來,繼續防範新的攻擊。特别時空格符号和與其産生相同作用的分隔關鍵字的符号,例如“” ,如果能成功過濾這種符号,那麼有很多注入攻擊将不能發生,并且同時也要過濾它們的十六進制表示“%XX”。