Вся информация и фото, взяты с https://codernotes.ru/articles/php/obratimoe-shifrovanie-po-klyuchu-na-php.html
Задача надежного шифрования текстовой информации часто встречается при программировании сайтов. В зашифрованном виде бывает необходимо хранить не только пароли, но и другую информацию. Недавно такая задача встала и у меня. Мне нужна была более-менее надежная функция обратимого шифрования текста по ключу. Почему по ключу? Дело в том, что шифрация без ключа может быть взломана, т.к. большинство алгоритмов шифрования можно найти в интернете и подобрать способ, чтобы получить исходные данные, а шифрация с ключом гораздо более надежная.
Обратимое шифрование на PHP 5 библиотекой MCrypt
Поискав по интернету я нашел целых 2 достаточно коротких в плане количества кода и в тоже время очень надежных способа обратимого шифрования по ключу, которые использует встроенную в php библиотеку Mcrypt.
На подавляющем большинстве хостингов данная библиотека сразу же идет вместе с php. Но если вы администрируете свой сервер и данной библиотеки почему-то вдруг не оказалось в составе php, вы всегда можете ее доустановить командой apt-get install php5-mcrypt для Debian-подобных систем (в т.ч. Mint, Ubuntu) или yum install php-mcrypt для RedHat-подобных систем (в т.ч. Fedora, openSUSE, CentOS) или любым другим способом, который вам нравится (через dpkg, rpm, yast и т.д.).
Затем в папке /etc находите папку php, содержащую ini-файлы расширений, загружаемых php по-умолчанию. Посмотреть путь до этой папки можно в php.ini в разделе «Dynamic Extensions». Это может быть папка /etc/php или /etc/php5/mods-available/ или как у меня на сервере /etc/php.d (вообщем, зависит от настроек php). В этой папке должен присутствовать файл mcrypt.ini. Если его там нет, тогда создайте его с таким содержимым:
; Enable mcrypt extension module
extension=mcrypt.so
После этого можно включить расширение командой php5enmod mcrypt, а затем перезапустить сервер /etc/init.d/apache2 restart для Debian-систем или service httpd restart для RedHat систем. Разумеется, все описанные действия выполняются с правами root-а.
Итак, теперь приведу примеры 2-х способов шифрования по ключу, которые я нашел для себя в интернете.
1-й способ. Обратимое шифрование по произвольному ключу.
1234567891011121314151617181920212223242526 | define( 'ENCRYPTION_KEY' , '_91X:s+{a2Jwb6*J' ); $txt = 'Тестируем обратимое шифрование на php' ; $encrypted = encrypt( $txt , ENCRYPTION_KEY); echo $encrypted . '<br>' ; $decrypted = decrypt( $encrypted , ENCRYPTION_KEY); echo $decrypted ; function encrypt( $decrypted , $key ) { $ekey = hash( 'SHA256' , $key , true); srand(); $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND); if ( strlen ( $iv_base64 = rtrim( base64_encode ( $iv ), '=' )) != 22) return false; $encrypted = base64_encode (mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $ekey , $decrypted . md5( $decrypted ), MCRYPT_MODE_CBC, $iv )); return $iv_base64 . $encrypted ; } function decrypt( $encrypted , $key ) { $ekey = hash( 'SHA256' , $key , true); $iv = base64_decode ( substr ( $encrypted , 0, 22) . '==' ); $encrypted = substr ( $encrypted , 22); $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $ekey , base64_decode ( $encrypted ), MCRYPT_MODE_CBC, $iv ), "\0\4" ); $hash = substr ( $decrypted , -32); $decrypted = substr ( $decrypted , 0, -32); if (md5( $decrypted ) != $hash ) return false; return $decrypted ; } |
2-й способ. Очень надежное обратимое шифрование по шестандцатиричному ключу.
Внимание! Ключ должен быть шестандцатиричным (символы 0123456789ABCDEF) длиной 32 или 64 символа.
12345678910111213141516171819202122232425262728293031323334 | define( 'ENCRYPTION_KEY' , 'e3f080b6edfcf6fff70654021c7c2e43' ); $txt = 'Тестируем обратимое шифрование на php' ; $encrypted = mc_encrypt( $txt , ENCRYPTION_KEY); echo $encrypted . '<br>' ; $decrypted = mc_decrypt( $encrypted , ENCRYPTION_KEY); echo $decrypted ; // Encrypt Function function mc_encrypt( $encrypt , $key ) { $encrypt = serialize( $encrypt ); $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM); $key = pack( 'H*' , $key ); $mac = hash_hmac( 'sha256' , $encrypt , substr (bin2hex( $key ), -32)); $passcrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key , $encrypt . $mac , MCRYPT_MODE_CBC, $iv ); $encoded = base64_encode ( $passcrypt ). '|' . base64_encode ( $iv ); return $encoded ; } // Decrypt Function function mc_decrypt( $decrypt , $key ) { $decrypt = explode ( '|' , $decrypt . '|' ); $decoded = base64_decode ( $decrypt [0]); $iv = base64_decode ( $decrypt [1]); if ( strlen ( $iv )!==mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC)){ return false; } $key = pack( 'H*' , $key ); $decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key , $decoded , MCRYPT_MODE_CBC, $iv )); $mac = substr ( $decrypted , -64); $decrypted = substr ( $decrypted , 0, -64); $calcmac = hash_hmac( 'sha256' , $decrypted , substr (bin2hex( $key ), -32)); if ( $calcmac !== $mac ){ return false; } $decrypted = unserialize( $decrypted ); return $decrypted ; } |
Примечательно, что если выполнить приведенные мной примеры несколько раз, мы увидим каждый раз разные зашифрованные данные, хотя шифровался один и тот же текст. При этом, хоть и зашифрованные данные выглядят по-разному, результат расшифровки всегда один и тот же — исходный текст. Подробнее почитать про библиотеку шифрования можно на php.net. На ее основе можно придумать и свой, уникальный способ обратимого шифрования.
Обратимое шифрование на PHP 7 библиотекой OpenSSL
Функции библиотеки Mcrypt, такие как mcrypt_encrypt и mcrypt_decrypt считаются устаревшими и не рекомендуют их использовать. Начиная с PHP 7.2 библиотеку Mcrypt перенесли в PECL. Вместо MCrypt предлагается использовать openssl_encrypt и openssl_decrypt из библиотеки OpenSSL.
Если всё же хотите подключить MCrypt в PHP 7.2 или выше, чтобы использовать ее функции шифрования, тогда можете посмотреть эту статью.
Однако, вернемся к библиотеке OpenSSL. Эта библиотека содержит множество различных методов и алгоритмов симметричного и асимметричного шифрования. Пока что приведу один пример шифрования этими функциями, взятый с php.net а в будущем, доработаю статью, добавив еще примеры.
1234567891011121314151617181920212223 | define( 'ENCRYPTION_KEY' , 'ab86d144e3f080b61c7c2e43' ); // Encrypt $plaintext = "Тестируем обратимое шифрование на php 7" ; $ivlen = openssl_cipher_iv_length( $cipher = "AES-128-CBC" ); $iv = openssl_random_pseudo_bytes( $ivlen ); $ciphertext_raw = openssl_encrypt( $plaintext , $cipher , ENCRYPTION_KEY, $options =OPENSSL_RAW_DATA, $iv ); $hmac = hash_hmac( 'sha256' , $ciphertext_raw , ENCRYPTION_KEY, $as_binary =true); $ciphertext = base64_encode ( $iv . $hmac . $ciphertext_raw ); echo $ciphertext . '<br>' ; // Decrypt $c = base64_decode ( $ciphertext ); $ivlen = openssl_cipher_iv_length( $cipher = "AES-128-CBC" ); $iv = substr ( $c , 0, $ivlen ); $hmac = substr ( $c , $ivlen , $sha2len =32); $ciphertext_raw = substr ( $c , $ivlen + $sha2len ); $plaintext = openssl_decrypt( $ciphertext_raw , $cipher , ENCRYPTION_KEY, $options =OPENSSL_RAW_DATA, $iv ); $calcmac = hash_hmac( 'sha256' , $ciphertext_raw , ENCRYPTION_KEY, $as_binary =true); if (hash_equals( $hmac , $calcmac )) { echo $plaintext ; } |
Обратите внимание: этот алгоритм будет работать начиная с PHP 5.6 и выше. На предыдущих версиях будет выдавать ошибку из-за функции hash_equals, которая осуществляет сравнение строк нечувствительное к атакам по времени (подробнее про атаки по времени можете почитать на википедии).
Другими альтернативами для шифрования на PHP 7+ являются библиотеки: Libsodium и defuse/php-encryption.