Web

Как зашифровать/расшифровать данные на php

Я сейчас студент и изучаю PHP, я пытаюсь сделать простое шифрование/дешифрование данных в PHP. Я провел несколько онлайн-исследований, и некоторые из них были довольно запутанными (по крайней мере, для меня). Вот, что я пытаюсь сделать.

У меня есть таблица, состоящая из таких полей (UserID, Fname, Lname, Email, Password).

Я хочу, чтобы все поля были зашифрованы, а затем расшифрованы (можно ли использовать sha256 для шифрования/дешифрования, если нет, то какой-нибудь другой алгоритм шифрования).

Еще одна вещь, которую я хочу узнать, это – как создать односторонний хэш (sha256) в сочетании с хорошим «salt». (По сути, я просто хочу иметь простую реализацию шифрования/дешифрования, используя хэш(sha256)+salt).

 

Ответ 1

Предисловие

Начните с определения таблицы:

- UserID

- Fname

- Lname

- Email

- Password

- IV

 

Необходимо выполнить следующие изменения:

  1. Поля FnameLname и Email будут зашифрованы с помощью симметричного шифра, предоставляемой OpenSSL

  2. В IV поле будет храниться вектор инициализации, используемый для шифрования. Требования к хранилищу зависят от используемого шифра и режима; подробнее об этом  позже.

  3. Поле Password будет хэшироваться с использованием одностороннего хэш-пароля.

 

Шифрование

Шифр и режим

Выбор лучшего шифра и режима шифрования выходит за рамки этого ответа, но окончательный выбор влияет на размер, как ключа шифрования, так и вектора инициализации; для этого ответа мы будем использовать AES-256-CBC, который имеет фиксированный размер блока 16 байтов и размер ключа 16, 24 или 32 байта.

Ключ шифрования

Хороший ключ шифрования - это двоичный объект, который генерируется надежным генератором случайных чисел. Рекомендуется следующий пример (PHP > = 5,3):

$key_size = 32; // 256 bits

$encryption_key = openssl_random_pseudo_bytes($key_size, $strong);

// $strong будет истинным, если ключ криптобезопасен

Это можно сделать один или несколько раз (если вы хотите создать цепочку ключей шифрования). Держите их как можно конфиденциальнее.

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), // заполненные данные

    'AES-256-CBC',             // шифр и режим

    $encryption_key,           // секретный ключ

    0,                                   // опции (не используются)

    $iv                                 // вектор инициализации

);

Требования к хранилищу

Выходное значение, как и 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); // чтение из базы данных

// $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

));

Аутентифицированное шифрование

Вы можете дополнительно улучшить целостность сгенерированного зашифрованного текста, добавив подпись, созданную на основе секретного ключа (отличного от ключа шифрования) и зашифрованного текста. Перед расшифровкой зашифрованного текста сначала проверяется подпись (желательно с помощью метода сравнения с постоянным временем).

Пример

// сгенерировать один раз и сохранить 

$auth_key = openssl_random_pseudo_bytes(32, $strong);

// аутентификация

$auth = hash_hmac('sha256', $enc_name, $auth_key, true);

$auth_enc_name = $auth . $enc_name;

// верификация

$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)) {

    // выполнение расшифровки

}

Можно также использовать функцию hash_equals()

Хеширование

Хранение обратимого пароля в базе данных должно быть максимально исключено; вы хотите только проверить пароль, а не знать его содержимое. Если пользователь теряет свой пароль, лучше позволить ему восстановить его, а не высылать ему его оригинал (убедитесь, что восстановление пароля возможно только в течение ограниченного времени). Применение хэш-функции - это односторонняя операция; после этого ее можно безопасно использовать для проверки, не раскрывая исходных данных; в случае паролей метод перебора является реальным подходом для их раскрытия из-за их относительно небольшой длины и плохого выбора паролей многими людьми.

