Особисто я хотів би використовувати, mcrypt
як інші публікації. Але є ще багато що зауважити ...
Як зашифрувати та розшифрувати пароль у PHP?
Дивіться нижче про сильний клас, який піклується про все для вас:
Який найбезпечніший алгоритм для шифрування паролів?
найбезпечніший ? будь-хто з них. Найбезпечніший метод, якщо ви збираєтесь шифрувати, - це захист від уразливості розкриття інформації (XSS, віддалене включення тощо). Якщо він виходить, зловмисник може врешті-решт зламати шифрування (жодне шифрування не може бути 100% незворотним без ключа. Як зазначає @NullUserException, це не зовсім вірно. Є деякі схеми шифрування, які неможливо зламати, наприклад OneTimePad ) .
Де я зберігаю приватний ключ?
Що я б робив - це використовувати 3 клавіші. Один постачається користувачем, один - специфічним для додатка, а другий - конкретним для користувача (як сіль). Спеціальний ключ програми може зберігатися в будь-якому місці (у конфігураційному файлі за межами веб-кореня, у змінній навколишнього середовища тощо). Спеціальна для користувача інформація зберігатиметься у стовпці в db поруч із зашифрованим паролем. Користувач, який надав, не зберігатиметься. Потім ви зробите щось подібне:
$key = $userKey . $serverKey . $userSuppliedKey;
Перевага полягає в тому, що будь-яка 2 клавіші може бути порушена без даних. Якщо є атака SQL Injection, вони можуть отримати $userKey
іншу, але не іншу 2. Якщо існує локальний сервер, вони можуть отримати $userKey
і $serverKey
, але не третій $userSuppliedKey
. Якщо вони переможуть користувача гайковим ключем, вони можуть дістати $userSuppliedKey
, але не інші 2 (але знову ж таки, якщо користувача бити гайковим ключем, ви все одно запізнилися).
Замість того, щоб зберігати приватний ключ, чи корисно вимагати від користувачів введення приватного ключа в будь-який час, коли їм знадобиться розшифрований пароль? (Користувачам цієї програми можна довіряти)
Абсолютно. Насправді, це єдиний спосіб, як я це зробив би. В іншому випадку вам потрібно буде зберігати незашифровану версію в довговічному форматі зберігання (спільна пам'ять, наприклад, APC або запам’ятована або у файлі сеансу). Це піддається додатковим компромісам. Ніколи не зберігайте незашифровану версію пароля ні в чому, крім локальної змінної.
Якими способами можна вкрасти та розшифрувати пароль? Що мені потрібно знати?
Будь-яка форма компромісу ваших систем дозволить їм переглядати зашифровані дані. Якщо вони можуть ввести код або потрапити у вашу файлову систему, вони можуть переглядати розшифровані дані (оскільки вони можуть редагувати файли, які дешифрують дані). Будь-яка форма відтворення або атаки MITM також надасть їм повний доступ до залучених ключів. Знюхання необмеженого трафіку HTTP також надасть їм ключі.
Використовуйте SSL для всього трафіку. І переконайтеся, що на сервері нічого не має вразливих ситуацій (CSRF, XSS, SQL Injection, Privilege Escalation, віддалене виконання коду тощо).
Редагувати: Ось реалізація сильного методу шифрування класом 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
cyphers, і 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 байт для солі
- 64 байти для hmac
- довжина даних
- Прокладка так, що довжина даних% 8 == 0
Отже, для 16-символьного джерела даних шифрується 16 символів даних. Таким чином, це означає, що фактичний розмір зашифрованих даних становить 16 байт за рахунок прокладки. Потім додайте 16 байт для солі та 64 байт для hmac, а загальний збережений розмір - 96 байт. Таким чином, у кращому випадку накладні 80 символів, а в гіршому 87-х символьних накладних ...
Я сподіваюся, що це допоможе ...
Примітка: 12.11.12: Я щойно оновив цей клас МНОГО кращим методом шифрування, використовуючи краще виведені ключі та виправляючи генерацію MAC ...