Увлекательная криптография или изыскания на тему обратимого шифрования на PHP. Защита PHP-скриптов от анализа и модификации Функции шифрования в PHP

(PHP 4, PHP 5, PHP 7)

crypt — One-way string hashing

Warning

This function is not (yet) binary safe!

Description

crypt (string $str [, string $salt ]) : string

crypt() will return a hashed string using the standard Unix DES -based algorithm or alternative algorithms that may be available on the system.

The salt parameter is optional. However, crypt() creates a weak hash without the salt . PHP 5.6 or later raise an E_NOTICE error without it. Make sure to specify a strong enough salt for better security.

password_hash() uses a strong hash, generates a strong salt, and applies proper rounds automatically. password_hash() is a simple crypt() wrapper and compatible with existing password hashes. Use of password_hash() is encouraged.

Some operating systems support more than one type of hash. In fact, sometimes the standard DES-based algorithm is replaced by an MD5-based algorithm. The hash type is triggered by the salt argument. Prior to 5.3, PHP would determine the available algorithms at install-time based on the system"s crypt(). If no salt is provided, PHP will auto-generate either a standard two character (DES) salt, or a twelve character (MD5), depending on the availability of MD5 crypt(). PHP sets a constant named CRYPT_SALT_LENGTH which indicates the longest valid salt allowed by the available hashes.

The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str , so longer strings that start with the same eight characters will generate the same result (when the same salt is used).

On systems where the crypt() function supports multiple hash types, the following constants are set to 0 or 1 depending on whether the given type is available:

  • CRYPT_STD_DES - Standard DES-based hash with a two character salt from the alphabet "./0-9A-Za-z". Using invalid characters in the salt will cause crypt() to fail.
  • CRYPT_EXT_DES - Extended DES-based hash. The "salt" is a 9-character string consisting of an underscore followed by 4 bytes of iteration count and 4 bytes of salt. These are encoded as printable characters, 6 bits per character, least significant character first. The values 0 to 63 are encoded as "./0-9A-Za-z". Using invalid characters in the salt will cause crypt() to fail.
  • CRYPT_MD5 - MD5 hashing with a twelve character salt starting with $1$
  • CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string. The two digit cost parameter is the base-2 logarithm of the iteration count for the underlying Blowfish-based hashing algorithmeter and must be in range 04-31, values outside this range will cause crypt() to fail. Versions of PHP before 5.3.7 only support "$2a$" as the salt prefix: PHP 5.3.7 introduced the new prefixes to fix a security weakness in the Blowfish implementation. Please refer to for full details of the security fix, but to summarise, developers targeting only PHP 5.3.7 and later should use "$2y$" in preference to "$2a$".
  • CRYPT_SHA256 - SHA-256 hash with a sixteen character salt prefixed with $5$. If the salt string starts with "rounds=
  • CRYPT_SHA512 - SHA-512 hash with a sixteen character salt prefixed with $6$. If the salt string starts with "rounds=$", the numeric value of N is used to indicate how many times the hashing loop should be executed, much like the cost parameter on Blowfish. The default number of rounds is 5000, there is a minimum of 1000 and a maximum of 999,999,999. Any selection of N outside this range will be truncated to the nearest limit.

As of PHP 5.3.0, PHP contains its own implementation and will use that if the system lacks of support for one or more of the algorithms.

Parameters

The string to be hashed.

Caution

Using the CRYPT_BLOWFISH algorithm, will result in the str parameter being truncated to a maximum length of 72 characters.

An optional salt string to base the hashing on. If not provided, the behaviour is defined by the algorithm implementation and can lead to unexpected results.

Return Values

Returns the hashed string or a string that is shorter than 13 characters and is guaranteed to differ from the salt on failure.

Warning

When validating passwords, a string comparison function that isn"t vulnerable to timing attacks should be used to compare the output of crypt() to the previously known hash. PHP 5.6 onwards provides hash_equals() for this purpose.

Changelog

