Как вы шифруете и расшифровываете строку PHP?

Что я имею в виду:

Original String + Salt or Key --> Encrypted String
Encrypted String + Salt or Key --> Decrypted (Original String)

Может быть что-то вроде:

"hello world!" + "ABCD1234" --> Encrypt --> "2a2ffa8f13220befbe30819047e23b2c" (may be, for e.g)
"2a2ffa8f13220befbe30819047e23b2c" --> Decrypt with "ABCD1234" --> "hello world!"
  • В PHP, как вы можете это сделать?

Попытался использовать Crypt_Blowfish, но для меня это не сработало.

Ответ 1

Обновлено

PHP 7 готовая версия. Он использует функцию openssl_encrypt из PHP библиотеки OpenSSL.

class Openssl_EncryptDecrypt {
    function encrypt ($pure_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $encryption_key, $options, $iv);
        $hmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        return $iv.$hmac.$ciphertext_raw;
    }
    function decrypt ($encrypted_string, $encryption_key) {
        $cipher     = 'AES-256-CBC';
        $options    = OPENSSL_RAW_DATA;
        $hash_algo  = 'sha256';
        $sha2len    = 32;
        $ivlen = openssl_cipher_iv_length($cipher);
        $iv = substr($encrypted_string, 0, $ivlen);
        $hmac = substr($encrypted_string, $ivlen, $sha2len);
        $ciphertext_raw = substr($encrypted_string, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $encryption_key, $options, $iv);
        $calcmac = hash_hmac($hash_algo, $ciphertext_raw, $encryption_key, true);
        if(function_exists('hash_equals')) {
            if (hash_equals($hmac, $calcmac)) return $original_plaintext;
        } else {
            if ($this->hash_equals_custom($hmac, $calcmac)) return $original_plaintext;
        }
    }
    /**
     * (Optional)
     * hash_equals() function polyfilling.
     * PHP 5.6+ timing attack safe comparison
     */
    function hash_equals_custom($knownString, $userString) {
        if (function_exists('mb_strlen')) {
            $kLen = mb_strlen($knownString, '8bit');
            $uLen = mb_strlen($userString, '8bit');
        } else {
            $kLen = strlen($knownString);
            $uLen = strlen($userString);
        }
        if ($kLen !== $uLen) {
            return false;
        }
        $result = 0;
        for ($i = 0; $i < $kLen; $i++) {
            $result |= (ord($knownString[$i]) ^ ord($userString[$i]));
        }
        return 0 === $result;
    }
}

define('ENCRYPTION_KEY', '__^%&[email protected]$&*[email protected]#$%^&*^__');
$string = "This is the original string!";

$OpensslEncryption = new Openssl_EncryptDecrypt;
$encrypted = $OpensslEncryption->encrypt($string, ENCRYPTION_KEY);
$decrypted = $OpensslEncryption->decrypt($encrypted, ENCRYPTION_KEY);

Ответ 2

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

Чтобы реализовать аутентифицированное шифрование, вы хотите зашифровать, а затем MAC. Порядок шифрования и аутентификации очень важен! Один из существующих ответов на этот вопрос сделал эту ошибку; как и многие криптографические библиотеки, написанные на PHP.

Вам следует избегать реализации собственной криптографии и вместо этого использовать защищенную библиотеку, написанную и проверенную экспертами по криптографии.

Обновление: PHP 7.2 теперь предоставляет libsodium ! Для большей безопасности обновите свои системы, чтобы они использовали PHP 7.2 или выше и следуйте только советам libsodium в этом ответе.

Используйте libsodium, если у вас есть доступ к PECL (илиodium_compat, если вы хотите использовать libsodium без PECL); иначе...
Используйте defuse/php-шифрование ; не катите свою собственную криптографию!

Обе библиотеки, указанные выше, позволяют легко и безболезненно внедрить аутентифицированное шифрование в ваши собственные библиотеки.

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

Шифрование:

  1. Шифрование с использованием AES в режиме CTR. Вы также можете использовать GCM (что устраняет необходимость в отдельном MAC). Кроме того, ChaCha20 и Salsa20 (предоставляемые libsodium) являются потоковыми шифрами и не нуждаются в специальных режимах.
  2. Если вы не выбрали GCM выше, вы должны аутентифицировать зашифрованный текст с HMAC-SHA-256 (или, для потоковых шифров, Poly1305 - большинство API libsodium делают это за вас). MAC должен охватывать IV, а также зашифрованный текст!

