Каков самый простой способ сделать двухстороннее шифрование в общих установках PHP?
Мне нужно иметь возможность шифровать данные с помощью строкового ключа и использовать тот же ключ для расшифровки на другом конце.
Безопасность не так важна, как переносимость кода, поэтому я хотел бы иметь возможность максимально упростить процесс. В настоящее время я использую реализацию RC4, но если я смогу найти что-то изначально поддерживаемое, я полагаю, что могу сэкономить много ненужного кода.
Отредактировано:
Вы действительно должны использовать openssl_encrypt () & openssl_decrypt ()
Как говорит Скотт , Mcrypt не очень хорошая идея, поскольку она не обновлялась с 2007 года.
Существует даже RFC для удаления Mcrypt из PHP – https://wiki.php.net/rfc/mcrypt-viking-funeral
Важно : если у вас нет особого варианта использования, не шифруйте пароли , вместо этого используйте алгоритм хэширования паролей. Когда кто-то говорит, что они шифруют свои пароли в серверном приложении, они либо не проинформированы, либо описывают опасный дизайн системы. Безопасное хранение паролей является совершенно отдельной проблемой при шифровании.
Быть информированным. Проектирование безопасных систем.
Если вы используете PHP 5.4 или новее и не хотите сами писать криптографический модуль, я рекомендую использовать существующую библиотеку, которая обеспечивает аутентифицированное шифрование . Связанная мной библиотека полагается только на то, что предоставляет PHP, и ее периодически проверяют несколько исследователей безопасности. (Включая меня).
Если ваши цели переносимости не препятствуют требованию расширений PECL, libsodium настоятельно рекомендуется по всем, что вы или я можем писать на PHP.
Обновление (2016-06-12): теперь вы можете использовать натрий_компат и использовать одни и те же предложения crypto libsodium без установки расширений PECL.
Если вы хотите попробовать свои силы в криптографической инженерии, читайте дальше.
Во-первых, вы должны потратить время, чтобы изучить опасности неавторизованного шифрования и принцип криптографической судьбы .
Шифрование в PHP на самом деле простое (мы собираемся использовать openssl_encrypt()
и openssl_decrypt()
только вы приняли некоторые решения о том, как зашифровать вашу информацию. openssl_get_cipher_methods()
для списка методов, поддерживаемых вашей системой. выбор – AES в режиме CTR :
aes-128-ctr
aes-192-ctr
aes-256-ctr
В настоящее время нет оснований полагать, что размер ключа AES является серьезной проблемой для беспокойства (больше, вероятно, не лучше, из-за плохого планирования ключей в 256-битном режиме).
Примечание. Мы не используем mcrypt
поскольку он является отказоустойчивым и имеет неповрежденные ошибки, которые могут быть mcrypt
безопасности. Из-за этих причин я рекомендую другим разработчикам PHP избежать этого.
class UnsafeCrypto { const METHOD = 'aes-256-ctr'; /** * Encrypts (but does not authenticate) a message * * @param string $message - plaintext message * @param string $key - encryption key (raw binary expected) * @param boolean $encode - set to TRUE to return a base64-encoded * @return string (raw binary) */ public static function encrypt($message, $key, $encode = false) { $nonceSize = openssl_cipher_iv_length(self::METHOD); $nonce = openssl_random_pseudo_bytes($nonceSize); $ciphertext = openssl_encrypt( $message, self::METHOD, $key, OPENSSL_RAW_DATA, $nonce ); // Now let's pack the IV and the ciphertext together // Naively, we can just concatenate if ($encode) { return base64_encode($nonce.$ciphertext); } return $nonce.$ciphertext; } /** * Decrypts (but does not verify) a message * * @param string $message - ciphertext message * @param string $key - encryption key (raw binary expected) * @param boolean $encoded - are we expecting an encoded string? * @return string */ public static function decrypt($message, $key, $encoded = false) { if ($encoded) { $message = base64_decode($message, true); if ($message === false) { throw new Exception('Encryption failure'); } } $nonceSize = openssl_cipher_iv_length(self::METHOD); $nonce = mb_substr($message, 0, $nonceSize, '8bit'); $ciphertext = mb_substr($message, $nonceSize, null, '8bit'); $plaintext = openssl_decrypt( $ciphertext, self::METHOD, $key, OPENSSL_RAW_DATA, $nonce ); return $plaintext; } }
$message = 'Ready your ammunition; we attack at dawn.'; $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'); $encrypted = UnsafeCrypto::encrypt($message, $key); $decrypted = UnsafeCrypto::decrypt($encrypted, $key); var_dump($encrypted, $decrypted);
Демо : https://3v4l.org/jl7qR
Вышеупомянутая простая библиотека криптографии по-прежнему небезопасна в использовании. Нам нужно аутентифицировать зашифрованные тексты и проверять их перед расшифровкой .
Примечание . По умолчанию UnsafeCrypto::encrypt()
возвращает необработанную двоичную строку. Назовите это так, если вам нужно сохранить его в двоично-безопасном формате (base64-encoded):
$message = 'Ready your ammunition; we attack at dawn.'; $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'); $encrypted = UnsafeCrypto::encrypt($message, $key, true); $decrypted = UnsafeCrypto::decrypt($encrypted, $key, true); var_dump($encrypted, $decrypted);
Демо : http://3v4l.org/f5K93
class SaferCrypto extends UnsafeCrypto { const HASH_ALGO = 'sha256'; /** * Encrypts then MACs a message * * @param string $message - plaintext message * @param string $key - encryption key (raw binary expected) * @param boolean $encode - set to TRUE to return a base64-encoded string * @return string (raw binary) */ public static function encrypt($message, $key, $encode = false) { list($encKey, $authKey) = self::splitKeys($key); // Pass to UnsafeCrypto::encrypt $ciphertext = parent::encrypt($message, $encKey); // Calculate a MAC of the IV and ciphertext $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true); if ($encode) { return base64_encode($mac.$ciphertext); } // Prepend MAC to the ciphertext and return to caller return $mac.$ciphertext; } /** * Decrypts a message (after verifying integrity) * * @param string $message - ciphertext message * @param string $key - encryption key (raw binary expected) * @param boolean $encoded - are we expecting an encoded string? * @return string (raw binary) */ public static function decrypt($message, $key, $encoded = false) { list($encKey, $authKey) = self::splitKeys($key); if ($encoded) { $message = base64_decode($message, true); if ($message === false) { throw new Exception('Encryption failure'); } } // Hash Size -- in case HASH_ALGO is changed $hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit'); $mac = mb_substr($message, 0, $hs, '8bit'); $ciphertext = mb_substr($message, $hs, null, '8bit'); $calculated = hash_hmac( self::HASH_ALGO, $ciphertext, $authKey, true ); if (!self::hashEquals($mac, $calculated)) { throw new Exception('Encryption failure'); } // Pass to UnsafeCrypto::decrypt $plaintext = parent::decrypt($ciphertext, $encKey); return $plaintext; } /** * Splits a key into two separate keys; one for encryption * and the other for authenticaiton * * @param string $masterKey (raw binary) * @return array (two raw binary strings) */ protected static function splitKeys($masterKey) { // You really want to implement HKDF here instead! return [ hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true), hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true) ]; } /** * Compare two strings without leaking timing information * * @param string $a * @param string $b * @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW * @return boolean */ protected static function hashEquals($a, $b) { if (function_exists('hash_equals')) { return hash_equals($a, $b); } $nonce = openssl_random_pseudo_bytes(32); return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce); } }
$message = 'Ready your ammunition; we attack at dawn.'; $key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'); $encrypted = SaferCrypto::encrypt($message, $key); $decrypted = SaferCrypto::decrypt($encrypted, $key); var_dump($encrypted, $decrypted);
Демоны : необработанные двоичные , base64-кодированные
Если кто-то хочет использовать эту библиотеку SaferCrypto
в производственной среде или собственную реализацию тех же концепций, я настоятельно рекомендую обратиться к вашим резидентным криптографам за вторым мнением, прежде чем вы это сделаете. Они смогут рассказать вам о ошибках, о которых я, возможно, даже не подозревал.
Вам будет намного лучше использовать авторитетную криптографическую библиотеку .
Используйте mcrypt_encrypt()
и mcrypt_decrypt()
с соответствующими параметрами. На самом деле легко и прямо, и вы используете проверенный битками пакет шифрования.
РЕДАКТИРОВАТЬ
Через 5 лет и через 4 месяца после этого ответа расширение mcrypt
теперь находится в процессе устаревания и возможного удаления с PHP.
Вот простая, но достаточно надежная реализация:
Код и примеры приведены здесь: https://stackoverflow.com/a/19445173/1387163