Реализация шифрования в PHP: от простых до сложных примеров

Раздел: Разработка на PHP -> Безопасность

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

Основные методы шифрования в PHP

Cовременное симметричное шифрование с использованием Sodium (libsodium)

Расширение Sodium (встроено в PHP 7.2+) является рекомендуемым решением для симметричного шифрования. Оно предоставляет простой API с встроенным управлением ключами, nonce и аутентификацией.

Как организовать шифрование данных без лишних усилий и с максимальной безопасностью?


  <?php
  // Генерация ключа (секретного)
  $key = sodium_crypto_secretbox_keygen();
  // Неповторяющийся nonce (24 байта)
  $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
  $message = 'Это секретные данные';

  // Шифрование
  $ciphertext = sodium_crypto_secretbox($message, $nonce, $key);

  // Расшифровка
  $decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
  if ($decrypted === false) {
      // данные повреждены или ключ неверен
  }
  ?>
  

Access php (доступ к файлам в php)

Возможные проблемы:

  • Nonce не должен повторяться при одном и том же ключе: всегда использовать random_bytes.
  • Ключ необходимо хранить в безопасном месте, не в коде.
  • При повреждении ciphertext расшифровка вернёт false – предусмотреть проверку.

Использование Sodium исключает типичные ошибки (неправильная длина IV, отсутствие аутентификации), что делает его основным выбором для новых проектов.

Как выполнить симметричное шифрование с помощью OpenSSL в режиме AEAD (AES-256-GCM)?

OpenSSL поддерживает аутентифицированное шифрование с дополнительными данными (AEAD). Режим GCM обеспечивает как конфиденциальность, так и целостность.


  <?php
  $plaintext = 'Секрет';
  $key = openssl_random_pseudo_bytes(32); // AES-256
  $ivlen = openssl_cipher_iv_length('aes-256-gcm'); // 12 байт
  $iv = openssl_random_pseudo_bytes($ivlen);
  $tag = ''; // будет заполнено шифрованием

  $ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, '', 16);

  // Расшифровка
  $decrypted = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag);
  ?>
  

Php filter (фильтрация данных в php)

Типичные ошибки:

  • Не сохранён тег аутентификации ($tag). Без него расшифровка становится небезопасной.
  • Размер IV фиксирован: для GCM 12 байт, использование других размеров приводит к ошибкам.
  • Забывают передать OPENSSL_RAW_DATA, и данные кодируются в base64.

Как защитить данные с помощью шифрования на основе пароля (password-based encryption)?

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


  <?php
  $password = 'пароль';
  $salt = random_bytes(16);
  $key = hash_pbkdf2('sha256', $password, $salt, 100000, 32, true);

  $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
  $ciphertext = openssl_encrypt('конфиденциально', 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);

  // Для проверки целостности - добавить HMAC
  $hmac = hash_hmac('sha256', $ciphertext, $key, true);
  ?>
  

Php пароль (работа с паролями в php)

Проблемы и их решения:

  • Отсутствие аутентификации: режим CBC не защищает от подмены. После расшифровки проверять HMAC.
  • Слабый пароль: используйте Argon2id (функция sodium_crypto_pwhash) вместо PBKDF2.
  • Не хранить соль и nonce вместе с зашифрованными данными – это нормальная практика, но они не должны быть секретными.

Как использовать асимметричное шифрование в PHP (RSA с openssl)?

Когда требуется обмен зашифрованными данными без общего секретного ключа, применяют публичный и приватный ключи.


  <?php
  // Генерация пары ключей
  $config = array(
      'private_key_bits' => 2048,
      'private_key_type' => OPENSSL_KEYTYPE_RSA
  );
  $res = openssl_pkey_new($config);
  openssl_pkey_export($res, $privateKey);
  $publicKey = openssl_pkey_get_details($res)['key'];

  // Шифрование открытым ключом
  $plaintext = 'Секрет для получателя';
  openssl_public_encrypt($plaintext, $ciphertext, $publicKey);

  // Расшифровка закрытым ключом
  openssl_private_decrypt($ciphertext, $decrypted, $privateKey);
  ?>
  

Tokens php (токены в php)

Ограничения RSA:

  • Максимальная длина открытого текста зависит от размера ключа (2048 бит ~ 245 байт). Для больших данных использовать гибридное шифрование (шифрование симметричным ключом, который зашифрован RSA).
  • Отсутствие встроенной аутентификации – можно подменить зашифрованное сообщение. Добавлять подпись.
  • Производительность RSA ниже, чем у симметричных алгоритмов.

Как использовать готовую библиотеку defuse/php-encryption для упрощения?

Библиотека defuse/php-encryption (на основе openssl и хэшей) предоставляет простой интерфейс для шифрования строк, автоматически обрабатывая nonce, аутентификацию и управление ключами.


  // composer require defuse/php-encryption
  use Defuse\Crypto\Crypto;
  use Defuse\Crypto\Key;

  $key = Key::createNewRandomKey();
  $ciphertext = Crypto::encrypt('Сообщение', $key);
  $plaintext = Crypto::decrypt($ciphertext, $key);
  