Дешифрирование:

  1. Если не используется Poly1305 или GCM, пересчитайте MAC зашифрованного текста и сравните его с MAC, который был отправлен с помощью hash_equals(). Если это не удается, прервать.
  2. Расшифруйте сообщение.

Другие вопросы дизайна:

  1. Не сжимайте ничего никогда. Зашифрованный текст не сжимается; сжатие открытого текста перед шифрованием может привести к утечке информации (например, CRIME и BREACH в TLS).
  2. Убедитесь, что вы используете mb_strlen() и mb_substr(), используя режим набора символов '8bit' mb_substr(), чтобы предотвратить проблемы mbstring.func_overload.
  3. IVs должны генерироваться с использованием CSPRNG; Если вы используете mcrypt_create_iv(), НЕ ИСПОЛЬЗУЙТЕ MCRYPT_RAND !
  4. Если вы не используете конструкцию AEAD, ВСЕГДА шифруйте, а затем MAC!
  5. bin2hex(), base64_encode() и т.д. может привести к утечке информации о ваших ключах шифрования через время кэширования. Избегайте их, если это возможно.

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

Если вам нужен профессиональный анализ вашей реализации, вы всегда можете нанять уважаемую команду консультантов по безопасности для проверки вашего криптографического кода PHP (раскрытие: мой работодатель).

Важно: когда не следует использовать шифрование

Не шифруйте пароли. Вместо этого вы хотите их хешировать, используя один из следующих алгоритмов хеширования паролей:

Никогда не используйте универсальную хэш-функцию (MD5, SHA256) для хранения пароля.

Не шифруйте параметры URL. Это неправильный инструмент для работы.

Пример строкового шифрования PHP с помощью Libsodium

Если вы используете PHP <7.2 или у вас нет установленной библиотеки libsodium, вы можете использовать natal_compat для достижения того же результата (хотя и медленнее).

<?php
declare(strict_types=1);

/**
 * Encrypt a message
 * 
 * @param string $message - message to encrypt
 * @param string $key - encryption key
 * @return string
 * @throws RangeException
 */
function safeEncrypt(string $message, string $key): string
{
    if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
        throw new RangeException('Key is not the correct size (must be 32 bytes).');
    }
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

    $cipher = base64_encode(
        $nonce.
        sodium_crypto_secretbox(
            $message,
            $nonce,
            $key
        )
    );
    sodium_memzero($message);
    sodium_memzero($key);
    return $cipher;
}

/**
 * Decrypt a message
 * 
 * @param string $encrypted - message encrypted with safeEncrypt()
 * @param string $key - encryption key
 * @return string
 * @throws Exception
 */
function safeDecrypt(string $encrypted, string $key): string
{   
    $decoded = base64_decode($encrypted);
    $nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');

    $plain = sodium_crypto_secretbox_open(
        $ciphertext,
        $nonce,
        $key
    );
    if (!is_string($plain)) {
        throw new Exception('Invalid MAC');
    }
    sodium_memzero($ciphertext);
    sodium_memzero($key);
    return $plain;
}

Затем, чтобы проверить это:

<?php
// This refers to the previous code block.
require "safeCrypto.php"; 

// Do this once then store it somehow:
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';

$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Halite - Libsodium Made Easy

Один из проектов, над которым я работаю, - это библиотека шифрования Halite, цель которой - сделать libsodium более простым и интуитивно понятным.

<?php
use \ParagonIE\Halite\KeyFactory;
use \ParagonIE\Halite\Symmetric\Crypto as SymmetricCrypto;

// Generate a new random symmetric-key encryption key. You're going to want to store this:
$key = new KeyFactory::generateEncryptionKey();
// To save your encryption key:
KeyFactory::save($key, '/path/to/secret.key');
// To load it again:
$loadedkey = KeyFactory::loadEncryptionKey('/path/to/secret.key');

$message = 'We are all living in a yellow submarine';
$ciphertext = SymmetricCrypto::encrypt($message, $key);
$plaintext = SymmetricCrypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Вся криптография лежит в основе libsodium.

Пример с defuse/php-шифрованием