Version Description
5.6.5 When the failure string "*0" is given as the salt , "*1" will now be returned for consistency with other crypt implementations. Prior to this version, PHP 5.6 would incorrectly return a DES hash.
5.6.0 Raise E_NOTICE security warning if salt is omitted.
5.5.21 When the failure string "*0" is given as the salt , "*1" will now be returned for consistency with other crypt implementations. Prior to this version, PHP 5.5 (and earlier branches) would incorrectly return a DES hash.
5.3.7 Added $2x$ and $2y$ Blowfish modes to deal with potential high-bit attacks.
5.3.2 Added SHA-256 and SHA-512 crypt based on Ulrich Drepper"s » implementation .
5.3.2 Fixed Blowfish behaviour on invalid rounds to return "failure" string ("*0" or "*1"), instead of falling back to DES.
5.3.0 PHP now contains its own implementation for the MD5 crypt, Standard DES, Extended DES and the Blowfish algorithms and will use that if the system lacks of support for one or more of the algorithms.

Examples

Example #1 crypt() examples

$hashed_password = crypt ("mypassword" ); // let the salt be automatically generated

/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (hash_equals ($hashed_password , crypt ($user_input , $hashed_password ))) {
echo "Password verified!" ;
}
?>

Example #2 Using crypt() with htpasswd

// Set the password
$password = "mypassword" ;

// Get the hash, letting the salt be automatically generated
$hash = crypt ($password );
?>

Example #3 Using crypt() with different hash types

/* These salts are examples only, and should not be used verbatim in your code.
You should generate a distinct, correctly-formatted salt for each password.
*/
if (CRYPT_STD_DES == 1 ) {
echo "Standard DES: " . crypt ("rasmuslerdorf" , "rl" ) . "\n" ;
}

if (CRYPT_EXT_DES == 1 ) {
echo "Extended DES: " . crypt ("rasmuslerdorf" , "_J9..rasm" ) . "\n" ;
}

if (CRYPT_MD5 == 1 ) {
echo "MD5: " . crypt ("rasmuslerdorf" , "$1$rasmusle$" ) . "\n" ;
}

if (CRYPT_BLOWFISH == 1 ) {
echo "Blowfish: " . crypt ("rasmuslerdorf" , "$2a$07$usesomesillystringforsalt$" ) . "\n" ;
}

if (CRYPT_SHA256 == 1 ) {
echo "SHA-256: " . crypt ("rasmuslerdorf" , "$5$rounds=5000$usesomesillystringforsalt$" ) . "\n" ;
}

if (CRYPT_SHA512 == 1 ) {
echo "SHA-512: " . crypt ("rasmuslerdorf" , "$6$rounds=5000$usesomesillystringforsalt$" ) . "\n" ;
}
?>

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

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

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

Но довольно уже воды.

Допустим, наше веб-приложение написано на php, нуждается в обратимом шифровании и мы считаем, что в силах написать свою систему шифра.

Итак, напишем собственную систему обратимого шифрования с приватным и публичным ключом, такую, которая будет обладать следующими признаками мало мальски защищенного криптографического алгоритма:

  1. Наличие шумовых символов в итоговом шифре.
  2. Информация в каждом канале Отправитель-Адресат будет шифроваться по приватному ключу, причем функция соответствия будет уникальной для каждого ключа.
  3. Каждое сообщение будет получать дайджест-код - некий уникальный код, являющийся функцией от приватного ключа и исходного сообщения. Это требуется для того, чтобы добиться уникальности функции соответствия «исходный символ <=> закодированный символ» не только для канала «Отправитель-Адресат», но и для каждого отдельного сообщения.

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

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

Собственно говоря, итоговый результат посмотреть можно

Класс SymCoder включает в себя методы шифрования и дешифрования.

Шифрование осуществляет метод code(), принимающий на входе исходное сообщение.

Здесь сообщение по сгенерированной таблице соответствия в tab_coded создает зашифрованное сообщение, разбавленное по краям и внутри шумовыми символами.

Шумовые символы кстати уникальны для каждого канала отправитель-адресат, так как генерируются используя ключ канала, но не уникальны для сообщений. Используемые для шифрования символы в code_symbols представляют собой некоторые знаки пунктуации и символы вида %, @ и т.п.

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

Таблица соответствия create_tab_coded строится используя трансляцию хеша ключа сообщения в массив с количеством элементов, равным количеству элементов в массиве кодовых символов. Позиция начала обхода двухсимвольных кодов также всегда различна и связана с ключом канала. Это дает возможность быть уверенным, что алгоритм обхода кодируемых символов и соответствия им кодовых символов всегда (ну или гарантированно часто) будет различным.

