2011年5月24日 星期二

PHP 加密

資料加密在我們生活中的地位已 經越來越重要了,尤其是考慮到在網絡上發生的大量 交易和傳輸的大量資料。如果對於採用安全措施有興趣的話,也一定會有興趣瞭解PHP提供的一系列安全功能。在本篇文章中,我們將介紹這些 功能,提供一些基本的用法,以便你能夠為自己的應用軟體中增加安全功能。



預備知識

  在詳細介紹PHP的安全功能之 前,我們需要花點時間來向沒有接觸過這方面內容的讀者介紹一些有關密碼學的基本知識,如果對密碼學的基本概念已經非常熟悉,就可以跳 過去這一部分。



  密碼學可以通俗地被描述為對加/解密的研究和實驗,加密是將易懂的資料轉換為不易懂資料的過程,解密 則是將不易懂的資料轉換為原來易懂資料的過程。不易懂的資料被稱作密碼,易懂的資料被稱作明碼。



  資料的加/解密都需 要一定的算法,這些算法可以非常地簡單,如著名的凱撒碼,但目前的加密算法要相對複雜得多,其中一些利用現有的方法甚至是無法破譯的 。



PHP的加密功能

  只要有一點使用非Windows平台經驗的人可能對crypt()也相當熟悉,這一函數完成被稱作單向加密 的功能,它可以加密一些明碼,但不能夠將密碼轉換為原來的明碼。儘管從表面上來看這似乎是一個沒有什麼用處的功能,但它的確被廣泛用 來保證系統密碼的完整性。因為,單向加密的密碼一旦落入第三方人的手裡,由於不能被還原為明文,因此也沒有什麼大用處。在驗證使用者輸 入的密碼時,使用者的輸入採用的也是單向算法,如果輸入與預存的經加密後的密碼相匹配,則輸入的口信一定是正確的。



  PHP同樣提供了使用其crypt()函數完成單向加密功能的可能性。我將在這裡簡要地介紹該函數:



  string crypt (string input_string [, string salt])

  其中的input_string參數是需要加密的字串,第二個可選的salt是一個位字串,它能夠影響 加密的暗碼,進一步地排除被稱作預計算攻擊的可能性。預設情況下,PHP使用一個2個字元的DES干擾串,如果你的系統使用的是MD5(我將在 以後介紹MD5算法),它會使用一個12個字元的干擾串。順便說一下,可以通過執行下面的命令發現系統將要使用的干擾串的長度:



  print "My system salt size is: ". CRYPT_SALT_LENGTH;

  系統也可能支持其他的加密算法。crypt()支持四 種算法,下面是它支持的算法和相應的salt參數的長度:



算法 Salt長度

CRYPT_STD_DES 2-character (Default)

CRYPT_EXT_DES 9-character

CRYPT_MD5 12-character beginning with $

CRYPT_BLOWFISH 16-character beginning with $



用crypt()實現使用者身份驗證

  作為crypt()函數的一個例子,考慮這樣一種情況,你希望增加一段PHP指令碼程式限 制對一個目錄的訪問,只允許能夠提供正確的使用者名和密碼的使用者訪問這一目錄。我將把資料預存在我喜歡的資料庫MySQL的一個表中。下面我 們以增加這個被稱作members的表開始我們的例子:



mysql>CREATE TABLE members (

  ->username CHAR(14) NOT NULL,

  ->password CHAR(32) NOT NULL,

  ->PRIMARY KEY(username)

  ->);



  然後,我們假定下面的資料已經預存在該表中:



使用者名 密碼

clark keloD1C377lKE

bruce ba1T7vnz9AWgk

peter paLUvRWsRLZ4U



  這些加密的密碼對應的明碼分別是kent、banner和parker。注意一下每個密碼的前二個字母, 這是因為我使用了下面的程式碼,根據密碼的前二個字母增加干擾串的:



$enteredPassword.

$salt = substr($enteredPassword, 0, 2);

$userPswd = crypt($enteredPassword, $salt);