<?php
/**
 * This requires https://github.com/defuse/php-encryption
 * php composer.phar require defuse/php-encryption
 */

use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

require "vendor/autoload.php";

// Do this once then store it somehow:
$key = Key::createNewRandomKey();

$message = 'We are all living in a yellow submarine';

$ciphertext = Crypto::encrypt($message, $key);
$plaintext = Crypto::decrypt($ciphertext, $key);

var_dump($ciphertext);
var_dump($plaintext);

Примечание: Crypto::encrypt() возвращает вывод в шестнадцатеричном формате.

Управление ключами шифрования

Если у вас возникнет желание использовать "пароль", остановитесь прямо сейчас. Вам нужен случайный 128-битный ключ шифрования, а не запоминающийся пароль.

Вы можете хранить ключ шифрования для долгосрочного использования, например, так:

$storeMe = bin2hex($key);

И, по запросу, вы можете получить его так:

$key = hex2bin($storeMe);

Я настоятельно рекомендую просто хранить случайно сгенерированный ключ для долгосрочного использования вместо любого пароля в качестве ключа (или для получения ключа).

Если вы используете библиотеку Defuse:

"Но я действительно хочу использовать пароль".

Это плохая идея, но хорошо, вот как это сделать безопасно.

Сначала сгенерируйте случайный ключ и сохраните его в константе.

/**
 * Replace this with your own salt! 
 * Use bin2hex() then add \x before every 2 hex characters, like so:
 */
define('MY_PBKDF2_SALT', "\x2d\xb7\x68\x1a\x28\x15\xbe\x06\x33\xa0\x7e\x0e\x8f\x79\xd5\xdf");

Обратите внимание, что вы добавляете дополнительную работу и можете просто использовать эту константу в качестве ключа и избавить себя от душевных страданий!

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

/**
 * Get an AES key from a static password and a secret salt
 * 
 * @param string $password Your weak password here
 * @param int $keysize Number of bytes in encryption key
 */
function getKeyFromPassword($password, $keysize = 16)
{
    return hash_pbkdf2(
        'sha256',
        $password,
        MY_PBKDF2_SALT,
        100000, // Number of iterations
        $keysize,
        true
    );
}

Не просто используйте 16-значный пароль. Ваш ключ шифрования будет комично взломан.

Ответ 3

Я опаздываю на вечеринку, но в поисках правильного способа сделать это я наткнулся на эту страницу, она была одной из самых популярных результатов поиска Google, поэтому я хотел бы поделиться своим взглядом на проблему, которую я считаю до даты написания этого поста (начало 2017 года). Начиная с PHP 7.1.0, mcrypt_decrypt и mcrypt_encrypt устаревают, поэтому при создании кода будущего развития следует использовать openssl_encrypt и openssl_decrypt

Вы можете сделать что-то вроде:

$string_to_encrypt="Test";
$password="password";
$encrypted_string=openssl_encrypt($string_to_encrypt,"AES-128-ECB",$password);
$decrypted_string=openssl_decrypt($encrypted_string,"AES-128-ECB",$password);

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

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

Ответ 4

Что не делать

ПРЕДУПРЕЖДЕНИЕ:
В этом ответе используется ECB. ECB не является режимом шифрования, это только строительный блок. Использование ECB, как показано в этом ответе, фактически не шифрует строку безопасно. Не используйте ECB в своем коде. См. ответ Скотта для хорошего решения.

Я получил это сам. На самом деле я нашел ответ на Google и только что изменил что-то. Результат совершенно небезопасен.

<?php
define("ENCRYPTION_KEY", "[email protected]#$%^&*");
$string = "This is the original data string!";

echo $encrypted = encrypt($string, ENCRYPTION_KEY);
echo "<br />";
echo $decrypted = decrypt($encrypted, ENCRYPTION_KEY);

/**
 * Returns an encrypted & utf8-encoded
 */
function encrypt($pure_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
    return $encrypted_string;
}

/**
 * Returns decrypted original string
 */
function decrypt($encrypted_string, $encryption_key) {
    $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, $encryption_key, $encrypted_string, MCRYPT_MODE_ECB, $iv);
    return $decrypted_string;
}
?>

Ответ 5

Для рамки Laravel

Если вы используете Laravel framework, тогда его проще шифровать и расшифровывать с помощью внутренних функций.