Особенности:

  • Библиотека генерирует случайный nonce и добавляет HMAC, что упрощает код.
  • Ключ должен быть сохранён в виде строки (Key::saveToAsciiSafeString()).
  • При изменении версии библиотеки формат может измениться – учитывать.
- Php пароль mysql (пароль для mysql в php)
- Domain block php (блокировка домена в php)
- Auth php (аутентификация в php)

Дополнительные примеры демонстрируют менее очевидные сценарии и полную отладку результатов.

Расширенные примеры с выводом

Пример 1. Шифрование файла с помощью Sodium (чтение, шифрование, запись)

Пример

<?php
$inputFile = 'secret.txt';
$outputFile = 'secret.enc';
$key = sodium_crypto_secretbox_keygen(); // сохранить в безопасном месте

$plaintext = file_get_contents($inputFile);
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);

file_put_contents($outputFile, $nonce . $ciphertext);

echo "Файл зашифрован: $outputFile\n";
// Вывод: Файл зашифрован: secret.enc

// Расшифровка
$encData = file_get_contents($outputFile);
$nonce = substr($encData, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = substr($encData, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

$decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
if ($decrypted === false) {
    echo "Ошибка расшифровки\n";
} else {
    file_put_contents('secret_dec.txt', $decrypted);
    echo "Файл расшифрован в secret_dec.txt\n";
}
?>
Файл зашифрован: secret.enc
Файл расшифрован в secret_dec.txt

Пример 2. Гибридное шифрование (RSA + AES) для передачи больших сообщений

Пример

<?php
// Предполагается, что открытый ключ получателя известен
$publicKey = '-----BEGIN PUBLIC KEY-----\n...';
$message = 'Длинный текст, который не влезет в RSA за раз';

// 1. Генерация случайного симметричного ключа
$symmetricKey = random_bytes(32); // AES-256

// 2. Шифрование сообщения симметричным ключом (AES-256-GCM)
$iv = random_bytes(12);
$tag = '';
$encMessage = openssl_encrypt(
    $message, 'aes-256-gcm', $symmetricKey, OPENSSL_RAW_DATA, $iv, $tag, '', 16
);

// 3. Шифрование симметричного ключа открытым ключом RSA
openssl_public_encrypt(
    $symmetricKey, $encKey, $publicKey, OPENSSL_PKCS1_OAEP_PADDING
);

// 4. Передача: ($encKey, $iv, $tag, $encMessage)
// Получатель:
// - расшифровывает $encKey своим закрытым ключом
// - использует $symmetricKey, $iv, $tag для расшифровки $encMessage
?>
(нет вывода, только логика)

Пример 3. Шифрование сериализованных данных (массив/объект) с сохранением структуры

Пример

<?php
$data = ['user_id' => 42, 'role' => 'admin', 'expires' => time() + 3600];
$serialized = serialize($data);

$key = sodium_crypto_secretbox_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$encrypted = sodium_crypto_secretbox($serialized, $nonce, $key);

// Можно хранить в сессии или куки
$storable = base64_encode($nonce . $encrypted);

// Расшифровка
$decoded = base64_decode($storable);
$nonce = substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$ciphertext = substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$decSerial = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
$decData = unserialize($decSerial);
print_r($decData);
?>
Array
(
    [user_id] => 42
    [role] => admin
    [expires] => 1718123456
)

Пример 4. Использование sodium_crypto_aead для аутентифицированного шифрования с дополнительными данными (AAD)

Пример

<?php
$key = sodium_crypto_aead_xchacha20poly1305_ietf_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
$plaintext = 'Данные';
$aad = 'метаданные-заголовок'; // не шифруется, но аутентифицируется

$ciphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
    $plaintext, $aad, $nonce, $key
);

// При расшифровке нужно передать тот же AAD
$decrypted = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
    $ciphertext, $aad, $nonce, $key
);

echo $decrypted; // Данные
?>
Данные

Пример 5. Шифрование базы данных на PHP с использованием Sodium (для защиты полей)

Пример

<?php
class ColumnEncryptor
{
    private string $key;

    public function __construct(string $key)
    {
        $this->key = $key;
    }

    public function encrypt(string $value): string
    {
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = sodium_crypto_secretbox($value, $nonce, $this->key);
        return base64_encode($nonce . $ciphertext);
    }

    public function decrypt(string $encoded): ?string
    {
        $decoded = base64_decode($encoded, true);
        if ($decoded === false) return null;
        $nonce = substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $ciphertext = substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $plain = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key);
        return $plain === false ? null : $plain;
    }
}

$encryptor = new ColumnEncryptor(sodium_crypto_secretbox_keygen());
$enc = $encryptor->encrypt('конфиденциальный email@example.com');
echo $enc . "\n";
echo $encryptor->decrypt($enc) . "\n";
?>
K25ub25jZUxhcmdl... (строка base64)
конфиденциальный email@example.com

Шифрование данных в PHP - comments

En
Php encrypt (php)