php 安全基礎 第三章 資料庫及SQL

來源:互聯網
上載者:User
 PHP的作用常常是溝通各種資料來源及使用者的橋樑。事實上,有些人認為PHP更像是一個平台而不是一個程式設計語言。基於這些原因,PHP頻繁用於與資料庫的交流。

  PHP可以很好的勝任這個任務,其原因特別是由於它能與很多種資料庫連接。下面列舉了PHP支援的小部分資料庫:

DB2

ODBC

SQLite

InterBase

Oracle

Sybase

MySQL

PostgreSQL

DBM

 

  與任何的遠端資料儲存方式相同,資料庫本身也存在著一些風險。儘管資料庫安全不是本書討論的問題,但資料庫安全是需要時刻注意的,特別是關於如何對待從資料庫讀取作為輸入的資料的問題。

  正如第一章所討論的,所有輸入必需要進行過濾,同時所有的輸出必須要轉義。當處理資料庫時,意味著所有來自資料庫的資料要過濾,所有寫入資料庫的資料要進行轉義。

 

小提示

  常犯的錯誤是忘記了SELECT語句本身是向資料庫傳送的資料。儘管該語句的目的是取得資料,但語句本身則是輸出。

 

  很多PHP開發人員不會去過濾來自資料庫的資料,他們認為資料庫內儲存的是已過濾的資料。雖然這種做法的安全風險是很小的,但是這不是最好的做法,同時我也不推薦這樣做。這種做法是基於對資料庫安全的絕對信任,但同時違反了深度防範的原則。如果惡意資料由於某些原因被注入了資料庫,如果你有過濾機制的話,就能發現並抓住它。請記住,冗餘的安全措施是有價值的,這就是一個很好的例子。

  本章包括了其它幾個需要關心的主題,包括存取權限暴露及SQL注入。SQL注入是需要特別關注的,這是因為在流行的PHP應用中頻繁發現了SQL注入漏洞。

3.1. 存取權限暴露

  資料庫使用中需要關注的主要問題之一是存取權限即使用者名稱及密碼的暴露。在編程中為了方便,一般都會用一個db.inc檔案儲存,如:

CODE:

 

<?php

 

$db_user = 'myuser';

$db_pass = 'mypass';

$db_host = '127.0.0.1';

 

$db = mysql_connect($db_host, $db_user, $db_pass);

 

?>

使用者名稱及密碼都是敏感性資料,是需要特別注意的。他們被寫在源碼中造成了風險,但這是一個無法避免的問題。如果不這麼做,你的資料庫就無法設定使用者名稱和密碼進行保護了。

  如果你讀過http.conf(Apache的設定檔)的預設版本的話,你會發現預設的檔案類型是text/plain(普通文本)。這樣,如果db.inc這樣的檔案被儲存在網站根目錄下時,就引發了風險。所有位於網站根目錄下的資源都有相應的URL,由於Apache沒有定義對.inc尾碼的檔案的處理方式類型,在對這一類檔案進行訪問時,會以普通文本的類型進行返回(預設類型),這樣存取權限就被暴露在客戶的瀏覽器上了。

  為了進一步說明這個風險,考慮一下一個以/www為網站根目錄的伺服器,如果db.inc被儲存在/www/inc,它有了一個自已的URLhttp://example.org/inc/db.inc(假設example.org是主機網域名稱)。通過訪問該URL就可以看到db.inc以文本方式顯示的源檔案。無論你把該檔案儲存在/www哪個子目錄下,都無法避免存取權限暴露的風險。

  對這個問題最好的解決方案是把它儲存在網站根目錄以外的包含目錄中。你無需為了達到包含它們的目的而把它們放至在檔案系統中的特定位置,所有只要做的只是保證Web伺服器對其有讀取許可權。因此,把它們放在網站根目錄下是沒有必要的風險,只要包含檔案還位於網站根目錄下,任何減少風險的努力都是徒勞的。事實上,你只要把必須要通過URL訪問的資源放置在網站根目錄下即可。畢竟這是一個公用的目錄。

 

 

  前面的話題對於SQLite資料庫也有用。把資料庫儲存在目前的目錄下是非常方便的,因為你只要調用檔案名稱而無需指定路徑。但是,把資料庫儲存在網站根目錄下就代表著不必要的風險。如果你沒有採用安全措施防止直接存取的話,你的資料庫就危險了。

如果由於外部因素導致無法做到把所有包含檔案放在網站根目錄之外,你可以在Apache配置成拒絕對.inc資源的請求。

CODE:

 

<Files ~ "\.inc$">

  Order allow,deny

  Deny from all

</Files>