$string = 'Some text to be encrypted';
$encrypted = \Illuminate\Support\Facades\Crypt::encrypt($string);
$decrypted_string = \Illuminate\Support\Facades\Crypt::decrypt($encrypted);

var_dump($string);
var_dump($encrypted);
var_dump($decrypted_string);

Примечание. Обязательно установите случайную строку 16, 24 или 32 символа в ключевой вариант файла config/app.php. В противном случае зашифрованные значения не будет безопасным.

Ответ 6

Историческая заметка: Это было написано во время PHP4. Это то, что мы теперь называем "устаревшим кодом".

Я оставил этот ответ в исторических целях, но некоторые из методов теперь устарели, метод шифрования DES не рекомендуется и т.д.

Я не обновил этот код по двум причинам: 1) я больше не работаю с методами шифрования вручную на PHP, и 2) этот код по-прежнему служит той цели, для которой он предназначался: продемонстрировать минимальную, упрощенную концепцию того, как шифрование может работать в PHP.

Если вы найдете аналогично упрощенное, источник PHP-шифрования для чайников, который может заставить людей запускаться в 10-20 строк кода или меньше, дайте мне знать в комментариях.

Помимо этого, пожалуйста, наслаждайтесь этим классическим эпизодом минималистичного ответа на шифрование PHP4 от ранней эры.


В идеале у вас есть - или вы можете получить доступ к библиотеке PHP mcrypt, так как это, безусловно, популярно и очень полезно для множества задач. Здесь прогоняются различные виды шифрования и некоторый пример кода: Методы шифрования в PHP

//Listing 3: Encrypting Data Using the mcrypt_ecb Function 

<?php 
echo("<h3> Symmetric Encryption </h3>"); 
$key_value = "KEYVALUE"; 
$plain_text = "PLAINTEXT"; 
$encrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $plain_text, MCRYPT_ENCRYPT); 
echo ("<p><b> Text after encryption : </b>"); 
echo ( $encrypted_text ); 
$decrypted_text = mcrypt_ecb(MCRYPT_DES, $key_value, $encrypted_text, MCRYPT_DECRYPT); 
echo ("<p><b> Text after decryption : </b>"); 
echo ( $decrypted_text ); 
?> 

Несколько предупреждений:

1) Никогда не используйте обратимое или "симметричное" шифрование при использовании одностороннего хэша.

2) Если данные действительно чувствительны, например, номера кредитной карты или социального обеспечения, остановитесь; вам нужно больше, чем любой простой кусок кода, но вам нужна крипто-библиотека, предназначенная для этой цели, и значительное количество времени для исследования необходимых методов. Кроме того, криптование программного обеспечения, вероятно, составляет 10% от безопасности конфиденциальных данных. Это похоже на переделку атомной электростанции - признайте, что эта задача опасна и сложна и не зная, если это произойдет. Финансовые штрафы могут быть огромными, поэтому лучше использовать услугу и нести ответственность за них.

3) Любой вид легко реализуемого шифрования, как указано здесь, может разумно защитить незначительно важную информацию, которую вы хотите сохранить от посторонних глаз, или ограничить воздействие в случае случайной/преднамеренной утечки. Но, видя, как ключ хранится в текстовом виде на веб-сервере, если они могут получить данные, они могут получить ключ дешифрования.

Как бы то ни было, получайте удовольствие:)

Ответ 7

Если вы не хотите использовать библиотеку (что вам нужно), используйте что-то вроде этого (PHP 7):

function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}

$string_to_encrypt='John Smith';
$password='password';
$encrypted_string=encrypt($string_to_encrypt, $password);
$decrypted_string=decrypt($encrypted_string, $password);

Ответ 8

Ниже код работает в php для всей строки со специальным символом

   // Encrypt text --

    $token = "9611222007552";

      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);  
      $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));  
      $crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
    echo    $crypted_token;
    //unset($token, $cipher_method, $enc_key, $enc_iv);

    // Decrypt text  -- 

    list($crypted_token, $enc_iv) = explode("::", $crypted_token);  
      $cipher_method = 'aes-128-ctr';
      $enc_key = openssl_digest(php_uname(), 'SHA256', TRUE);
      $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv));
    echo   $token;
    //unset($crypted_token, $cipher_method, $enc_key, $enc_iv);