Web

Простейшее двустороннее шифрование с использованием PHP

Каков самый простой способ двустороннего шифрования в обычных установках PHP?

Мне нужно иметь возможность шифровать данные с помощью строкового ключа и использовать тот же ключ для дешифрования на другом конце.

Безопасность не так важна, как переносимость кода, поэтому я хотел бы, чтобы все было как можно проще. В настоящее время я использую реализацию RC4, но если мне удастся найти что-то с встроенной поддержкой, я полагаю, что смогу сэкономить много ненужного кода.

 

Ответ 1

Важно: если у вас нет особых условий использования, не шифруйте пароли, вместо этого используйте алгоритм хэширования паролей. Когда кто-то говорит, что шифрует свои пароли в приложении на стороне сервера, он либо не информирован, либо описывает опасную конструкцию системы. Безопасное хранение паролей это совершенно отдельная от шифрования проблема. Будьте информированы. Разрабатывайте безопасные системы.

Портативное шифрование данных в PHP

Если вы используете PHP 5.4 или новее и не хотите писать криптографический модуль самостоятельно, я рекомендую использовать существующую библиотеку, которая обеспечивает аутентифицированное шифрование. Библиотека, на которую я дал ссылку, полагается только на то, что предоставляет PHP, и периодически проверяется несколькими исследователями безопасности. 

Если ваши цели по переносимости не препятствуют необходимости использования расширений PECL, libsodium настоятельно рекомендуется больше, чем все, что вы или я можем написать на PHP.

Также вы можете использовать sodium_compat и использовать те же криптовалюты, которые предлагает libsodium, без установки расширений PECL.

Если вы хотите попробовать свои силы в криптографии, читайте дальше.

 Во-первых, вам следует потратить время на изучение опасностей неаутентифицированного шифрования и принципа криптографического обмана.

  1. Зашифрованные данные все равно могут быть подделаны злоумышленником.

  2. Проверка подлинности зашифрованных данных предотвращает фальсификацию.

  3. Аутентификация незашифрованных данных не предотвращает фальсификацию.

 Шифрование и расшифровка

