作為一本相關安全方面的書,通常加密是需要提及的話題。我之所以在本書的主體部分忽略了加密問題,是因為它的用途是狹窄的,而開發者應從大處著眼來考慮安全問題。過度依賴加密常常會混淆問題的根源。儘管加密本身是有效的,但是進行加密並不會神奇地提高一個應用程式的安全性。
一個PHP開發人員應主要熟悉以下的加密方式:
l
l Hash函數(資訊摘要)
l 資訊驗證碼
本附錄主要著重於使用mcrypt擴充的對稱加密演算法。需要參考的資料如下:
實用加密技術(Applied Cryptography), by Bruce Schneier (Wiley)
http://www.php.cn/
http://www.php.cn/
http://www.php.cn/
http://www.php.cn/
://www.php.cn/
http://www.php.cn/
C.1.密碼的儲存
當你在資料庫內儲存的密碼時,永遠不要以明碼方式存入,而是應該儲存密碼的hash值並同時使用附加字串:
<?php /* $password contains the password. */ $salt = 'SHIFLETT'; $password_hash = md5($salt . md5($password . $salt)); /* Store password hash. */ ?>
<?php
$salt = 'SHIFLETT';
$password_hash = md5($salt .
md5($_POST['password'] . $salt));
/* Compare password hashes. */
?>
<?php echo '<pre class="brush:php;toolbar:false">' . print_r(mcrypt_list_algorithms(), TRUE) . ''; ?>
<?php
mcrypt_encrypt($algorithm,
$key,
$cleartext,
$mode,
$iv);
mcrypt_decrypt($algorithm,
$key,
$ciphertext,
$mode,
$iv);
?>
當你需要確認一個密碼是否正確時,以相同的方式計算hash值並比較異同:
<?php echo '<pre class="brush:php;toolbar:false">' . print_r(mcrypt_list_modes(), TRUE) . ''; ?>
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);
}
}
?>
理由認為密碼也是相同的。
如果使用了這個技巧,是不可能告訴使用者他們的密碼是什麼的。當使用者忘記密碼時,你只能讓他輸入一個新密碼並重新計算hash值存入資料庫。當然,你需要非常小心地對使用者進行身分確認──密碼提醒機制是容易受到頻繁攻擊的目標,同時也是經常出現安全漏洞的來源。
C.2. 使用mcrypt
PHP的標準加密擴充是mcrypt,它支援許多不同的加密演算法。你可以透過mcrypt_list_algorithms( )函數來查看你的平台上支援的演算法列表:
<?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; ?>
)函數來實現。這兩個函數都有5個參數,第一個參數是用來指定使用的演算法:
<?php $crypt = new crypt(); $crypt->cleartext = '1234567890123456'; $crypt->generate_iv(); $crypt->encrypt(); $ciphertext = $crypt->ciphertext; $iv = $crypt->iv; $string = base64_encode($iv . $ciphertext); ?>
其中的加密鍵(第二個參數)是非常敏感的數據,因此你要確保把它存放在安全的地方。可以用第八章保護資料庫權限的方法來保護加密鍵。如果經濟條件允許的話,硬體加密鍵是最好的選擇,它提供了超級強大的安全性。
函數有多種模式可供選擇,你可以使用mcrypt_list_modes(
)來列出所有支援的模式:
<?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; ?>
第五個參數($iv)為初始化向量,並且可以使用以建立函式建立函數。
下面的範例類別提供了基本的加密解密方法:
<?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); }
🎜rrreee🎜🎜🎜🎜🎜🎜rrreee🎜🎜🎜🎜🎜🎜rrreee🎜🎜🎜🎜🎜🎜rrreee
上面的类会在其它示例中使用,下面是它的使用方法示例:
<?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; ?>
小提示
本扩展要求你在编译PHP时使用-mcrypt标识。安装指南及要求详见http://www.php.cn/。
C.3. 信用卡号的保存
我常常被问到如何安全地保存信用卡号。我的总是会首先询问他们是否确实有必要保存信用卡号。毕竟不管具体是如何操作的,引入不必要的风险是不明智的。同时国家法律还有关于信用卡信息处理方面的规定,我还时刻小心地提醒我并不是一个法律专家。
本书中我并不会专门讨论信用卡处理的方法,而是会说明如何保存加密信息到数据库及在读取时解密。该流程会导致系统性能的下降,但是确实提供了一层保护措施。其主要优点是如果数据库内容泄密暴露出的只是加密信息,但是前提是加密键是安全的。因此,加密键与加密的实现方法本身同样重要。
保存加密数据到数据的过程是,首先加密数据,然后通过初始向量与明文建立密文来保存到数据库。由于密文是二进制字符串,还需要通过base64_encode( )转换成普通文本字符串以保证二进制编码的安全存储。
<?php $crypt = new crypt(); $crypt->cleartext = '1234567890123456'; $crypt->generate_iv(); $crypt->encrypt(); $ciphertext = $crypt->ciphertext; $iv = $crypt->iv; $string = base64_encode($iv . $ciphertext); ?>
保存该字串至数据库。在读取时,则是上面流程的逆处理:
<?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; ?>
本实现方法假定加密算法与模式不变。如果它们是不定的话,你还要保存它们以用于解密数据。加密键是唯一需要保密的数据。
C.4. 加密会话数据
如果你的数据库存在安全问题,或者部分保存在会话中的数据是敏感的,你可能希望加密会话数据。除非很有必要,一般我不推荐这样做,但是如果你觉得在你的情形下需要这样做的话,本节提供了一个实现方法的示例。
这个方案十分简单。实际上,在第八章中,已经说明了如何通过调用session_set_save_handler( )来执行你自己的会话机制。通过对保存和读取数据的函数的少量调整,你就能加密存入数据库的数据及在读取时解密数据:
<?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); }
以上就是PHP安全-加密的内容,更多相关内容请关注PHP中文网(www.php.cn)!