보안에 관한 책에서 암호화는 반드시 언급해야 할 주제입니다. 내가 이 책의 주요 부분에서 암호화를 무시하는 이유는 암호화의 사용 범위가 좁고 개발자는 보안을 더 큰 그림에서 생각해야 하기 때문입니다. 암호화에 대한 과도한 의존으로 인해 문제의 원인이 모호해지는 경우가 많습니다. 암호화 자체는 효과적이지만 이를 암호화한다고 해서 마법처럼 애플리케이션이 더 안전해지는 것은 아닙니다.
PHP 개발자는 주로 다음 암호화 방법에 익숙해야 합니다.
l 대칭 암호화
l 비대칭 암호화(공개 키)
l 해시 함수(정보 요약)
l 정보 확인 코드
이 부록은 주로 mcrypt 확장자를 사용하는 대칭 암호화 알고리즘에 중점을 둡니다. 참고하셔야 할 내용은 다음과 같습니다.
Applied Cryptography, by Bruce Schneier (와일리)
http://www.php.cn/
http://www.php.cn/
http://www.php.cn/
http://www.php.cn/
http://www.php.cn/
C.1 비밀번호 보관
데이터베이스에 비밀번호를 저장할 때 비밀번호의 해시를 저장하고 다음 문자열을 사용하세요.
<?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. */ ?>
해시 값이 정확히 동일하다면 비밀번호도 동일하다고 생각할 이유가 있습니다.
이 트릭을 사용하면 사용자에게 비밀번호가 무엇인지 알려주는 것이 불가능합니다. 사용자가 비밀번호를 잊어버린 경우 새 비밀번호를 입력하고 해시 값을 다시 계산하여 데이터베이스에 저장하도록 요청할 수 있습니다. 물론 사용자 인증에 매우 주의해야 합니다. 비밀번호 알림은 공격의 빈번한 대상이자 보안 침해의 빈번한 원인입니다.
C.2 mcrypt를 사용하세요
PHP의 표준 암호화 확장은 다양한 암호화 알고리즘을 지원하는 mcrypt입니다. mcrypt_list_algorithms( ) 기능을 사용하여 플랫폼에서 지원되는 알고리즘 목록을 확인하세요:
<?php echo '<pre class="brush:php;toolbar:false">' . print_r(mcrypt_list_algorithms(), TRUE) . ''; ?>
암호화와 복호화는 각각 mcrypt_encrypt( )와 mcrypt_decrypt( )로 수행됩니다. ) 기능을 구현합니다. 두 함수 모두 5개의 매개변수가 있습니다.
<?php mcrypt_encrypt($algorithm, $key, $cleartext, $mode, $iv); mcrypt_decrypt($algorithm, $key, $ciphertext, $mode, $iv); ?>
암호화 키(두 번째 매개변수)는 매우 민감한 데이터이므로 안전한 곳에 보관하는 것이 좋습니다. 암호화 키는 8장의 데이터베이스 권한 보호 방법을 사용하여 보호할 수 있습니다. 재정 상황이 허락한다면 매우 강력한 보안을 제공하는 하드웨어 암호화 키가 최선의 선택입니다.
이 함수에는 선택할 수 있는 여러 모드가 있으며, mcrypt_list_modes( ) 지원되는 모든 모드를 나열하려면:
<?php echo '<pre class="brush:php;toolbar:false">' . print_r(mcrypt_list_modes(), TRUE) . ''; ?>
5장 매개변수($iv)는 mcrypt_create_iv() 함수를 사용하여 생성할 수 있는 초기화 벡터입니다.
다음 예제 클래스는 기본 암호화 및 복호화 방법을 제공합니다.
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; ?>
小提示
本扩展要求你在编译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)!