К примеру, сообщение «привет, мир» будучи закодированным, выглядит вот так:

Digest-a00bf11d-&?==&!&?.@.@=!=-.?&1.#&?=:.:.1%!&-%@&@%~&1^#=?%%.!%+.?.~=?..&?%&&:%~.#%@&1&1.#=?.#.?.!&1==&=.-=!

А вот то же самое сообщение, закодированное снова:

Digest-a00bf11d-=:.?=:&!.?.1&-=:=?.?.=.?.!&=%!=-%@=!%~.=^#.1%%.!%+=:.~.@..==%&&1%~.1%@=?.@.!&=.!&@=:&1.==:=!.1&:

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

Сообщения обладают избыточностью, которая уменьшается по мере роста объема сообщения, в пределе доходя до 10% шума (для самых коротких сообщений шум достигает 90% и выше процентов), минимальная длина шифрованного сообщения - 116 символов. Один из некоторых минусов данного способа шифрования - увеличения кодированных сообщений как минимум в два раза.

Декодирование заключается в обратном переводе вида «кодовый символ» - исходный символ с вырезанием шума из сообщения. Что может быть в качестве ключа? В принципе, любая строка, уникальная для каждой пары вида адресат-получатель.

К примеру, если вы создаете мессенджер с шифрованием сообщений, в таком случае простейший вариант закрытого ключа может быть md5($user_id_1. $salt. $user_id_2), тогда ключ будет уникальный для каждого канала сообщений.

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

Рассмотрим основные методы шифровки

base64 - позволяет шифровать и расшифровывать данные алгоритмом MIME base64. Он не использует ключей и его часто используют для скрытия ссылок в php.

Примеры:
//шифруем текст
$text = "Ссылка";
echo base64_encode($text); //Выдаст: PGEgaHJlZj0iIyI+0KHRgdGL0LvQutCwPC9hPg==
//дешифровка
echo base64_decode("PGEgaHJlZj0iIyI+0KHRgdGL0LvQutCwPC9hPg==");
?>

Как видно мы использовали сначала операцию base64_encode и получили шифр: PGEgaHJlZj0iIyI+0KHRgdGL0LvQutCwPC9hPg== , а затем подставили его в base64_decode и получили ссылку обратно.

md5 - позволяет хэшировать данные в одностороннем порядке. То есть в отличие от base64, вы уже не сможете их расшифровать обратно. Часто md5 используют для хранения паролей в БД, но в последнее время зашифрованную комбинацию md5 стало легко найти в таблицах по расшифровке, любезно предоставленную многими сайтами и алгоритмами. Поэтому для хранения паролей md5 лучше заменять на Blowfish алгоритмы.

Пример:

//шифруем текст
echo md5("комбинация");
?>

Шифрование по ключу

И последний пример шифровки/дешифровки, о котором я хотел рассказать использует ключ (как пароль). То есть в функцию шифрования вы передаете уникальный ключ, код шифруется вместе с ним. Для расшифровки вы должны предоставить функции зашифрованный код и ключ, который знаете только вы. Пример использования функций в самом низу кода.

function __encode($text, $key) {



$enc_text=base64_encode(mcrypt_generic ($td,$iv.$text));
mcrypt_generic_deinit ($td);
mcrypt_module_close ($td);
return $enc_text; } }
function strToHex($string) {
$hex="";
for ($i=0; $i < strlen($string); $i++) { $hex .= dechex(ord($string[$i])); }
return $hex; }
function __decode($text, $key) {
$td = mcrypt_module_open ("tripledes", "", "cfb", "");
$iv_size = mcrypt_enc_get_iv_size ($td);
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size ($td), MCRYPT_RAND);
if (mcrypt_generic_init ($td, $key, $iv) != -1) {
$decode_text = substr(mdecrypt_generic ($td, base64_decode($text)),$iv_size);
mcrypt_generic_deinit ($td);
mcrypt_module_close ($td);
return $decode_text; } }
function hexToStr($hex) {
$string="";
for ($i=0; $i < strlen($hex)-1; $i+=2) { $string .= chr(hexdec($hex[$i].$hex[$i+1])); }
return $string; }

