PHP安全-加密
作為一本相關(guān)安全方面的書,通常加密是需要提及的話題。我之所以在本書的主體部分忽略了加密問題,是因?yàn)樗挠猛臼仟M窄的,而開發(fā)者應(yīng)從大處著眼來考慮安全問題。過分依賴于加密常常會混淆問題的根源。盡管加密本身是有效的,但是進(jìn)行加密并不會神奇地提高一個應(yīng)用的安全性。
一個PHP開發(fā)人員應(yīng)主要熟悉以下的加密方式:
l對稱加密
l非對稱加密(公鑰)
lHash函數(shù)(信息摘要)
l信息驗(yàn)證碼
本附錄主要關(guān)注于使用mcrypt擴(kuò)展的對稱加密算法。你需要參考的資料如下:
實(shí)用加密技術(shù)(Applied Cryptography), by Bruce Schneier (Wiley)
http://www.schneier.com/blog/
http://wikipedia.org/wiki/Cryptography
http://phpsec.org/articles/2005/password-hashing.html
http://pear.php.net/package/Crypt_HMAC
http://pear.php.net/package/Crypt_RSA
C.1. 密碼的存儲
當(dāng)你在數(shù)據(jù)庫內(nèi)存儲的密碼時,永遠(yuǎn)不要以明碼方式存入,而是應(yīng)該存儲密碼的hash值并同時使用附加字串:
<?php
/* $password contains the password. */
$salt = ’SHIFLETT’;
$password_hash = md5($salt . md5($password . $salt));
/* Store password hash. */
?>
當(dāng)你需要確認(rèn)一個密碼是否正確時,以同樣的方式計算出hash值并比較異同:
<?php
$salt = ’SHIFLETT’;
$password_hash = md5($salt . md5($_POST[’password’] . $salt));
/* Compare password hashes. */
?>
如果hash值完全相同,你就有理由認(rèn)為密碼也是相同的。
如果使用了這個技巧,是不可能告訴用戶他們的密碼是什么的。當(dāng)用戶忘記密碼時,你只能讓他錄入一個新密碼并重新計算hash值存入數(shù)據(jù)庫。當(dāng)然,你需要非常小心地對用戶進(jìn)行身份確認(rèn)——密碼提醒機(jī)制是易受頻繁攻擊的目標(biāo),同時也是經(jīng)常出現(xiàn)安全漏洞的源頭。
C.2. 使用mcrypt
PHP的標(biāo)準(zhǔn)加密擴(kuò)展是mcrypt,它支持很多不同的加密算法。你可以通過mcrypt_list_algorithms( )函數(shù)來查看你的平臺上支持的算法列表:
<?php
echo ’<pre>’ . print_r(mcrypt_list_algorithms(), TRUE) . ’</pre>’;
?>
加密和解密分別由mcrypt_encrypt( ) 及 mcrypt_decrypt( )函數(shù)來實(shí)現(xiàn)。這兩個函數(shù)都有5個參數(shù),第一個參數(shù)是用于指定使用的算法:
<?php
mcrypt_encrypt($algorithm,
$key,
$cleartext,
$mode,
$iv);
mcrypt_decrypt($algorithm,
$key,
$ciphertext,
$mode,
$iv);
?>
其中的加密鍵(第二個參數(shù))是非常敏感的數(shù)據(jù),因此你要確保把它存放在安全的地方。可以用第八章中保護(hù)數(shù)據(jù)庫權(quán)限的方法來保護(hù)加密鍵。如果經(jīng)濟(jì)條件允許的話,硬件加密鍵是最好的選擇,它提供了超級強(qiáng)大的安全性。
函數(shù)有多種模式可供選擇,你可以使用mcrypt_list_modes( )來列出所有支持的模式:
<?php
echo ’<pre>’ . print_r(mcrypt_list_modes(), TRUE) . ’</pre>’;
?>
第五個參數(shù)($iv)為初始化向量,可以使用mcrypt_create_iv( )函數(shù)建立。
下面的示例類提供了基本的加密解密方法:
class crypt
{
private $algorithm;
private $mode;
private $random_source;
public $cleartext;
public $ciphertext;
public $iv;
public function __construct($algorithm = MCRYPT_BLOWFISH,
$mode = MCRYPT_MODE_CBC,
$random_source = MCRYPT_DEV_URANDOM)
{
$this->algorithm = $algorithm;
$this->mode = $mode;
$this->random_source = $random_source;
}
public function generate_iv()
{
$this->iv = mcrypt_create_iv(mcrypt_get_iv_size($this->algorithm,
$this->mode), $this->random_source);
}
public function encrypt()
{
$this->ciphertext = mcrypt_encrypt($this->algorithm,
$_SERVER[’CRYPT_KEY’], $this->cleartext, $this->mode, $this->iv);
}
public function decrypt()
{
$this->cleartext = mcrypt_decrypt($this->algorithm,
$_SERVER[’CRYPT_KEY’], $this->ciphertext, $this->mode, $this->iv);
}
}
?>
上面的類會在其它示例中使用,下面是它的使用方法示例:
<?php
$crypt = new crypt();
$crypt->cleartext = ’This is a string’;
$crypt->generate_iv();
$crypt->encrypt();
$ciphertext = base64_encode($crypt->ciphertext);
$iv = base64_encode($crypt->iv);
unset($crypt);
/* Store $ciphertext and $iv (initialization vector). */
$ciphertext = base64_decode($ciphertext);
$iv = base64_decode($iv);
$crypt = new crypt();
$crypt->iv = $iv;
$crypt->ciphertext = $ciphertext;
$crypt->decrypt();
$cleartext = $crypt->cleartext;
?>
小提示
本擴(kuò)展要求你在編譯PHP時使用-mcrypt標(biāo)識。安裝指南及要求詳見http://php.net/mcrypt。
C.3. 信用卡號的保存
我常常被問到如何安全地保存信用卡號。我的總是會首先詢問他們是否確實(shí)有必要保存信用卡號。畢竟不管具體是如何操作的,引入不必要的風(fēng)險是不明智的。同時國家法律還有關(guān)于信用卡信息處理方面的規(guī)定,我還時刻小心地提醒我并不是一個法律專家。
本書中我并不會專門討論信用卡處理的方法,而是會說明如何保存加密信息到數(shù)據(jù)庫及在讀取時解密。該流程會導(dǎo)致系統(tǒng)性能的下降,但是確實(shí)提供了一層保護(hù)措施。其主要優(yōu)點(diǎn)是如果數(shù)據(jù)庫內(nèi)容泄密暴露出的只是加密信息,但是前提是加密鍵是安全的。因此,加密鍵與加密的實(shí)現(xiàn)方法本身同樣重要。
保存加密數(shù)據(jù)到數(shù)據(jù)的過程是,首先加密數(shù)據(jù),然后通過初始向量與明文建立密文來保存到數(shù)據(jù)庫。由于密文是二進(jìn)制字符串,還需要通過base64_encode( )轉(zhuǎn)換成普通文本字符串以保證二進(jìn)制編碼的安全存儲。
<?php
$crypt = new crypt();
$crypt->cleartext = ’1234567890123456’;
$crypt->generate_iv();
$crypt->encrypt();
$ciphertext = $crypt->ciphertext;
$iv = $crypt->iv;
$string = base64_encode($iv . $ciphertext);
?>
保存該字串至數(shù)據(jù)庫。在讀取時,則是上面流程的逆處理:
<?php
$string = base64_decode($string);
$iv_size = mcrypt_get_iv_size($algorithm, $mode);
$ciphertext = substr($string, $iv_size);
$iv = substr($string, 0, $iv_size);
$crypt = new crypt();
$crypt->iv = $iv;
$crypt->ciphertext = $ciphertext;
$crypt->decrypt();
$cleartext = $crypt->cleartext;
?>
本實(shí)現(xiàn)方法假定加密算法與模式不變。如果它們是不定的話,你還要保存它們以用于解密數(shù)據(jù)。加密鍵是唯一需要保密的數(shù)據(jù)。
C.4. 加密會話數(shù)據(jù)
如果你的數(shù)據(jù)庫存在安全問題,或者部分保存在會話中的數(shù)據(jù)是敏感的,你可能希望加密會話數(shù)據(jù)。除非很有必要,一般我不推薦這樣做,但是如果你覺得在你的情形下需要這樣做的話,本節(jié)提供了一個實(shí)現(xiàn)方法的示例。
這個方案十分簡單。實(shí)際上,在第八章中,已經(jīng)說明了如何通過調(diào)用session_set_save_handler( )來執(zhí)行你自己的會話機(jī)制。通過對保存和讀取數(shù)據(jù)的函數(shù)的少量調(diào)整,你就能加密存入數(shù)據(jù)庫的數(shù)據(jù)及在讀取時解密數(shù)據(jù):
<?php
function _read($id)
{
global $_sess_db;
$algorithm = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_CBC;
$id = mysql_real_escape_string($id);
$sql = 'SELECT data
FROM sessions
WHERE id = ’$id’';
if ($result = mysql_query($sql, $_sess_db))
{
$record = mysql_fetch_assoc($result);
$data = base64_decode($record[’data’]);
$iv_size = mcrypt_get_iv_size($algorithm, $mode);
$ciphertext = substr($data, $iv_size);
$iv = substr($data, 0, $iv_size);
$crypt = new crypt();
$crypt->iv = $iv;
$crypt->ciphertext = $ciphertext;
$crypt->decrypt();
return $crypt->cleartext;
}
return ’’;
}
function _write($id, $data)
{
global $_sess_db;
$access = time();
$crypt = new crypt();
$crypt->cleartext = $data;
$crypt->generate_iv();
$crypt->encrypt();
$ciphertext = $crypt->ciphertext;
$iv = $crypt->iv;
$data = base64_encode($iv . $ciphertext);
$id = mysql_real_escape_string($id);
$access = mysql_real_escape_string($access);
$data = mysql_real_escape_string($data);
$sql = 'REPLACE
INTO sessions
VALUES (’$id’, ’$access’, ’$data’)';
return mysql_query($sql, $_sess_db);
}
相關(guān)文章:
1. PHP字符串前后字符或空格刪除方法介紹2. css進(jìn)階學(xué)習(xí) 選擇符3. XML入門的常見問題(一)4. 將properties文件的配置設(shè)置為整個Web應(yīng)用的全局變量實(shí)現(xiàn)方法5. Laravel操作session和cookie的教程詳解6. jsp實(shí)現(xiàn)登錄界面7. Echarts通過dataset數(shù)據(jù)集實(shí)現(xiàn)創(chuàng)建單軸散點(diǎn)圖8. html小技巧之td,div標(biāo)簽里內(nèi)容不換行9. 淺談SpringMVC jsp前臺獲取參數(shù)的方式 EL表達(dá)式10. 解析原生JS getComputedStyle