譯註:如果只是因為要舉個例子而這麼寫的話,可以理解,畢竟大家學到了一些手段,但這個例子未免生硬了一點。實際上只要把該檔案更名為db.inc.php就可以了。就好象房子破了個洞而不去修補,卻在外面去造一個更大的房子把破房子套起來一樣。

 

  在第8章中你還可以看到另外一種防止資料庫存取權限暴露的方法,該方法對於共用伺服器環境(在該環境下儘管檔案位於網站根目錄之外,但依然存在暴露的風險)非常有效。

3.2. SQL 注入

  SQL 注入是PHP應用中最常見的漏洞之一。事實上令人驚奇的是,開發人員要同時犯兩個錯誤才會引發一個SQL注入漏洞,一個是沒有對輸入的資料進行過濾(過濾輸入),還有一個是沒有對發送到資料庫的資料進行轉義(轉義輸出)。這兩個重要的步驟缺一不可,需要同時加以特別關注以減少程式錯誤。

  對於攻擊者來說,進行SQL注入攻擊需要思考和實驗,對資料庫方案進行有根有據的推理非常有必要(當然假設攻擊者看不到你的來源程式和資料庫方案),考慮以下簡單的登入表單:

CODE:

 

<form action="/login.php" method="POST">

<p>Username: <input type="text" name="username" /></p>

<p>Password: <input type="password" name="password" /></p>

<p><input type="submit" value="Log In" /></p>

</form>

圖 3-1 給出了該表單在瀏覽器中的顯示。

 

  作為一個攻擊者,他會從推測驗證使用者名稱和密碼的查詢語句開始。通過查看源檔案,他就能開始猜測你的習慣。

 

圖 3-1. 登入表單在瀏覽器中的顯示

 

命名習慣。通常會假設你表單中的欄位名為與資料表中的欄位名相同。當然,確保它們不同未必是一個可靠的安全措施。

  第一次猜測,一般會使用下面例子中的查詢:

CODE:

 

<?php

 

$password_hash = md5($_POST['password']);

 

$sql = "SELECT count(*)

      FROM   users

      WHERE  username = '{$_POST['username']}'

      AND    password = '$password_hash'";

 

?>

使用使用者密碼的MD5值原來是一個通行的做法,但現在並不是特別安全了。最近的研究表明MD5演算法有缺陷,而且大量MD5資料庫降低了MD5反向破解的難度。請訪問http://md5.rednoize.com/ 查看示範。

 

  譯註:原文如此,山東大學教授王小雲的研究表明可以很快的找到MD5的“碰撞”,就是可以產生相同的MD5值的不同兩個檔案和字串。MD5是資訊摘要演算法,而不是密碼編譯演算法,反向破解也就無從談起了。不過根據這個成果,在上面的特例中,直接使用md5是危險的。

 

  最好的保護方法是在密碼上附加一個你自己定義的字串,例如:

CODE:

 

<?php

 

$salt = 'SHIFLETT';

$password_hash = md5($salt . md5($_POST['password'] . $salt));

 

?>

    當然,攻擊者未必在第一次就能猜中,他們常常還需要做一些實驗。有一個比較好的實驗方式是把單引號作為使用者名稱錄入,原因是這樣可能會暴露一些重要訊息。有很多開發人員在Mysql語句執行出錯時會調用函數mysql_error()來報告錯誤。見下面的例子:

CODE:

 

<?php

 

mysql_query($sql) or exit(mysql_error());

 

?>

    雖然該方法在開發中十分有用,但它能向攻擊者暴露重要訊息。如果攻擊者把單引號做為使用者名稱,mypass做為密碼,查詢語句就會變成:

CODE:

 

<?php

 

$sql = "SELECT *

      FROM   users

      WHERE  username = '''

      AND    password = 'a029d0df84eb5549c641e04a9ef389e5'";

 

?>

當該語句發送到MySQL後,系統就會顯示如下錯誤資訊:

 

You have an error in your SQL syntax. Check the manual that corresponds to your

MySQL server version for the right syntax to use near 'WHERE username = ''' AND

password = 'a029d0df84eb55

 

  不費吹灰之力,攻擊者已經知道了兩個欄位名(username和password)以及他們出現在查詢中的順序。除此以外,攻擊者還知道了資料沒有正確進行過濾(程式沒有提示非法使用者名稱)和轉義(出現了資料庫錯誤),同時整個WHERE條件的格式也暴露了,這樣,攻擊者就可以嘗試操縱符合查詢的記錄了。

  在這一點上,攻擊者有很多選擇。一是嘗試填入一個特殊的使用者名稱,以使查詢無論使用者名稱密碼是否符合,都能得到匹配:

 

