資深的 PHP 開發人員 Octavia Andreea Anghel 在 DevX.com 發表了一篇 PHP 加密技術的教學文件,討論如何取得和安裝各種 PHP 加密技術軟件包,以提高你的 PHP 應用程式的安全性。本文是系列的第一部分,第二部分在這裡。 在理想世界,加密和保安技術根本無須存在,但現實世界遠非完美,所以軟件開發商要花費大量時間和金錢,為應用程式設立保安措施,加密只是整個保安拼圖中的 一小塊,其他的尚有 SSL / TLS、數碼證書、數碼簽名等等。本文將會介紹如何用 PHP 來實作最常見的加密算法,除了描述 PHP 預設的加密功能,你也會看到各種各樣加密函式庫和軟件包。
本文所有範例均使用一個短小的純文本檔 textfile.txt,它就是密碼學所謂的「明文」(plaintext),明文加密後便變成「密文」(ciphertext)。本文使用的「明文」如下:
For every difficult and complicated question there is an answer that is simple, easily understood, and wrong. HL Mencken
PHP 預設的加密函式
在預設的安裝模式下,PHP 包含了三個內建的加密函式:md5()、crypt()、和 sha1()。md5() 函式的原型是:
| PHP | | copy code | | ? |
| 1 | string md5(string $str [, bool $raw_output ]) |
這 個函式使用 MD5 訊息摘要算法 (MD5 Message-Digest algorithm) 計算輸入字串的 MD5 散列值,其中引數 $str 是需要加密的字串,若果引數 $raw_output 的值是FALSE (這是預設值),函式會以一個 32 字符的十六進制數字送回散列值,若果該引數的值是 TRUE,函式會送回一個 16 字節長度的原始二進制數字。
至於 crypt() 則是一個單向的加密函式,可讓你把一個輸入的密碼與一個預存的密碼比較,無須經過任何解密程序,crypt() 函式的原型是:
| PHP | | copy code | | ? |
| 1 | string crypt (string $str [, string $salt ]) |
它使用以 Unix 的 DES 為基礎的加密算法(或者操作系統提供的替代算法)來加密輸入的字串,引數 $str 就是要加密的字串,可選引數 $salt 是一個提供給加密程序的字串,若果你不提供 $salt,PHP 將每次隨機產生一個。
sha1() 函式是用來計算一個字串的 SHA-1 散列值,該函式的原型是:
| PHP | | copy code | | ? |
| 1 | string sha1 (string $str [, bool $raw_output ]) |
函式 sha1() 以字串的方式送回輸入字串的 SHA-1 散列值,同樣地,引數 $str 代表輸入字串,若果可選引數 $raw_output 的值是 TRUE,函式會送回一個長度 20 個字節的原始二進制數字;若果引數的值是 FALSE,函式會一個 40 字符的十六進制數字。
下面的範例顯示了如何使用 PHP 的預設加密函式把 textfile.txt 文件加密,並把結果存到 encrypted.txt 檔案中:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | |
| 03 | $file = 'textfile.txt'; |
| 04 | $initial_contents = file_get_contents($file); |
| 05 | |
| 06 | if($initial_contents) { |
| 07 | $password = 'OctaviaAnghel'; |
| 08 | |
| 09 | // 計算 MD5 散列值 |
| 10 | $md5_data = md5($password); |
| 11 | |
| 12 | // 加密數據 |
| 13 | $crypt = crypt($password); |
| 14 | |
| 15 | // 計算 SHA-1 散列值 |
| 16 | $sha1 = sha1($password); |
| 17 | |
| 18 | $encrypted_file = @fopen('encrypted.txt','w'); |
| 19 | $ok_encrypt = @fwrite($encrypted_file,'md5: '. $md5_data. |
| 20 | "\r\n".'crypt: '.$crypt."\r\n".'sha1: '.$sha1); |
| 21 | |
| 22 | if($ok_encrypt) { |
| 23 | echo '成功建立密文檔案 encrypted_file.txt!!!'; |
| 24 | } |
| 25 | else { |
| 26 | echo ("檔案寫入失敗!"); |
| 27 | } |
| 28 | |
| 29 | @fclose($encrypted_file); |
| 30 | } |
| 31 | ?> |
除了內建的函式外,PHP 也支援外部的加密函式庫和軟件包,下表顯示了本文餘下部分將會提及的函式庫和軟件包:
| 軟件包/函式庫 | 描述 |
| MCrypt | MCrypt 提供範圍廣泛的加密功能,可用來加密大型檔案或數據流,你可以在 http://mcrypt.sourceforge.net/ 找到更多資料。 |
| MHash | 使用 MHash 來獲取散列值,MHash 支援最流行的散列算法和實作,包括 SHA、MD5、及 CRC,你可以用這些算法來計算校驗值 (checksum)、訊息摘要 (message digest)、和產生署碼簽署,MHash 經常用來計算輸入 HTML 密碼欄位中密碼的散列值。你可以在 http://mhash.sourceforge.net/ 找到更多 MHash 的資訊。 |
| Crypt_Blowfish | Crypt_Blowfish 可以進行快速雙向加密,可以選擇使用或不使用密鑰,它無需依靠 PHP 的 MCrypt 擴展模組,不過若果已經安裝 MCrypt,Crypt_Blowfish 可以使用它。你可以在 http://pear.php.net/package/Crypt_Blowfish 找到更多細節。 |
| Crypt_RSA | Crypt_RSA 提供 RSA 一樣的密鑰生成、加密/解密、數位簽署及簽署驗證功能,你可以在 http://pear.php.net/package/Crypt_RSA 找到更多資料。 |
| Crypt_HMAC | 這個類可用來計算兼容 RFC 2104 的散列值,你可以在 http://pear.php.net/package/Crypt_HMAC 找到完整的資料。 |
| Crypt_DiffieHellman | 這是一個在 PHP 5 上實作的 Diffie-Hellman 密鑰交換協議,你可以在 http://pear.php.net/package/Crypt_DiffieHellman 找到更多資料。 |
加密大量數據與 MCrypt
即使不是專業的密碼學家,PHP 開發人員也可以使用 MCrypt 中大量的加密函式來加密文件或數據流,MCrypt 支援多種區塊加密算法 (block encryption algorithm),包括 Blowfish、DES、TripleDES、SAFER-SK128、TWOFISH、TEA、RC2、3-WAY、SAFER-SK64,和幾種 「運作模式」,MCrypt 一類的區塊加密軟件針對固定長度的數據塊,一般來說是 64 或 128 bits,由於明文的長度並不是固定的,加上使用相同的密鑰來加密相同的明文,會得到相同的結果,所以專家們發明了數個解決方案允許區塊加密軟件處理任意 長度的明文,這些解決方案稱為「運作模式」,MCrypt 支援的運作模式包括 CBC、CFB、CTR、ECB、OFB、及 NCFB。
MCrypt 的配套函式庫是 Libmcrypt,它提供實際的加密功能,微軟視窗用家可以在這裡下載,Linux 用家則可在這裡下載。
| 原作者註解:若果你使用的是 PHP 5.0.0,你需要 libmcrypt 2.5.6 或更高版本。 |
安裝 libmcrypt:
- 下載 libmcrypt.dll。
- 複製 libcrypt.dll 到 {php_home}/ext 及 {Windows_home}/System32 資料夾。
- 在 php.ini 中把 extension=php_mcrypt.dll 前面的評論符號「;」刪除,啟動這個模組。
- 儲存更新後的 php.ini。
| 原作者註解:使用 Linux 的話,請下載 libmcrypt-x.z.tar.gz 檔案,並遵照附帶的安裝指示安裝。 |
MCrypt 有四種運作模式:CBC、OFB、CFB、及 ECB,若果你使用 libmcrypt-2.4.x 或更高版本,那麼 MCrypt 也可以使用 OFB 及 STREAM 運作模式,下表列出常用的運作模式,與及扼要介紹何時使用甚麼模式。
| 運作模式 | 描述 |
| MCRYPT_MODE_ECB | 適合隨機數據,你可以用這種模式來加密不同的密碼。 |
| MCRYPT_MODE_CBC | 用來加密文件。 |
| MCRYPT_MODE_CFB | 建議用來加密字節流。 |
| MCRYPT_MODE_OFB | 專門用在不容許出錯的應用系統。 |
| MCRYPT_MODE_NOFB | 兼容 OFB,但更加安全。 |
| MCRYPT_MODE_STREAM | 需要資料流算法時使用,例如 WAKE 或 RC4。 |
除了這些運作模式,MCrypt 也支援以下加密算法:
- MCRYPT_3DES
- MCRYPT_ARCFOUR
- MCRYPT_BLOWFISH
- MCRYPT_IDEA (非免費)
- MCRYPT_LOKI97
- MCRYPT_MARS
- MCRYPT_PANAMA
- MCRYPT_RIJNDAEL_128
| 原作者註解:由於 MCrypt 支援的加密算法可能隨時間而改變,請定期檢查這份名單。 |
MCrypt 範例
這裡是一個使用 MCrypt 加密和解密一個文件的範例,程式會把檔案 textfile.txt 加密成為 encrypted.txt,然後把這個檔案解密成為 newfile.txt。
| PHP | | copy code | | ? |
| 01 | // Listing file_encrypt.php |
| 02 | |
| 03 | <?php |
| 04 | $file = 'textfile.txt'; |
| 05 | $initial_contents = file_get_contents($file); |
| 06 | |
| 07 | if($initial_contents) { |
| 08 | // 這個函式開啟 MCrypt 模組,並設定所使用的運作模式 |
| 09 | $td = mcrypt_module_open('tripledes', '', 'ecb', ''); |
| 10 | |
| 11 | // 從一個隨機來源建立一個初始向量 |
| 12 | $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); |
| 13 | |
| 14 | // 這個函式初始化所有加密所需的緩衝器 |
| 15 | mcrypt_generic_init($td, $initial_contents, $iv); |
| 16 | |
| 17 | // 這個函式加密數據 |
| 18 | $encrypted_data = mcrypt_generic($td, $initial_contents); |
| 19 | |
| 20 | $encrypted_file = @fopen('encrypted.txt','w'); |
| 21 | $ok_encrypt = @fwrite($encrypted_file,$encrypted_data); |
| 22 | if($ok_encrypt) { |
| 23 | echo '成功建立密文檔案 encrypted_file.txt!!!'; |
| 24 | } |
| 25 | else { |
| 26 | echo ("檔案寫入失敗!"); |
| 27 | } |
| 28 | |
| 29 | @fclose($encrypted_file); |
| 30 | |
| 31 | mcrypt_generic_init($td, $initial_contents, $iv); |
| 32 | |
| 33 | // 這個函式解密數據 |
| 34 | $p_t = mdecrypt_generic($td, $encrypted_data); |
| 35 | |
| 36 | $newfile = @fopen('newfile.txt','w'); |
| 37 | $ok_decrypt = @fwrite($newfile,$p_t); |
| 38 | if($ok_decrypt) { |
| 39 | echo '成功建立解密文件 newfile.txt!!!'; |
| 40 | } |
| 41 | else { |
| 42 | echo ("檔案寫入失敗!"); |
| 43 | } |
| 44 | @fclose($newfile); |
| 45 | |
| 46 | // 這個函式為加密模組解除初始化 |
| 47 | mcrypt_generic_deinit($td); |
| 48 | |
| 49 | // 關閉加密模組 |
| 50 | mcrypt_module_close($td); |
| 51 | } |
| 52 | ?> |