$str = "Булочки, которые надо зашифровать!
По ключу";
$code = strToHex(__encode($str, "My#key-do-36-simvolov"));
echo "Зашифрованный код: ".$code."
";

$str = __decode(hexToStr($code), "My#key-do-36-simvolov");
echo "Расшифрованный код: ".$str."
";
?>

Шифровать можно с html содержимым. Длина ключа должна быть не более 36 символов.

Этот метод можно использовать для шифровки каких-то данных и их помещения в txt файл или БД, а получать с помощью дешифровки с ключем.

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

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

В качестве еще одного примера псевдошифрования приведу пример «шифрования» паролей в базе данных одной CMS – там пароли шифруются не в md5() или , а просто кодируются через base64. Т.е. при сливе базы хакеру не составит никого труда расшифровать все пароли через встроенную php-функцию base64_decode().

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

Mcrypt версии 2.4.7 поддерживает следующие алгоритмы симметричного шифрования: Blowfish, RC2, Safer-sk64 xtea, Cast-256, RC4, Safer-sk128, DES, RC4-iv, Serpent, Enigma, Rijndael-128, Threeway, Rijndael-192, TripleDES, LOKI97, Rijndael-256, Twofish, Panama, Saferplus и т.д. Подробнее про каждый алгоритм написано в Википедии.

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

Пример шифрования и расшифровки строки

mcrypt_module_open("des", "", "ecb", "")
Эта функция открывает модуль алгоритма и используемый режим. Для данного примера Алгоритм DES в режиме ECB.

$key = substr($key, 0, mcrypt_enc_get_key_size($td));
Максимальный размер ключа должен быть получен вызовом функции mcrypt_enc_get_key_size(), и каждое значение меньше полученного будет правильным.

$s = mcrypt_generic($td, $source);
При шифровании данные заполняются нулевыми байтами, чтобы гарантировать длину данных в n*blocksize. Размер блока blocksize определяется алгоритмом (для DES размер блока 64 бита). Поэтому при расшифровке в конце строки могут появиться “\0”, которые удаляются функцией trim()

  • Перевод
  • Tutorial

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

Это краткое руководство о том, как избежать распространенных ошибок с симметричным шифрованием на PHP.

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

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

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

Функции шифрования в PHP

Используйте расширения Mcrypt или OpenSSL .

Алгоритм шифрования и его режим работы, одноразовый код (вектор инициализации)

Используйте AES-256 в режиме CTR со случайным одноразовым кодом (прим. перев.: nonce ). AES это стандарт, поэтому можно использовать функции любого из расширений - Mcrypt или OpenSSL.

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

Одноразовый код должен быть длиной 128 бит (16 байт), просто строка байт без какого-либо кодирования.

В расширении Mcrypt AES известен как Rijndael-128 (прим. перев.: несмотря на то, что речь идет про AES-256, это не ошибка. AES-256 != Rijndael-256 ). В OpenSSL соответственно AES-256-CTR.

Пример использования Mcrypt:
Пример использования OpenSSL:
Убедитесь, что шифрование работает правильно с помощью тестовых векторов (прим. перев.: для AES-256-CTR см. пункт F.5.5 на странице 57 ).

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

Режим CTR сохраняет стойкость только если не использовать один и тот же одноразовый код с одним и тем же ключом. По этой причине важно генерировать одноразовые коды при помощи криптографически стойкого источника случайности. Кроме того, это означает, что вы не должны шифровать более чем 2^64 сообщений с одним ключом. Поскольку длина одноразового кода 128 бит, важно ограничение на количество сообщений (и соответствующих им одноразовых кодов) 2^128/2 из-за парадокса Дней рождения (прим. перев.: ).

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

Аутентификация данных

Всегда проводите проверку подлинности и целостности данных.
Для этого после шифрования используйте MAC . Т.е. сначала данные шифруются, а затем берется HMAC-SHA-256 от полученного шифротекста, включая собственно шифротекст и одноразовый код.

При расшифровке сначала проверте HMAC, используя алгоритм сравнения устойчивый к атакам по времени. Не сравнивайте напрямую $user_submitted_mac и $calculated_mac, используя операторы сравнения == или ===. Лучше даже использовать "двойную проверку HMAC ".