Алгоритмы хэширования, такие как MD5 или SHA1, были созданы для проверки содержимого файла по известному хэш-значению. Они сильно оптимизированы, чтобы сделать эту проверку как можно более быстрой и при этом точной. Учитывая их относительно ограниченное пространство вывода, было бы легко создать базу данных с известными паролями и соответствующими им хэш-выводами - специальные таблицы. Добавление салта к паролю перед его хешированием сделало бы спец. таблицу бесполезной, но недавние аппаратные усовершенствования сделали поиск методом грубой силы жизнеспособным подходом. Вот почему вам нужен алгоритм хэширования, который заведомо медленный и который просто невозможно оптимизировать. Он также должен быть способен увеличивать нагрузку на более быстрое оборудование, не влияя на способность проверять существующие хэши паролей, чтобы сделать этот подход перспективным.

В настоящее время доступны два популярных варианта:

  1. PBKDF2 (функция вывода ключей на основе пароля v2)

  2. bcrypt (он же Blowfish)

В этом ответе будет использоваться пример с 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.40s на машине 3GHz) и салта в 22 символа.

Проверка

После того, как вы выбрали строку, содержащую информацию о пользователе, вы подтверждаете пароль следующим образом:

$given_password = $_POST['password']; // введенный пароль

$db_hash = $row['Password']; // поле с хэшем пароля

$given_hash = crypt($given_password, $db_hash);

if (isEqual($given_hash, $db_hash)) {

    // пароль пользователя прошел проверку

}

// постоянное время сравнения строк

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_hash(), password_verify()

 

Ответ 2

Справочная информация и объяснение

Чтобы понять этот вопрос, вы должны сначала понять, что такое SHA256. SHA256 - это функция криптографического хеширования . Криптографическая хеш-функция - это односторонняя функция, вывод которой криптографически безопасен. Это означает, что легко вычислить хэш (эквивалентно шифрованию данных), но трудно получить исходный ввод с использованием хеша (эквивалент расшифровки данных). Поскольку использование криптографической хеш-функции означает, что дешифрование невозможно с вычислительной точки зрения, поэтому вы не можете выполнить дешифрование с помощью SHA256.

Вы хотите использовать двустороннюю функцию, а точнее блочный шифр. Функция, позволяющая осуществлять как шифрование, так и дешифрование данных. Функции mcrypt_encrypt и mcrypt_decrypt по умолчанию используют алгоритм Blowfish. Использование mcrypt в PHP можно найти в руководстве. Также существует список определений шифров для выбора шифра, который использует mcrypt. Блочный шифр шифрует вход в блоках известного размера и положения с известным ключом, так что данные могут быть позже расшифрованы с помощью ключа. Это то, что 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);

 

Ответ 3

Мне потребовалось довольно много времени, чтобы понять, как не получить false при использовании openssl_decrypt() и получить работающее шифрование и дешифрование.

    // криптографический ключ в виде двоичной строки длиной 16 байт (поскольку AES-128 имеет размер ключа 16 байт)

    $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);

    // расшифровываем, чтобы снова получить $plaintext

    $parts         = explode(':', $encrypted);

    $decrypted = openssl_decrypt($parts[0], 'aes-256-cbc', $encryption_key, OPENSSL_RAW_DATA, base64_decode($parts[1])); 

 

Если вы хотите передать зашифрованную строку через URL-адрес, вам необходимо указать URL-адрес строки:

    $encrypted = urlencode($encrypted);

Чтобы сгенерировать ключи длиной 16 байт, вы можете использовать:

    $bytes = openssl_random_pseudo_bytes(16);

    $hex   = bin2hex($bytes);

Чтобы увидеть сообщения об ошибках openssl, можно использовать: 

echo openssl_error_string();

Надеюсь, это поможет.

 

Ответ 4

Вот пример использования openssl_encrypt

// Шифрование:

$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);

// Дешифровка:

$decryptedText = openssl_decrypt($encryptedText, $encryptionMethod, $secretHash, 0, $iv);

print "My Decrypted Text: ". $decryptedText;

 

Схожие статьи

Как закомментировать строку в HTML: примеры комментирования кода
Web

Как закомментировать строку в HTML: примеры комментирования кода

Web

Функции PHP для идентификации вредоносного кода

Web

Функции startWith() и endWith() в PHP

Какой выбрать CDN для сайта. Оптимизируем скорость загрузки
Web

Какой выбрать CDN для сайта. Оптимизируем скорость загрузки