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

186

Решение

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

Чтобы реализовать аутентифицированное шифрование, вы хотите зашифровать, а затем 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. Не сжимайте ничего никогда. Зашифрованный текст не сжимается; сжатие открытого текста перед шифрованием может привести к утечке информации (например, CRIME и BREACH на 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()и т. д. может привести к утечке информации о ваших ключах шифрования через тайминги кеша. Избегайте их, если это возможно.

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

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

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

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

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

Если вы на PHP < 7.2 или иным образом не имеет установленного libsodium, вы можете использовать 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);

Halite — Libsodium Made Easy

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

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

347

Другие решения

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

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

Я получил это на себе. На самом деле я нашел ответ на 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;
}
?>
44

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

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

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

39

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

$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. В противном случае, зашифрованные значения
не будет в безопасности.

12

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

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

6

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