// $userPswd然後就和使用者名一起預存在MySQL 中



  我將使用Apache的密碼-應答認證配置提示使用者輸入使用者名和密碼,一個鮮為人知的有關PHP的訊息是,它可以把Apache 的密碼-應答系統輸入的使用者名和密碼識別為$PHP_AUTH_USER和$PHP_AUTH_PW,我將在身份驗證指令碼中用到這二個變數。花一些時 間仔細閱讀下 面的指令碼,多注意一下其中的解釋,以便更好地理解下面的程式碼:



crypt()和Apache的密碼-應答驗證系統的應用

<?php



$host = "localhost";

$user = "zorro";

$pswd = "hell odolly";

$db = "users";



// Set authorization to False



$authorization = 0;



// Verify that user has entered username and password



if (isset($PHP_AUTH_USER) && isset($PHP_AUTH_PW)) :



mysql_pconnect($host, $user, $pswd) or die("Can\'t connect to MySQL

server!");



mysql_select_db($db) or die("Can\'t select database!");



// Perform the encryption

$salt = substr($PHP_AUTH_PW, 0, 2);

$encrypted_pswd = crypt($PHP_AUTH_PW, $salt);



// Build the query



$query = "SELECT username FROM members WHERE

username = \'$PHP_AUTH_USER\' AND

password = \'$encrypted_pswd\'";



// Execute the query



if (mysql_numrows(mysql_query($query)) == 1) :

$authorization = 1;

endif;



endif;



// confirm authorization



if (! $authorization) :



header(\'WWW-Authenticate: Basic realm="Private"\');

header(\'HTTP/1.0 401 Unauthorized\');

print "You are unauthorized to enter this area.";

exit;



else :



print "This is the secret data!";



endif;



?>



  上面就是一個核實使用者訪問權限的簡單身份驗證系統。在使用crypt()保護重要的機密資料時,記住在預設狀態下使用的 crypt()並不是最安全的,只能用在對安全性要求較低的系統中,如果需要較高的安全性能,就需要我在本篇文章的後面介紹的算法。



  下面我將介紹另一個PHP支持的函數══md5(),這一函數使用MD5散列算法,它有幾種很有趣的用法值得一提:



混編

  一個混編函數可以將一個可變長度的訊息變換為具有固定長度被混編過的輸出,也被稱作「訊息文摘」。這是十分有用的,因為 一個固定長度的字串可以用來檢查文件的完整性和驗證數字簽名以及使用者身份驗證。由於它適合於PHP,PHP內置的md5()混編函數將把一個可 變長度的訊息轉換為128位(32個字元)的訊息文摘。混編的一個有趣的特點是不能通過分析混編後的訊息得到原來的明碼,因為混編後的結果 與原來的明碼內容沒有依賴關係。 即便只改變一個字串中的一個字元,也將使得MD5混編算法計算出二個截然不同的結果。我們首先來看下 表的內容及其相應的結果:





使用md5()混編字串

<?php

$msg = "This is some message that I just wrote";

$enc_msg = md5($msg);

print "hash: $enc_msg ";

?>



  結果:



hash: 81ea092649ca32b5ba375e81d8f4972c

  注意,結果的長度為32個字元。再來看一下下面的表,其中的$msg的值有了一點 微小的變化:



使用md5()對一個稍微變化的字串進行混編

<?php

//注意,message中少了一個s

$msg = "This is some mesage that I just wrote";

$enc_msg = md5($msg);

print "hash2: $enc_msg <br /><br />";

?>



  結果:



hash2: e86cf511bd5490d46d5cd61738c82c0c

  可以 發現,儘管二個結果的長度都是32個字元,但明文中一點微小的變化使得結果發生了很大的變化,因此,混編和md5()函數是檢查資料中微小變 化的一個很好的工具。



  儘管crypt()和md5()各有用處,但二者在功能上都受到一定的限制。在下面的部分中,我們將介紹 二個非常有用的被稱作Mcrypt和Mhash的PHP擴展,將大大拓展PHP使用者在加密方面的選擇。



  儘管我們在上面的小節中說明了 單向加密的重要性,但有時我們可能需要在加密後,再把密碼資料還原成原來的資料,幸運的是,PHP通過Mcrypt擴展庫的形式提供了這種可能 性。



Mcrypt

Mcrypt 2.5.7 Unix | Win32

  Mcrypt 2.4.7是一個功能強大的加密算法擴展庫,它包括有22種算法 ,其中就包括下面的幾種算法:



Blowfish RC2 Safer-sk64 xtea

Cast-256 RC4 Safer-sk128

DES RC4-iv Serpent

Enigma Rijndael-128 Threeway

Gost Rijndael-192 TripleDES

LOKI97 Rijndael-256 Twofish

PanamaSaferplus Wake

安裝:

  在標準的PHP軟體包中不包括Mcrypt,因此需要下載它,下載的地址為:ftp://argeas.cs-net.gr/pub/unix/mcrypt/ 。下載後,按照下面的方法進行編譯,並把它擴充在PHP中:



下載Mcrypt軟體包。

gunzipmcrypt-x.x.x.tar.gz

tar -xvfmcrypt-x.x.x.tar

./configure --disable-posix-threads

make

make install

cd to your PHP directory.

./configure -with-mcrypt=[dir] [--other-configuration-directives]

make

make install

  當然了,根據你的 要求和PHP安裝時與網路服務器軟體的關係,上面的過程可能需要作適當的修改。



使用Mcrypt

  Mcrypt的優點不僅僅 在於其提供的加密算法較多,還在於它可以對資料進行加/解密處理,此外,它還提供了35種處理資料用的函數。儘管對這些函數進行詳細介紹 已經超出了這篇文章的範圍,我還是要就幾個典型的函數作一下簡要的介紹。



  首先,我將介紹如何使用Mcrypt擴展庫對數 據進行加密,然後再介紹如何使用它進行解密。下面的程式碼對這一過程進行了演示,首先是對資料進行加密,然後在瀏覽器上顯示加密後的數 據,並將加密後的資料還原為原來的字串,將它顯示在瀏覽器上。



使用Mcrypt對資料進行加、解密

<?php



// Designate string to be encrypted

$string = "Applied Cryptography, by Bruce Schneier, is

a wonderful cryptography reference.";



// Encryption/decryption key

$key = "Four score and twenty years ago";



// Encryption Algorithm

$cipher_alg = MCRYPT_RIJNDAEL_128;



// Create the initialization vector for added security.

$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg,

MCRYPT_MODE_ECB), MCRYPT_RAND);



// Output original string

print "Original string: $string <p>";



// Encrypt $string

$encrypted_string = mcrypt_encrypt($cipher_alg, $key,

$string, MCRYPT_MODE_CBC, $iv);



// Convert to hexadecimal and output to browser

print "Encrypted string: ".bin2hex($encrypted_string)."<p>";

$decrypted_string = mcrypt_decrypt($cipher_alg, $key,

$encrypted_string, MCRYPT_MODE_CBC, $iv);



print "Decrypted string: $decrypted_string";



?>



執行上面的指令碼將會產生下面的輸出:



Original string: Applied Cryptography, by Bruce Schneier, is a wonderful cryptography reference.



Encrypted string: 02a7c58b1ebd22a9523468694b091e60411cc4dea8652bb8072 34fa06bbfb20e71ecf525f29df58e28f3d9bf541f7ebcecf62b c89fde4d8e7ba1e6cc9ea24850478c11742f5cfa1d23fe22fe8 bfbab5e



Decrypted string: Applied Cryptography, by Bruce Schneier, is a wonderful cryptography reference.



  上面的程式碼中二個最典型的函數是mcrypt_encrypt()和 mcrypt_decrypt(),它們的用途是顯而易見的。我使用了「電報密碼本」模式,Mcrypt提供了幾種加密方式,由於每種加密方式都有可以影 響 密碼安全的特定字元,因此每種模式都需要瞭解。對於沒有接觸過密碼系統的讀者來說,可能對mcrypt_create_iv()函數更有興趣,儘管對這 一函數進行徹底的解釋已經超出了本篇文章的範圍,但我仍然會提到它增加的初始化向量(hence, iv),這一向量可以使每條訊息彼此獨立。 儘管不是所有的模式都需要這一初始化變數,但如果在要求的模式中沒有提供這一變數,PHP就會給出警告訊息。



Mhash擴展庫

http://sourceforge.net/projects/mhash/



  0.8.3版的Mhash擴展庫支持12種混編算法,仔細檢查Mhash v.0.8.3的頭文件mhash.h可以知道,它支持下面的混編算法:



CRC32 HAVAL160 MD5

CRC32B HAVAL192 RIPEMD160

GOST HAVAL224 SHA1

HAVAL128 HAVAL256 TIGER

安裝

  象Mcrypt一 樣,Mhash也沒有包括在PHP軟體包中,對於非Windows使用者而言,下面是安裝過程:



下載Mhash擴展庫

gunzipmhash-x.x.x.tar.gz

tar -xvfmhash-x.x.x.tar

./configure

make

make install

cd <PHP所在的目錄 >

./configure -with-mhash=[dir] [--other-configuration-directives]

make

make install

  象Mcrypt一樣 ,根據PHP在網路服務器軟體上的安裝方式,可能需要對Mhash進行其他的配置。



  對於Windows使用者而言, http://www.php4win.de中有一個很好的包括Mhash擴展庫在內的PHP軟體包。只要下載並進行解壓縮,然後根據其中的readme.first文檔中的指 令進行安裝即可。



使用Mhash

  對訊息進行混編非常簡單,看一下下面的例子:



<?php

$hash_alg = MHASH_TIGER;

$message = "These are the directions to the secret fort. Two steps left, three steps right, and cha chacha.";

$hashed_message = mhash($hash_alg, $message);

print "The hashed message is ". bin2hex($hashed_message);

?>



  執行這一段指令碼程式將得到下面的輸出結果:



The hashed message is 07a92a4db3a4177f19ec9034ae5400eb60d1a9fbb4ade461

   在這裡使用bin2hex()函數的目的是方便我們理解$hashed_message 的輸出,這是因為混編的結果是二進制格式,為了能夠將它轉化為易於理解的格式,必須將它轉換為十六進制格式。



  需要 注意的是,混編是單向功能,其結果不依賴輸入,因此可以公開顯示這一訊息。這一策略通常用於讓使用者比較下載文件和系統管理員提供的文 件,以確保文件的完整性。



   Mhash還有其他一些有用的函數。例如,我需要輸出一個Mhash支持的算法的名字,由於 Mhash支持的所有算法的名字都以MHASH_開頭,因此,可以通過執行如下的程式碼完成這一任務:



<?php

$hash_alg = MHASH_TIGER;

print "This data has been hashed with the".mhash_get_hash_name($hashed_message)."hashing algorithm.";

?>



得到的輸出是:



This data has been hashed with the TIGER hashing algorithm.

關於PHP和加密最後需要注意的一個問題

  關於PHP和加密需要注意的最後的一個重要問題是在服務器和客戶端之間傳輸的資料 在傳輸過程中是不安全的!PHP是一種服務器端技術,不能阻止資料在傳輸過程中洩密。因此,如果想實現一個完整的安全應用,建議選用 Apache-SSL或其他的安全服務器佈置。



結論

  這篇文章介紹了PHP最有用的功能之一══資料加密,不僅討論了PHP內置 的crypt() 和md5()加密函數,還討論了用於資料加密的功能強大的擴展庫══Mcrypt和Mhash。在這篇文章最後,我需要指出的是,一個真正 安全的PHP應用還應該包括安全的服務器,由於PHP是一種服務器端的技術,因此,在資料由客戶端向服務器端進行傳輸時,它不能保證資料的 安全。