безопасность — простейшее двустороннее шифрование с использованием переполнения стека

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

Мне нужно иметь возможность шифровать данные с помощью строкового ключа и использовать тот же ключ для расшифровки на другом конце.

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

193

Решение

Отредактировано:

Вы действительно должны использовать openssl_encrypt () & openssl_decrypt ()

Как Скотт говорит, что Mcrypt не очень хорошая идея, так как он не обновлялся с 2007 года.

Существует даже RFC для удаления Mcrypt из PHP — https://wiki.php.net/rfc/mcrypt-viking-funeral

172

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

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

Быть информированным. Проектирование безопасных систем.

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

Если ваши цели переносимости не мешают требовать расширения PECL, libsodium является высоко рекомендуется по всему, что вы или я могу написать на PHP.

Обновление (2016-06-12): Теперь вы можете использовать sodium_compat и использовать те же предложения crypto libsodium без установки расширений PECL.

Если вы хотите попробовать себя в криптографии, читайте дальше.


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

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

Шифрование и дешифрование

Шифрование в PHP на самом деле просто (мы будем использовать openssl_encrypt() а также openssl_decrypt() как только вы приняли решение о том, как зашифровать вашу информацию. советоваться openssl_get_cipher_methods() для получения списка методов, поддерживаемых в вашей системе. Лучший выбор AES в режиме CTR:

  • aes-128-ctr
  • aes-192-ctr
  • aes-256-ctr

В настоящее время нет оснований полагать, что Размер ключа AES является серьезной проблемой, о которой стоит беспокоиться не лучше из-за плохого планирования ключей в 256-битном режиме).

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

Simple Encryption / Decryption Wrapper с использованием OpenSSL

class UnsafeCrypto
{
const METHOD = 'aes-256-ctr';

/**
* Encrypts (but does not authenticate) a message
*
* @param string $message - plaintext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return a base64-encoded
* @return string (raw binary)
*/
public static function encrypt($message, $key, $encode = false)
{
$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = openssl_random_pseudo_bytes($nonceSize);

$ciphertext = openssl_encrypt(
$message,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);

// Now let's pack the IV and the ciphertext together
// Naively, we can just concatenate
if ($encode) {
return base64_encode($nonce.$ciphertext);
}
return $nonce.$ciphertext;
}

/**
* Decrypts (but does not verify) a message
*
* @param string $message - ciphertext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encoded - are we expecting an encoded string?
* @return string
*/
public static function decrypt($message, $key, $encoded = false)
{
if ($encoded) {
$message = base64_decode($message, true);
if ($message === false) {
throw new Exception('Encryption failure');
}
}

$nonceSize = openssl_cipher_iv_length(self::METHOD);
$nonce = mb_substr($message, 0, $nonceSize, '8bit');
$ciphertext = mb_substr($message, $nonceSize, null, '8bit');

$plaintext = openssl_decrypt(
$ciphertext,
self::METHOD,
$key,
OPENSSL_RAW_DATA,
$nonce
);

return $plaintext;
}
}

Пример использования

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

демонстрация: https://3v4l.org/jl7qR


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

Заметка: По умолчанию, UnsafeCrypto::encrypt() вернет необработанную двоичную строку. Назовите это так, если вам нужно сохранить его в бинарно-безопасном формате (в кодировке base64):

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = UnsafeCrypto::encrypt($message, $key, true);
$decrypted = UnsafeCrypto::decrypt($encrypted, $key, true);

var_dump($encrypted, $decrypted);

демонстрация: http://3v4l.org/f5K93

Оболочка простой аутентификации

