Я в настоящее время студент, и я изучаю PHP, я пытаюсь сделать простой шифрование / дешифрование данных в PHP. Я сделал некоторые онлайн-исследования, и некоторые из них были довольно запутанными (по крайней мере, для меня).
Вот что я пытаюсь сделать:
У меня есть таблица, состоящая из этих полей (UserID, Fname, Lname, Email, Password)
Я хочу, чтобы все поля были зашифрованы и затем расшифрованы (возможно ли использовать sha256
для шифрования / дешифрования, если не алгоритм шифрования)
Еще одна вещь, которую я хочу узнать, – это создать односторонний hash(sha256)
сочетании с хорошей «солью». (В принципе, я просто хочу иметь простую реализацию шифрования / дешифрования, hash(sha256)+salt)
Sir / Ma'am, ваши ответы будут очень полезны и очень оценены. Спасибо ++
Начиная с определения вашей таблицы:
- UserID - Fname - Lname - Email - Password - IV
Вот изменения:
Lname
, Lname
и Email
будут зашифрованы с использованием симметричного шифра, предоставляемого OpenSSL , IV
будет сохранен вектор инициализации, используемый для шифрования. Требования к хранению зависят от используемого шифрования и используемого режима; об этом позже. Password
будет хешировано с помощью одностороннего хеша пароля, Шифр и режим
Выбор лучшего шифрования и режима шифрования выходит за рамки этого ответа, но окончательный выбор влияет на размер ключа шифрования и вектора инициализации; для этого сообщения мы будем использовать AES-256-CBC с фиксированным размером блока 16 байт и размером ключа 16, 24 или 32 байта.
Ключ шифрования
Хорошим ключом шифрования является двоичный blob, который генерируется из надежного генератора случайных чисел. Следующий пример рекомендуется (> = 5.3):
$key_size = 32; // 256 bits $encryption_key = openssl_random_pseudo_bytes($key_size, $strong); // $strong will be true if the key is crypto safe
Это можно сделать один или несколько раз (если вы хотите создать цепочку ключей шифрования). Держите их как можно более закрытыми.
IV
Вектор инициализации добавляет случайность к шифрованию и требуется для режима CBC. Эти значения должны быть идеально использованы только один раз (технически один раз за ключ шифрования), поэтому обновление любой части строки должно его регенерировать.
Предоставляется функция, которая поможет вам генерировать IV:
$iv_size = 16; // 128 bits $iv = openssl_random_pseudo_bytes($iv_size, $strong);
Давайте зашифруем поле имени, используя более ранние $encryption_key
и $iv
; Чтобы сделать это, мы должны заполнить наши данные размером блока:
function pkcs7_pad($data, $size) { $length = $size - strlen($data) % $size; return $data . str_repeat(chr($length), $length); } $name = 'Jack'; $enc_name = openssl_encrypt( pkcs7_pad($name, 16), // padded data 'AES-256-CBC', // cipher and mode $encryption_key, // secret key 0, // options (not used) $iv // initialisation vector );
Зашифрованный вывод, как и IV, является двоичным; сохранение этих значений в базе данных может быть выполнено с использованием определенных типов столбцов, таких как BINARY
или VARBINARY
.
Выходное значение, такое как IV, является двоичным; для хранения этих значений в MySQL, рассмотрите использование столбцов BINARY
или VARBINARY
. Если это не вариант, вы также можете преобразовать двоичные данные в текстовое представление с использованием base64_encode()
или bin2hex()
, для этого требуется от 33 до 100% большего объема памяти.
Расшифровка сохраненных значений аналогична:
function pkcs7_unpad($data) { return substr($data, 0, -ord($data[strlen($data) - 1])); } $row = $result->fetch(PDO::FETCH_ASSOC); // read from database result // $enc_name = base64_decode($row['Name']); // $enc_name = hex2bin($row['Name']); $enc_name = $row['Name']; // $iv = base64_decode($row['IV']); // $iv = hex2bin($row['IV']); $iv = $row['IV']; $name = pkcs7_unpad(openssl_decrypt( $enc_name, 'AES-256-CBC', $encryption_key, 0, $iv ));
Вы можете дополнительно улучшить целостность сгенерированного текста шифрования, добавив подпись, созданную из секретного ключа (отличного от ключа шифрования) и шифрованного текста. Перед расшифровкой шифрованного текста подпись сначала проверяется (предпочтительно с методом сравнения по времени).
// generate once, keep safe $auth_key = openssl_random_pseudo_bytes(32, $strong); // authentication $auth = hash_hmac('sha256', $enc_name, $auth_key, true); $auth_enc_name = $auth . $enc_name; // verification $auth = substr($auth_enc_name, 0, 32); $enc_name = substr($auth_enc_name, 32); $actual_auth = hash_hmac('sha256', $enc_name, $auth_key, true); if (hash_equals($auth, $actual_auth)) { // perform decryption }
См. Также: hash_equals()
Сохранять обратимый пароль в вашей базе данных следует избегать как можно больше; вы просто хотите проверить пароль, а не знать его содержимое. Если пользователь теряет свой пароль, лучше разрешить им перезагружать его, а не отправлять их в исходное (убедитесь, что сброс пароля может выполняться только в течение ограниченного времени).
Применение хэш-функции – односторонняя операция; впоследствии его можно безопасно использовать для проверки без выявления исходных данных; для паролей метод грубой силы – это приемлемый подход, чтобы выявить его из-за относительно короткой длины и низкого выбора пароля для многих людей.
Хеширующие алгоритмы, такие как MD5 или SHA1, были сделаны для проверки содержимого файла с известным значением хэша. Они сильно оптимизированы, чтобы сделать эту проверку как можно быстрее, но при этом быть точным. Учитывая их относительно ограниченное пространство на выходе, было легко создать базу данных с известными паролями и их соответствующими выходами хэшей, радужными таблицами.
Добавление соли к паролю перед хэшированием сделало бы таблицу радуги бесполезной, но недавние аппаратные улучшения, сделанные грубой силой, выглядят жизнеспособным подходом. Вот почему вам нужен алгоритм хэширования, который преднамеренно медленный и просто невозможно оптимизировать. Он также должен иметь возможность увеличивать нагрузку на более быстрое оборудование, не влияя на возможность проверки существующих хэшей паролей, чтобы сделать их будущими доказательствами.
В настоящее время доступны два популярных варианта:
В этом ответе будет использоваться пример с bcrypt.
поколение
Хэш пароля может быть сгенерирован следующим образом:
$password = 'my password'; $random = openssl_random_pseudo_bytes(18); $salt = sprintf('$2y$%02d$%s', 13, // 2^n cost factor substr(strtr(base64_encode($random), '+', '.'), 0, 22) ); $hash = crypt($password, $salt);
Соль генерируется с помощью openssl_random_pseudo_bytes()
чтобы сформировать случайную комбинацию данных, которая затем выполняется через base64_encode()
и strtr()
чтобы соответствовать требуемому алфавиту [A-Za-z0-9/.]
.
Функция crypt()
выполняет хеширование на основе алгоритма ( $2y$
для Blowfish), фактор стоимости (коэффициент 13 принимает примерно 0,40 с на машине 3GHz) и соль 22 символа.
Проверка
После того, как вы выберете строку, содержащую пользовательскую информацию, вы подтвердите пароль таким образом:
$given_password = $_POST['password']; // the submitted password $db_hash = $row['Password']; // field with the password hash $given_hash = crypt($given_password, $db_hash); if (isEqual($given_hash, $db_hash)) { // user password verified } // constant time string compare function isEqual($str1, $str2) { $n1 = strlen($str1); if (strlen($str2) != $n1) { return false; } for ($i = 0, $diff = 0; $i != $n1; ++$i) { $diff |= ord($str1[$i]) ^ ord($str2[$i]); } return !$diff; }
Чтобы проверить пароль, вы снова вызываете crypt()
но вы передаете ранее вычисленный хеш в качестве значения соли. Возвращаемое значение дает тот же хеш, если данный пароль соответствует хешу. Чтобы проверить хеш, часто рекомендуется использовать функцию сравнения по времени, чтобы избежать атак тайминга.
Хеширование паролей с помощью PHP 5.5
В PHP 5.5 были введены функции хэширования пароля , которые можно использовать для упрощения описанного выше метода хеширования:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 13]);
И проверка:
if (password_verify($given_password, $db_hash)) { // password valid }
См. Также: password_hash()
, password_verify()
Я думаю, что на это был дан ответ раньше … но в любом случае, если вы хотите шифровать / дешифровать данные, вы не можете использовать SHA256
//Key $key = 'SuperSecretKey'; //To Encrypt: $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, 'I want to encrypt this', MCRYPT_MODE_ECB); //To Decrypt: $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_ECB);
Чтобы понять этот вопрос, вы должны сначала понять, что такое SHA256. SHA256 – это криптографическая хэш-функция . Криптографическая хэш-функция – это односторонняя функция, выход которой криптографически защищен. Это означает, что легко вычислить хэш (эквивалент шифрования данных), но трудно получить исходный ввод с использованием хэша (что эквивалентно расшифровке данных). Поскольку использование криптографической хеш-функции означает, что дешифрование является вычислительно неосуществимым, поэтому вы не можете выполнять дешифрование с помощью SHA256.
То, что вы хотите использовать, – это двухсторонняя функция, а более конкретно – блок-шифр . Функция, которая позволяет шифровать и дешифровать данные. Функции mcrypt_encrypt
и mcrypt_decrypt
по умолчанию используют алгоритм Blowfish. Использование PHP mcrypt можно найти в этом руководстве . Также существует список определений шифров для выбора шифрования mcrypt. Вики на Blowfish можно найти в Википедии . Блок-шифр шифрует ввод в блоках известного размера и позиции известным ключом, чтобы впоследствии данные могли быть дешифрованы с помощью ключа. Это то, что SHA256 не может предоставить вам.
$key = 'ThisIsTheCipherKey'; $ciphertext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, 'This is plaintext.', MCRYPT_MODE_CFB); $plaintext = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB);
Вот пример использования openssl_encrypt
//Encryption: $textToEncrypt = "My Text to Encrypt"; $encryptionMethod = "AES-256-CBC"; $secretHash = "encryptionhash"; $iv = mcrypt_create_iv(16, MCRYPT_RAND); $encryptedText = openssl_encrypt($textToEncrypt,$encryptionMethod,$secretHash, 0, $iv); //Decryption: $decryptedText = openssl_decrypt($encryptedText, $encryptionMethod, $secretHash, 0, $iv); print "My Decrypted Text: ". $decryptedText;
Мне потребовалось некоторое время, чтобы понять, как не получить false
при использовании openssl_decrypt()
и получить шифрование и дешифрование работы.
// cryptographic key of a binary string 16 bytes long (because AES-128 has a key size of 16 bytes) $encryption_key = '58adf8c78efef9570c447295008e2e6e'; // example $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); $encrypted = openssl_encrypt($plaintext, 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, $iv); $encrypted = $encrypted . ':' . base64_encode($iv); // decrypt to get again $plaintext $parts = explode(':', $encrypted); $decrypted = openssl_decrypt($parts[0], 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, base64_decode($parts[1]));
Если вы хотите передать зашифрованную строку через URL-адрес, вам нужно указать urlencode строку:
$encrypted = urlencode($encrypted);
Чтобы лучше понять, что происходит, читайте:
Для генерации 16-байтных ключей вы можете использовать:
$bytes = openssl_random_pseudo_bytes(16); $hex = bin2hex($bytes);
Чтобы увидеть сообщения об ошибках openssl, вы можете использовать: echo openssl_error_string();
Надеюсь, это поможет.