Как вы шифруете и расшифровываете строку 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 ответов


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

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

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

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

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

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

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

безопасность:

  1. шифрование с помощью AES в режиме CTR. Вы также можете использовать GCM (который устраняет необходимость в отдельном MAC). Кроме того, ChaCha20 и Salsa20 (предоставляются libsodium) являются потоковыми шифрами и не нуждаются в специальных режимах.
  2. если вы не выбрали GCM выше, вы должны аутентифицировать зашифрованный текст с помощью HMAC-SHA-256 (или для потоковых шифров, Poly1305 -- большинство Libsodium APIs делают это за вас). 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(), etc. может утечка информации о ваших ключах шифрования через время кэширования. Избегайте их, если это возможно.

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

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


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

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

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

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

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


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