Как зашифровать и расшифровать строку в 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, но это не сработало для меня.

6 177

6 ответов:

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

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

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

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

используйте libsodium, если у вас есть PECL доступ (или sodium_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. не сжать что-нибудь. Шифротекст не сжимается; сжатие открытого текста до шифрования может привести к утечке информации (например, преступление и нарушение на TLS).
  2. убедитесь, что вы используете mb_strlen() и mb_substr(), С помощью '8bit' режим набора символов для предотвращения mbstring.func_overload вопросы.
  3. IVs должны генерировать с помощью CSPRNG; если вы используете mcrypt_create_iv(),НЕ ИСПОЛЬЗОВАТЬ MCRYPT_RAND!
  4. если вы не используете конструкцию AEAD,всегда шифровать, то MAC!
  5. bin2hex(),base64_encode() и т. д. может произойти утечка информации о ваших ключах шифрования через время кэширования. Избегайте их, если это возможно.

даже если вы будете следовать приведенным здесь советам, многое может пойти не так с криптографией. всегда есть криптографическая экспертиза вашей реализации. Если вам не посчастливилось быть личными друзьями со студентом криптографии в вашем местном университете, вы всегда можете попробовать Cryptography Stack Exchange форум для получения советов.

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

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

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

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

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

пример шифрования строки PHP с Libsodium

если вы находитесь на PHP sodium_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);

Галит-Libsodium Сделано Проще

один из проектов, над которым я работал, - это библиотека шифрования под названием галита, который стремится сделать 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);

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

если вы используете Библиотека разрядки:

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

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

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

/**
 * 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-символьный пароль. Ваш ключ шифрования будет комично сломан.

чего не делать

предупреждение:
Этот ответ использует ЕЦБ. ЕЦБ-это не режим шифрования, это всего лишь строительный блок. Использование ЕЦБ, как показано в этом ответе, на самом деле не шифрует строку надежно. Не используйте ЕЦБ в своем коде. Смотрите Скотт для хорошего решения.

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

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");
$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;
}
?>

Я опаздываю на вечеринку, но в поисках правильного способа сделать это я наткнулся на эту страницу, это был один из лучших результатов поиска Google, поэтому я хотел бы поделиться своим мнением о проблеме, которую я считаю актуальной на момент написания этого сообщения (начало 2017 года). Из PHP 7.1.0 the 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 функция.

для Laravel framework

Если вы используете 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 символов в поле ключевая опция конфигурации / приложения.PHP-файл. В противном случае зашифрованные значения не будет безопасно.

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

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

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

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

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


В идеале у вас есть - или вы можете получить-доступ к библиотеке mcrypt PHP, так как она, безусловно, популярна и очень полезны различные задания. Вот бег вниз различных видов шифрования и некоторые примеры кода:методы шифрования в 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) Если данные действительно чувствительны, например, номера кредитной карты или номера социального страхования, остановитесь; вам нужно больше, чем любой простой кусок кода, но вам нужна крипто-библиотека для этого и предназначены значительные объемы времени на исследование необходимых методов. Кроме того, программное обеспечение crypto, вероятно, составляет

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

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

Если вы не хотите использовать библиотеку (что вы должны) , то используйте что-то вроде этого (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);