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

Как определить функцию PHP при нажатии кнопки на форме

Web

Что это значит: ошибка 720 при подключении к VPN, можно ли исправить

Все, что нужно знать об атаке на WordPress — меры предосторожности
Web

Все, что нужно знать об атаке на WordPress — меры предосторожности

Web

Как изменить mysql на mysqli?

×