class SaferCrypto extends UnsafeCrypto
{
const HASH_ALGO = 'sha256';

/**
* Encrypts then MACs a message
*
* @param string $message - plaintext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encode - set to TRUE to return a base64-encoded string
* @return string (raw binary)
*/
public static function encrypt($message, $key, $encode = false)
{
list($encKey, $authKey) = self::splitKeys($key);

// Pass to UnsafeCrypto::encrypt
$ciphertext = parent::encrypt($message, $encKey);

// Calculate a MAC of the IV and ciphertext
$mac = hash_hmac(self::HASH_ALGO, $ciphertext, $authKey, true);

if ($encode) {
return base64_encode($mac.$ciphertext);
}
// Prepend MAC to the ciphertext and return to caller
return $mac.$ciphertext;
}

/**
* Decrypts a message (after verifying integrity)
*
* @param string $message - ciphertext message
* @param string $key - encryption key (raw binary expected)
* @param boolean $encoded - are we expecting an encoded string?
* @return string (raw binary)
*/
public static function decrypt($message, $key, $encoded = false)
{
list($encKey, $authKey) = self::splitKeys($key);
if ($encoded) {
$message = base64_decode($message, true);
if ($message === false) {
throw new Exception('Encryption failure');
}
}

// Hash Size -- in case HASH_ALGO is changed
$hs = mb_strlen(hash(self::HASH_ALGO, '', true), '8bit');
$mac = mb_substr($message, 0, $hs, '8bit');

$ciphertext = mb_substr($message, $hs, null, '8bit');

$calculated = hash_hmac(
self::HASH_ALGO,
$ciphertext,
$authKey,
true
);

if (!self::hashEquals($mac, $calculated)) {
throw new Exception('Encryption failure');
}

// Pass to UnsafeCrypto::decrypt
$plaintext = parent::decrypt($ciphertext, $encKey);

return $plaintext;
}

/**
* Splits a key into two separate keys; one for encryption
* and the other for authenticaiton
*
* @param string $masterKey (raw binary)
* @return array (two raw binary strings)
*/
protected static function splitKeys($masterKey)
{
// You really want to implement HKDF here instead!
return [
hash_hmac(self::HASH_ALGO, 'ENCRYPTION', $masterKey, true),
hash_hmac(self::HASH_ALGO, 'AUTHENTICATION', $masterKey, true)
];
}

/**
* Compare two strings without leaking timing information
*
* @param string $a
* @param string $b
* @ref https://paragonie.com/b/WS1DLx6BnpsdaVQW
* @return boolean
*/
protected static function hashEquals($a, $b)
{
if (function_exists('hash_equals')) {
return hash_equals($a, $b);
}
$nonce = openssl_random_pseudo_bytes(32);
return hash_hmac(self::HASH_ALGO, $a, $nonce) === hash_hmac(self::HASH_ALGO, $b, $nonce);
}
}

Пример использования

$message = 'Ready your ammunition; we attack at dawn.';
$key = hex2bin('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');

$encrypted = SaferCrypto::encrypt($message, $key);
$decrypted = SaferCrypto::decrypt($encrypted, $key);

var_dump($encrypted, $decrypted);

демос: сырой двоичный файл, кодировке base64


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

Вам будет гораздо лучше использовать авторитетная криптографическая библиотека.

189

использование mcrypt_encrypt() а также mcrypt_decrypt() с соответствующими параметрами. Действительно легко и просто, и вы используете проверенный в бою пакет шифрования.

РЕДАКТИРОВАТЬ

5 лет и 4 месяца после этого ответа mcrypt расширение в настоящее время находится в процессе устаревания и возможного удаления из PHP.

21

PHP 7.2 полностью отошел от Mcrypt и шифрование теперь основано на ремонтопригодности Libsodium библиотека.

Все ваши потребности в шифровании могут быть в основном решены с помощью Libsodium библиотека.

// On Alice's computer:
$msg = 'This comes from Alice.';
$signed_msg = sodium_crypto_sign($msg, $secret_sign_key);


// On Bob's computer:
$original_msg = sodium_crypto_sign_open($signed_msg, $alice_sign_publickey);
if ($original_msg === false) {
throw new Exception('Invalid signature');
} else {
echo $original_msg; // Displays "This comes from Alice."}

Документация Libsodium: https://github.com/paragonie/pecl-libsodium-doc

2

Вот простая, но достаточно безопасная реализация:

  • AES-256 шифрование в режиме CBC
  • PBKDF2 для создания ключа шифрования из простого текстового пароля
  • HMAC для аутентификации зашифрованного сообщения.

Код и примеры здесь: https://stackoverflow.com/a/19445173/1387163

1
По вопросам рекламы [email protected]