Если проверка HMAC удачна, можно безопасно производить расшифровку. Если же HMAC не подходит, немедленно завершайте работу.

Ключи шифрования и аутентификации

В идеале использовать ключи, полученные из криптографически стойкого источника случайности. Для AES-256 необходимы 32 байта случайных данных («сырая» строка – последовательность бит без использования какой-либо кодировки).

Если приложение запущено под PHP версии ниже 5.5, где нет встроеной реализации PBKDF2, то придется использовать собственную реализацию на PHP, пример которой можно найти тут: https://defuse.ca/php-pbkdf2.htm . Имейте ввиду, что полагаясь на собственную реализацию, возможно не получится преобразовать ключ должным образом, как это делает встроенная функция hash_pbkdf2().

Не используйте один и тотже ключ для шифрования и аутентификации. Как сказано выше, необходимо 32 байта на ключ шифрования и 32 байта на ключ аутентификации (HMAC). С помощью PBKDF2 вы можете получить 64 байта из пароля и использовать, скажем, первые 32 байта в качестве ключа шифрования, и остальные 32 байта для ключа аутентификации.

Если у вас пароли хранятся в файле, например, в виде HEX-строки, не перекодируйте их перед тем как «скормить» функциям шифрования. Вместо этого используйте PBKDF2 для преобразования ключей из HEX-кодировки сразу в качественный ключ шифрования или аутентификации. Или используйте SHA-256 с выводом без дополнительного кодирования (просто строка в 32 байта) для хеширования паролей. Использование обычного хеширования паролей дает достаточно энтропии. Подробнее описано в следующих параграфах.

Растяжение ключа

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

Одним из параметров PBKDF2 является количество итераций хеширования. И чем оно выше, тем на большую безопасность ключа можно рассчитывать. Если ваш код работает на 64-битной платформе, используйте SHA-512 в качестве алгоритма хэширования для PBKDF2. В случае 32-битной платформы используйте SHA-256.

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

В случае, если вы можете использовать пароли с высокой энтропией, не обязательно проводить «растяжение», как для паролей с низкой энтропией. Например, если вы создаете «главный_ключ_шифрования» и «главный_ключ_аутентификации», используя /dev/urandom, то необходимость в PBKDF2 вообще отпадает. Только убедитесь, что используете ключи как последовательности бит, без какого-либо кодирования.

Кроме того, с помощью PBKDF2 несложно получить оба ключа и для шифрования, и для аутентификации от одного мастер-пароля (просто использовать небольшое количество итераций или даже одну). Это полезно, если у вас есть только один «мастер-пароль», используемый и для шифрования, и для аутентификации.

Хранение и управление ключами

Самое лучшее - это использовать отдельное специализированное устройство для хранения ключей (HSM).

Если это невозможно, то для усложнения атаки, можно использовать шифрование файла с ключами или файла конфигурации (в котором хранятся фактические ключи шифрования / аутентификации) с помощью ключа, хранящегося в отдельном месте (вне домашего каталога или корня сайта). Например, вы можете использовать переменную окружения Apache в httpd.conf, чтобы сохранить ключ, необходимый для расшифровки файла с фактическими ключами:
SetEnv keyfile_key crypto_strong_high_entropy_key # You can access this variable in PHP using $_SERVER["keyfile_key"] # Rest of the config
Теперь, если файлы в корне сайта и ниже, в том числе файлы с ключами, будут скомпрометированы (например, при утечке бэкапа), зашифрованные данные останутся в безопаности поскольку ключ, хранящийся в переменной окружения, не был скомпрометирован. Важно помнить, что файлы httpd.conf следует бэкапить отдельно, и не скомпрометировать переменную keyfile_key через, например, вывод phpinfo().

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

Сжатие данных

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

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

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

Если у вас нет жесткой необходимости сжимать данные, не сжимайте.

Серверное окружение

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

Есть различные причины, делающие разделяемые сервера сомнительным местом для размещения критичных к безопасности приложений. Например, недавно были продемонстрированы атаки между виртуальными серверами: eprint.iacr.org/2014/248.pdf . Это хорошее напоминание, что техники нападения не деградируют, а наоборот оттачиваются и улучшаются со временем. Всегда надо учитывать такие подводные камни.

Консультация эксперта

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