개인적으로 mcrypt
다른 사람들이 게시 한 것처럼 사용 합니다. 그러나 주목할 것이 훨씬 더 많습니다 ...
PHP에서 암호를 어떻게 암호화하고 해독합니까?
당신을 위해 모든 것을 돌보는 강력한 수업은 아래를 참조하십시오.
비밀번호를 암호화하는 가장 안전한 알고리즘은 무엇입니까?
가장 안전한 가요? 그들 중 하나. 암호화하려는 경우 가장 안전한 방법은 정보 유출 취약성 (XSS, 원격 포함 등)을 방지하는 것입니다. 그것이 유출되면 공격자는 결국 암호화를 해독 할 수 있습니다 (키없이 암호화를 되돌릴 수없는 100 %는 없습니다-@NullUserException이 지적한 것처럼 이것은 완전히 사실이 아닙니다. OneTimePad 와 같이 해독이 불가능한 일부 암호화 체계가 있습니다 ) .
개인 키는 어디에 저장합니까?
내가 할 일은 3 키를 사용하는 것입니다. 하나는 사용자가 제공하고 다른 하나는 응용 프로그램에 따라 다른 하나는 소금과 같은 사용자에 따라 다릅니다. 응용 프로그램 특정 키는 웹 루트 외부의 환경 설정 파일, 환경 변수 등 어디에나 저장할 수 있습니다. 사용자 별 암호는 암호화 된 암호 옆에있는 DB의 열에 저장됩니다. 사용자가 제공 한 것이 저장되지 않았습니다. 그런 다음 다음과 같은 작업을 수행합니다.
$key = $userKey . $serverKey . $userSuppliedKey;
이로 인한 이점은 데이터를 손상시키지 않고 2 개의 키를 손상시킬 수 있다는 것입니다. SQL 주입 공격이 있다면, 그들은을 얻을 수 $userKey
있지만, 다른 2 로컬 서버가 악용이 있다면, 그들이 얻을 수 $userKey
하고 $serverKey
있지만, 셋째 없습니다 $userSuppliedKey
. 그들이 렌치로 사용자를 이길 경우 $userSuppliedKey
다른 2를 얻을 수는 없지만 다시 얻을 수는 있습니다 (그러나 다시 사용자가 렌치로 맞으면 너무 늦습니다).
개인 키를 저장하는 대신 암호를 해독해야 할 때마다 사용자가 개인 키를 입력하도록 요구하는 것이 좋습니다. (이 응용 프로그램의 사용자는 신뢰할 수 있습니다)
물론. 사실, 내가 할 유일한 방법입니다. 그렇지 않으면 암호화되지 않은 버전을 내구성있는 저장소 형식 (APC 또는 memcached와 같은 공유 메모리 또는 세션 파일)으로 저장해야합니다. 그것은 추가 타협에 노출됩니다. 암호화되지 않은 암호 버전을 로컬 변수 이외의 다른 곳에 저장하지 마십시오.
비밀번호를 도난 및 해독 할 수있는 방법은 무엇입니까? 무엇을 알고 있어야합니까?
시스템이 손상되면 암호화 된 데이터를 볼 수 있습니다. 코드를 삽입하거나 파일 시스템에 액세스 할 수있는 경우 해독 된 데이터를 볼 수 있습니다 (데이터를 해독하는 파일을 편집 할 수 있으므로). 모든 형태의 Replay 또는 MITM 공격도 관련된 키에 대한 모든 액세스 권한을 부여합니다. 원시 HTTP 트래픽을 스니핑하면 키가 제공됩니다.
모든 트래픽에 SSL을 사용하십시오. 그리고 서버에 어떤 종류의 취약점 (CSRF, XSS, SQL Injection, Privilege Escalation, Remote Code Execution 등)이 없는지 확인하십시오.
편집 : 강력한 암호화 방법의 PHP 클래스 구현은 다음과 같습니다.
/**
* A class to handle secure encryption and decryption of arbitrary data
*
* Note that this is not just straight encryption. It also has a few other
* features in it to make the encrypted data far more secure. Note that any
* other implementations used to decrypt data will have to do the same exact
* operations.
*
* Security Benefits:
*
* - Uses Key stretching
* - Hides the Initialization Vector
* - Does HMAC verification of source data
*
*/
class Encryption {
/**
* @var string $cipher The mcrypt cipher to use for this instance
*/
protected $cipher = '';
/**
* @var int $mode The mcrypt cipher mode to use
*/
protected $mode = '';
/**
* @var int $rounds The number of rounds to feed into PBKDF2 for key generation
*/
protected $rounds = 100;
/**
* Constructor!
*
* @param string $cipher The MCRYPT_* cypher to use for this instance
* @param int $mode The MCRYPT_MODE_* mode to use for this instance
* @param int $rounds The number of PBKDF2 rounds to do on the key
*/
public function __construct($cipher, $mode, $rounds = 100) {
$this->cipher = $cipher;
$this->mode = $mode;
$this->rounds = (int) $rounds;
}
/**
* Decrypt the data with the provided key
*
* @param string $data The encrypted datat to decrypt
* @param string $key The key to use for decryption
*
* @returns string|false The returned string if decryption is successful
* false if it is not
*/
public function decrypt($data, $key) {
$salt = substr($data, 0, 128);
$enc = substr($data, 128, -64);
$mac = substr($data, -64);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
if (!hash_equals(hash_hmac('sha512', $enc, $macKey, true), $mac)) {
return false;
}
$dec = mcrypt_decrypt($this->cipher, $cipherKey, $enc, $this->mode, $iv);
$data = $this->unpad($dec);
return $data;
}
/**
* Encrypt the supplied data using the supplied key
*
* @param string $data The data to encrypt
* @param string $key The key to encrypt with
*
* @returns string The encrypted data
*/
public function encrypt($data, $key) {
$salt = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
list ($cipherKey, $macKey, $iv) = $this->getKeys($salt, $key);
$data = $this->pad($data);
$enc = mcrypt_encrypt($this->cipher, $cipherKey, $data, $this->mode, $iv);
$mac = hash_hmac('sha512', $enc, $macKey, true);
return $salt . $enc . $mac;
}
/**
* Generates a set of keys given a random salt and a master key
*
* @param string $salt A random string to change the keys each encryption
* @param string $key The supplied key to encrypt with
*
* @returns array An array of keys (a cipher key, a mac key, and a IV)
*/
protected function getKeys($salt, $key) {
$ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$keySize = mcrypt_get_key_size($this->cipher, $this->mode);
$length = 2 * $keySize + $ivSize;
$key = $this->pbkdf2('sha512', $key, $salt, $this->rounds, $length);
$cipherKey = substr($key, 0, $keySize);
$macKey = substr($key, $keySize, $keySize);
$iv = substr($key, 2 * $keySize);
return array($cipherKey, $macKey, $iv);
}
/**
* Stretch the key using the PBKDF2 algorithm
*
* @see http://en.wikipedia.org/wiki/PBKDF2
*
* @param string $algo The algorithm to use
* @param string $key The key to stretch
* @param string $salt A random salt
* @param int $rounds The number of rounds to derive
* @param int $length The length of the output key
*
* @returns string The derived key.
*/
protected function pbkdf2($algo, $key, $salt, $rounds, $length) {
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
$res = $tmp;
for ($j = 1; $j < $rounds; $j++) {
$tmp = hash_hmac($algo, $tmp, $key, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
protected function pad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$padAmount = $length - strlen($data) % $length;
if ($padAmount == 0) {
$padAmount = $length;
}
return $data . str_repeat(chr($padAmount), $padAmount);
}
protected function unpad($data) {
$length = mcrypt_get_block_size($this->cipher, $this->mode);
$last = ord($data[strlen($data) - 1]);
if ($last > $length) return false;
if (substr($data, -1 * $last) !== str_repeat(chr($last), $last)) {
return false;
}
return substr($data, 0, -1 * $last);
}
}
PHP 5.6 :에 추가 된 함수를 사용하고 있습니다 hash_equals
. 5.6보다 낮 으면 이중 HMAC 검증을 사용하여 타이밍 안전 비교 기능 을 구현하는이 대체 기능을 사용할 수 있습니다 .
function hash_equals($a, $b) {
$key = mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);
return hash_hmac('sha512', $a, $key) === hash_hmac('sha512', $b, $key);
}
용법:
$e = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$encryptedData = $e->encrypt($data, $key);
그런 다음 해독하십시오.
$e2 = new Encryption(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$data = $e2->decrypt($encryptedData, $key);
$e2
다른 인스턴스가 데이터를 올바르게 해독한다는 것을 보여주기 위해 두 번째로 사용 했습니다.
이제 어떻게 작동합니까? 왜 다른 솔루션보다 사용합니까?
열쇠
키는 직접 사용되지 않습니다. 대신, 키는 표준 PBKDF2 파생으로 확장됩니다.
암호화에 사용되는 키는 암호화 된 모든 텍스트 블록에 고유합니다. 제공된 키는 "마스터 키"가됩니다. 따라서이 클래스는 암호 및 인증 키에 대한 키 회전을 제공합니다.
중요 사항 :이 $rounds
매개 변수는 충분한 강도의 실제 임의 키 (최소 128 비트 암호화 보안 임의)로 구성됩니다. 암호 또는 비 랜덤 키 (또는 임의의 128 비트 CS 랜덤보다 적은 임의의 키)를 사용하려는 경우이 매개 변수 를 늘려야합니다. 나는 암호에 대해 최소 10000을 제안 할 것입니다 (당신이 감당할수록 더 좋을지라도 런타임에 추가 될 것입니다 ...)
데이터 무결성
- 업데이트 된 버전은 ENCRYPT-THEN-MAC를 사용하는데, 이는 암호화 된 데이터의 신뢰성을 보장하는 훨씬 더 좋은 방법입니다.
암호화 :
- mcrypt를 사용하여 실제로 암호화를 수행합니다. 하나
MCRYPT_BLOWFISH
또는 MCRYPT_RIJNDAEL_128
사이퍼 MCRYPT_MODE_CBC
를 사용하고 모드를 사용하는 것이 좋습니다 . 그것은 충분히 강력하고 여전히 빠릅니다 (암호화 및 암호 해독주기는 내 컴퓨터에서 약 1/2 초가 걸립니다).
이제 첫 번째 목록에서 3을 가리키면 다음과 같은 기능이 제공됩니다.
function makeKey($userKey, $serverKey, $userSuppliedKey) {
$key = hash_hmac('sha512', $userKey, $serverKey);
$key = hash_hmac('sha512', $key, $userSuppliedKey);
return $key;
}
makeKey()
함수 에서 확장 할 수는 있지만 나중에 확장 될 것이기 때문에 실제로 그렇게 큰 포인트는 없습니다.
저장 크기까지는 일반 텍스트에 따라 다릅니다. Blowfish는 8 바이트 블록 크기를 사용하므로 다음과 같은 이점이 있습니다.
- 소금은 16 바이트
- hmac의 경우 64 바이트
- 데이터 길이
- 데이터 길이 % 8 == 0이되도록 채우기
따라서 16 자 데이터 소스의 경우 16 자 데이터가 암호화됩니다. 따라서 실제 암호화 된 데이터 크기는 패딩으로 인해 16 바이트입니다. 그런 다음 salt에 16 바이트를 추가하고 hmac에 64 바이트를 추가하면 총 저장 크기는 96 바이트입니다. 따라서 최대 80 자의 오버 헤드가 발생하고 최악의 경우 87 자의 오버 헤드가 발생합니다.
도움이되기를 바랍니다 ...
참고 : 12/11/12 : 방금 더 나은 파생 키를 사용하고 MAC 생성을 수정하는 훨씬 나은 암호화 방법 으로이 클래스를 업데이트했습니다 ...