Шифрование в PHP на самом деле простое (мы будем использовать openssl_encrypt() и openssl_decrypt() после того, как вы примете решение о том, как зашифровать вашу информацию. Обратитесь к openssl_get_cipher_methods() для получения списка методов, поддерживаемых в вашей системе. Лучшим выбором является AES в режиме CTR:

  1. aes-128-ctr

  2. aes-192-ctr

  3. aes-256-ctr

В настоящее время нет оснований полагать, что размер ключа AES является существенной проблемой, о которой стоит беспокоиться (больше, вероятно, не лучше, из-за плохого планирования ключей в 256-битном режиме).

Примечание. Мы не используем mcrypt, потому что он является неподдерживаемым ПО и имеет неисправленные ошибки, которые могут повлиять на безопасность. По этим причинам я рекомендую другим разработчикам PHP избегать его.

Простая обёртка шифрования/дешифрования с использованием OpenSSL:

class UnsafeCrypto {

    const METHOD = 'aes-256-ctr';

    /**

     * Шифрует (но не аутентифицирует) сообщение.

     * 

     * @param string $message - сообщение в открытом виде

     * @param string $key - ключ шифрования (ожидается необработанный двоичный файл)

     * @param boolean $encode - установите значение TRUE, чтобы вернуть base64-кодированное сообщение. 

     * @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

        );

        // Теперь давайте соберем вместе IV и шифротекст.

        // Наивно полагать, что мы можем просто объединить

        if ($encode) {

            return base64_encode($nonce.$ciphertext);

        }

        return $nonce.$ciphertext;

    }

 

    /**

     * Расшифровывает (но не проверяет) сообщение.

     * 

     * @param string $message - шифротекст сообщения

     * @param string $key - ключ шифрования (ожидается необработанный двоичный файл)

     * @param boolean $encoded - ожидается ли кодированная строка?

     * @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);

 

Простая обёртка аутентификации:

class SaferCrypto extends UnsafeCrypto {

    const HASH_ALGO = 'sha256';

    /**

     * Шифрует и затем передает сообщение по MAC-адресу

     * 

     * @param string $message - сообщение в открытом виде

     * @param string $key - ключ шифрования (ожидается необработанный двоичный файл)

     * @param boolean $encode - установите значение TRUE, чтобы вернуть строку в кодировке base64

     * @return string (raw binary)

     */

    public static function encrypt($message, $key, $encode = false) {

        list($encKey, $authKey) = self::splitKeys($key);

        // Передать в UnsafeCrypto::encrypt

        $ciphertext = parent::encrypt($message, $encKey);

        // Вычислить MAC из IV и шифротекста

        $mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);

        if ($encode) {

            return base64_encode($mac.$ciphertext);

        }

        // Добавить MAC к шифротексту и вернуть вызывающему лицу

        return $mac.$ciphertext;

    }

    /**

     * Расшифровывает сообщение (после проверки целостности)

     * 

     * @param string $message - шифротекст сообщения.

     * @param string $key - ключ шифрования (ожидается необработанный двоичный код)

     * @param boolean $encoded - ожидается ли кодированная строка?

     * @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_ALGO

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

        }

        // Передать в UnsafeCrypto::decrypt

        $plaintext = parent::decrypt($ciphertext, $encKey);

        return $plaintext;

    }

    /**

     * Разделяет ключ на два отдельных ключа: один для шифрования

     * и другой для аутентификации

     * 

     * @param string $masterKey (необработанный двоичный ключ)

     * @return array (две необработанные двоичные строки)

     */

    protected static function splitKeys($masterKey) {

        // Вы действительно хотите реализовать HKDF!

        return [

            hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),

            hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)

        ];

    }

    /**

     * Сравнение двух строк без утечки информации о времени.

     * 

     * @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);

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

 

Ответ 2

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

<?php

$message_to_encrypt = "Yoroshikune";

$secret_key                 = "my-secret-key";

$method                       = "aes128";

$iv_length = openssl_cipher_iv_length($method);

$iv = openssl_random_pseudo_bytes($iv_length);

$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);

echo $encrypted_message;

?>

Вот объяснение используемых переменных:

message_to_encrypt : данные, которые вы хотите зашифровать secret_key : это ваш «пароль» для шифрования. Убедитесь, что вы не выбрали что-то слишком простое, и будьте осторожны, чтобы не поделиться своим секретным ключом с другими людьми 

method : метод шифрования. Здесь мы выбрали AES128. 

iv_length и iv : подготовка к шифрованию с использованием байтов 

encrypted_message : переменная, содержащая ваше зашифрованное сообщение.

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

<?php

$message_to_encrypt = "Yoroshikune";

$secret_key                 = "my-secret-key";

$method                      = "aes128";

$iv_length = openssl_cipher_iv_length($method);

$iv = openssl_random_pseudo_bytes($iv_length);

$encrypted_message = openssl_encrypt($message_to_encrypt, $method, $secret_key, 0, $iv);

$decrypted_message = openssl_decrypt($encrypted_message, $method, $secret_key, 0, $iv);

echo $decrypted_message;

?>

Метод расшифровки, предлагаемый openssl_decrypt(), близок к openssl_encrypt().

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

 

Ответ 3

PHP 7.2 полностью отказался от Mcrypt, и теперь шифрование основано на поддерживаемой библиотеке Libsodium. Все ваши потребности в шифровании могут быть в основном решены с помощью библиотеки Libsodium.

// компьютер Васи:

$msg              = “Это пришло от Васи";

$signed_msg = sodium_crypto_sign($msg, $secret_sign_key);

// компьютер Пети:

$original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);

if ($original_msg === false) {

    throw new Exception('Неверная сигнатура');

} else {

    echo $original_msg; // Отображает «Это пришло от Васи».

}

 

Ответ 4

ВАЖНО: этот ответ действителен только для PHP 5, в PHP 7 используются встроенные криптографические функции.

Вот простая, но достаточно безопасная реализация:

  1. AES-256 шифрование в режиме CBC.

  2. PBKDF2 для создания ключа шифрования из обычного пароля.

  3. HMAC для проверки подлинности зашифрованного сообщения.

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

Кто такой веб-разработчик. Описание профессии и ее подводные камни
Web

Кто такой веб-разработчик. Описание профессии и ее подводные камни

Как и на чём лучше писать сайты?
Web

Как и на чём лучше писать сайты?

Web

Создание шаблона проектирования Singleton в PHP5

Web

PHP — объединение или прямая вставка переменных в строку

×