myuser' or 'foo' = 'foo' --

 

  假定將mypass作為密碼,整個查詢就會變成:

CODE:

 

<?php

 

$sql = "SELECT *

      FROM   users

      WHERE  username = 'myuser' or 'foo' = 'foo' --

      AND    password = 'a029d0df84eb5549c641e04a9ef389e5'";

 

?>

由於中間插入了一個SQL注釋標記,所以查詢語句會在此中斷。這就允許了一個攻擊者在不知道任何合法使用者名稱和密碼的情況下登入。

 

  如果知道合法的使用者名稱,攻擊者就可以該使用者(如chris)身份登入:

 

chris' --

 

  只要chris是合法的使用者名稱,攻擊者就可以控制該帳號。原因是查詢變成了下面的樣子:

CODE:

 

<?php

$sql = "SELECT *

      FROM   users

      WHERE  username = 'chris' --

      AND    password = 'a029d0df84eb5549c641e04a9ef389e5'";

?>

幸運的是,SQL注入是很容易避免的。正如第一章所提及的,你必須堅持過濾輸入和轉義輸出。

  雖然兩個步驟都不能省略,但只要實現其中的一個就能消除大多數的SQL注入風險。如果你只是過濾輸入而沒有轉義輸出,你很可能會遇到資料庫錯誤(合法的資料也可能影響SQL查詢的正確格式),但這也不可靠,合法的資料還可能改變SQL語句的行為。另一方面,如果你轉義了輸出,而沒有過濾輸入,就能保證資料不會影響SQL語句的格式,同時也防止了多種常見SQL注入攻擊的方法。

  當然,還是要堅持同時使用這兩個步驟。過濾輸入的方式完全取決於輸入資料的類型(見第一章的樣本),但轉義用於向資料庫發送的輸出資料只要使用同一個函數即可。對於MySQL使用者,可以使用函數mysql_real_escape_string( ):

CODE:

 

<?php

 

$clean = array();

$mysql = array();

 

$clean['last_name'] = "O'Reilly";

$mysql['last_name'] = mysql_real_escape_string($clean['last_name']);

 

$sql = "INSERT

      INTO   user (last_name)

      VALUES ('{$mysql['last_name']}')";

 

?>

盡量使用為你的資料庫設計的轉義函數。如果沒有,使用函數addslashes( )是最終的比較好的方法。

 

  當所有用於建立一個SQL語句的資料被正確過濾和轉義時,實際上也就避免了SQL注入的風險。

CODE:

 

如果你正在使用支援參數化查詢語句和預留位置的資料庫操作類(如PEAR::DB, PDO等),你就會多得到一層保護。見下面的使用PEAR::DB的例子:

 

CODE:

 

<?php

$sql = 'INSERT

      INTO   user (last_name)

      VALUES (?)';

$dbh->query($sql, array($clean['last_name']));

?>

 

CODE:

 

由於在上例中資料不能直接影響查詢語句的格式,SQL注入的風險就降低了。PEAR::DB會自動根據你的資料庫的要求進行轉義,所以你只需要過濾輸出即可。

如果你正在使用參數化查詢語句,輸入的內容就只會作為資料來處理。這樣就沒有必要進行轉義了,儘管你可能認為這是必要的一步(如果你希望堅持轉義輸出習慣的話)。實際上,這時是否轉義基本上不會產生影響,因為這時沒有特殊字元需要轉換。在防止SQL注入這一點上,參數化查詢語句為你的程式提供了強大的保護。

 

  譯註:關於SQL注入,不得不說的是現在大多虛擬機器主機都會把magic_quotes_gpc選項開啟,在這種情況下所有的用戶端GET和POST的資料都會自動進行addslashes處理,所以此時對字串值的SQL注入是不可行的,但要防止對數字值的SQL注入,如用intval()等函數進行處理。但如果你編寫的是通用軟體,則需要讀取伺服器的magic_quotes_gpc後進行相應處理。

 

3.3. 資料的暴露

  關於資料庫,另外需要關心的一點是敏感性資料的暴露。不管你是否儲存了信用卡號,社會保險號,或其它資料,你還是希望確認資料庫是安全的。

 

雖然資料庫安全已經超出了本書所討論的範圍(也不是PHP開發人員要負責的),但是你可以加密最敏感的資料,這樣只要密鑰不泄露,資料庫的安全問題就不會造成災難性的後果。(關於加密的詳細介紹參見本書附錄C)

  要看圖的話,請至技術文檔區下載原版chm

 

  http://www.phpchina.cn/bbs/viewt ... &extra=page%3D1

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.