PHP: Предупреждение mcrypt_generic_init (): неверный размер Iv; поставляемая длина: 12, необходимо: 8

Основные факты:

$algorithm  = MCRYPT_BLOWFISH;
$mode       = MCRYPT_MODE_CBC;
$randSource = MCRYPT_DEV_URANDOM;

Заметка Это не строгий вопрос кодирования.

Контекст:

CentOS 7, Apache 2.4.12 и PHP 5.6.20.

Я делаю электронное письмо в формате HTML со ссылкой «подтвердить свой адрес электронной почты», которая позволяет завершить процесс регистрации. Все на моем виртуальном частном сервере - UTF-8, и все вводы формы и строки запроса обрабатываются с помощью многобайтовых (mb) функций.

Фон

Как эксперимент (Я знаю о возрасте и состоянии библиотеки Mcrypt), Я пытаюсь расшифровать параметры строки запроса Blowfish. Предположим, что на пути вверх последовательность шифрования работает отлично, и я получаю электронное письмо со ссылкой.

На пути внизhmac_hash() Подписание (SHA-512, только для этого эксперимента) работает, и я могу отделить каждое независимое сообщение (32 символа) от его контрольной суммы хеша (128 символов). Декодирование Base64 отдельной части сообщения работает. Для каждого параметра у меня остаетсясоставной шифрованный текстгде составной зашифрованный текст равенIV + базовый зашифрованный текст, Предположим, я используюверсия изsubstr() получить IV и базовый зашифрованный текст независимо (что является нормой для курса).

проблема

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 12, needed: 8

Предположим, я прочесал руководство по PHP и Stackoverflow. Предположим, я смотрел на другие вопросы, похожие, но не совсем такие, как этот. Предположим, я искал в интернете безрезультатно. Предположим, у меня достаточно опыта для настройкиmb_string должным образом. Предположим, что я позабочусь о заполнении mcrypt, когда преодолею эту проблему.

Могут ли многобайтовые проблемы мешать расшифровке?

Может ли base64 кодироватьIV + base cipher text развратить IV?

Может ли base64 padding быть проблемой?

Должен ли я указать более конкретныйMCRYPT_BLOWFISH_*?

Почему размер Blowfish IV сообщает 8 байтов, но редко дает 8 байтов IV?

Какой substr () я должен использовать,substr() или жеmb_substr()для установки, которая склоняется к созданию всего UTF-8 и обрабатывает все другие входные данные как многобайтовый UTF-8. Я знаю, что это странный вопрос, но все примеры последовательности расшифровки mycrypt в PHP Manual используютзиЬзЬг ()и никто не используетmb_substr(), Все на моем сайте работает с mb_functions, когда это возможно, и я не против использоватьsubstr() если это решило мою проблему, но не решило ее. Когда я используюmb_substr()Я получаю следующее предупреждение.

PHP: Warning  mcrypt_generic_init(): Iv size is incorrect; supplied length: 11, needed: 8

У кого-нибудь есть опыт работы с этой конкретной проблемой? Конструктивные ответы будут вознаграждены!

Самый последний

Выше приведен пример хэша Blowfish, который я пытаюсь восстановить из массива, полученного через SHA512 HMACed, симметрично зашифрованный Blowfish (CBC), URL-адрес, безопасный в кодировке Base64, в кодировке urlenco, строку запроса (phew!).

Ниже показано, как выглядят строки для строки запроса (разделив хэш blowfish выше) после шифрования, подписи и кодирования base64, но до того, как их кодируют в формате urlencoded. Каждый из них имеет длину 128 символов (каждая строка становится длиннее, когда вы делаете больше вещей).

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

 Anthony Rutledge23 июн. 2016 г., 22:50
@BentCoder Это похоже на фильм «Золотое дитя» «Ой, Монти, дно!»
 Nitin19 июн. 2016 г., 20:58
Похоже, вы очень близки. Еще одна вещь, которая пришла мне в голову, заключалась в ограничении персонажей, используемых при создании iv, но это идет за счет безопасности.
 Anthony Rutledge19 июн. 2016 г., 07:18
@ Nitin Я пытался убедиться, что длина строки, которую я использую для ключа, составляет не менее 56 символов. Кроме того, я интегрировал безопасную для URL версию base64_encode. Единственное, о чем я могу думать, - это вернуть ключ в виде двоичных данных и использовать его таким образом.
 Rudie20 июн. 2016 г., 19:17
Так что, стоит ли это base64 ?, так что кодирование больше не имеет значения. В любом случае, кодирование не должно быть проблемой, потому что артефакты шифрования всегда двоичные (или делаются двоичными безопасными, как вывод md5). Какие строки вы подстроки ??
 Anthony Rutledge23 июн. 2016 г., 14:42
@BentCoder Спасибо за ваш вклад. Это забавно. Я прибегнул к тому, чтобы все входные данные для любой функции mcrypt были в формате ISO-8859-1 и проблема с IV исчезла. Шифрование работает, а дешифрование - нет. Я обязательно посмотрю код в вашей ссылке. Спасибо.
 Anthony Rutledge20 июн. 2016 г., 01:51
@Rudie Другими словами, когда выпереключать контексты, Когда вы никогда не покидаете PHP, это не проблема, но IV путешествует в разных контекстах. Я пробовал оба функции substr () и mb_substr (), и, как вы можете видеть в сценарии, я говорю «символы» для mb_substr (). Нет кости. Да, мой стек UTF-8 все, ноmcrypt_create_iv() выходы ISO-8859-1.
 Anthony Rutledge15 июн. 2016 г., 04:45
Мы все знаем, что предполагает, но просто пойти с этим! ;-)
 Anthony Rutledge19 июн. 2016 г., 17:16
@ Nitin По-видимому, возвращаемая IV - ISO-8859-1. Когда я конвертирую его в UTF-8, IV всегда сообщает, что это 8 байтов. Возможно, я решил это.
 Anthony Rutledge19 июн. 2016 г., 21:13
@Nitin Возможно, мне следует использовать ISO-8859-1 IV во время шифрования и преобразовать входящий IV (mb, UTF8,) в ISO-8859-1 IV. Это может быть билет.
 Rudie20 июн. 2016 г., 00:16
12байтов возможно, вы используете mb_substr (), которая принимает символы, а не байты. С substr () 8 займет 8 байтов. Кодирование никогда не должно быть проблемой, потому что вывод является либо двоичным (используйте base64), либо безопасным для ascii (mb или нет не будет иметь значения).
 Anthony Rutledge23 июн. 2016 г., 22:24
@BentCoder Эй, я просмотрел код шифрования. К сожалению, нет игры в кости. Этот пример кода (часть шифрования), за исключением того, что он не является режимом Blowfish или CBC, кажется, работает в мире, где им никогда не приходится учитывать проблемы с многобайтовыми данными, или тот факт, что mycrypt выдает ISO-8859-1, но Я обрабатываю многобайтовую строку запроса UTF-8. Я ценю, что вы пытались помочь. Больше предложений будут с благодарностью.
 Anthony Rutledge19 июн. 2016 г., 02:12
@Nitin Спасибо, что посмотрели на мою проблему. Я дал вам награду только за удар! ;-) Привет, возможно, ответом будет просто использовать расширение OpenSSL. Но я ничего не могу найти нигде, чтобы предположить, что Blowfish не будет работать с mcrypt. Все методы mcrypt хотят строку для ключа, но, возможно, ключ должен быть двоичной строкой?
 Anthony Rutledge20 июн. 2016 г., 23:00
@Rudie У меня безопасным способом URL base64 кодировал составной зашифрованный текст (IV + зашифрованный текст), но вам не хватает того, что он выходит из UTF-8 и входит в UTF-8. Таким образом, mcrypt жалуется, потому что хочет работать с набором символов ISO-8859-1.
 Anthony Rutledge15 июн. 2016 г., 13:40
@ Syon Вы, кажется, довольно хорошо с шифрованием. Какие-нибудь мысли?
 Nitin18 июн. 2016 г., 20:19
У меня была похожая проблема, когда я использовал ryndael 128. Потратил часы на поиски причины, но обнаружил, что mcrypt не поддерживается активно и переключился на шифрование openssl, которое до сих пор работает хорошо. Это предположение, но я думаю, что проблема заключается в том, как ключ получен / воссоздан.
 BentCoder23 июн. 2016 г., 12:58
@AnthonyRutledge Я знаю, что вы на самом деле не ищете код, а просто хотите поделиться этой ссылкой, если она поможет. смотреть наCryptorService.php
 Anthony Rutledge19 июн. 2016 г., 23:01
Ха! Принудительное использование ISO-8859-1 во время шифрования дало мне сообщение об ошибке «длина 4, требуется 8». Я просто позволю ему сделать нормальное создание IV во время шифрования и попробую принудительно установить iso-8859-1 во время дешифрования.
 Anthony Rutledge19 июн. 2016 г., 21:06
Теперь, на этапе шифрования, я получаю сообщение об ошибке «Неверный размер IV». Я думаю, что mcrypt не поддерживает многобайтовый режим. Я попытался ограничить IV ($ ivSize / 2), и он дал мне «у вас есть только 7 из 8 "байтов, необходимых. Это определенно проблема МБ.
 Anthony Rutledge20 июн. 2016 г., 01:48
@ Руди Привет Руди. Спасибо за ваш вклад. Учтите, однако, что с момента создания IV (ISO-8859-1), конкатенированного в начало текста шифра (с конкатенацией HMAC перед всем этим), base64_encoded, хранящегося в строке запроса, urlencoded , вывод с веб-сервера (UTF-8), введенный нажатием (filter_input_array(INPUT_GET)) по ссылке в электронном письме (где все входные данные гарантированно являются UTF-8 через строковые функции mb в моей структуре фильтра, а не ISO-8859-1), автоматически кодируются с помощью PHP, отделены от HMAC, base64_decoded и восстановлены из составной зашифрованный текст.
 BentCoder23 июн. 2016 г., 22:49
@AnthonyRutledge - с точки зрения blowfish и cbc, хотя я сам не проверял, но насколько я вижу, если вы установите$algorithm = 'blowfish' а также$mode = 'cbc'класс должен справиться с этим, но я не думаю, что это все равно решит вашу конкретную проблему. Как вы сказали, это сработает, только если вы начнете использовать все это в самом начале определенным образом, который, к сожалению, к вам не относится. Похоже, у вас может получиться работающее решение, в котором задействован небольшой фрагментарный и хакерский код.

Ответы на вопрос(1)

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

Строки в PHP - это просто наборы байтов. Вы можете хранить там текст в выбранной кодировке или просто хранить там двоичные данные, например изображения. PHP не знает, какие данные содержатся в какой строке, и какая кодировка там используется. Это зависит от разработчика, чтобы отслеживать эту информацию.

При работе с шифрованием вы получаете двоичные данные при генерации случайных строк или шифровании некоторых полезных данных. Он сохраняется в виде строк, но не имеет кодировки UTF-8, так как это всего лишь байты. Я бы даже не сказал, что это кодировка ISO-8859-1, поскольку это означало бы, что байт 77 (0x4D) обозначает букву «М». Но на самом деле это просто цифры - 77 вообще не означает ни одной буквы.

Еще одна вещь, которую нужно добавить - для символов ASCII (латинские буквы, цифры и т. Д. - значения 0-127 байт) требуется один байт для представления этого символа в кодировке UTF-8 (так же, как в ISO-8859). Так, насколько вы проходитеbase64_encodeДанные, вы не должны беспокоиться об этом.mb_substr также будет работать так же, какsubstr. Но! для двоичных данных вы не можете использоватьmb_* функции, как это работает с персонажами. Например, если зашифрованные данные составляют два байта0xC5 0xA1Это единственный символ в UTF-8. Шифрование работает с байтами (вплоть до конечного результата, который может быть чем угодно - даже с двоичными файлами), а не с символами.

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

Чтобы показать параметры передачи в URL, есть два файла:encrypt.php а такжеdecrypt.php, Сохранить в каталог, запуститьphp -S localhost:8000 в нем и идиHTTP: // локальный: 8000 / encrypt.php

encrypt.php:

<?php
// mcrypt_enc_get_key_size($td) gives 56, so it's longest that this key can be
$key = 'LedsoilgarvEwAbDavVenpirabUfjaiktavKekjeajUmshamEsyenvoa';
$data = 'This is very important data, with some š UTF-8 ĘĖ symbols';

$td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');

// create random IV - it's just random 8 bytes. You should use random_bytes() instead if available
$ivSize = mcrypt_enc_get_iv_size($td);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);

mcrypt_generic_init($td, $key, $iv);

$encrypted = mcrypt_generic($td, $data);

mcrypt_generic_deinit($td);
mcrypt_module_close($td);

// payload that you want to send - binary. It's neither UTF-8 nor ISO-8859-1 - it's just bytes
$payload = $iv . $encrypted;

// base64 to pass safely
$base64EncodedPayload = base64_encode($payload);
// URL encode for URL. No need to do both URL-safe base64 *and* base64 + urlencode
$link = 'http://localhost:8000/decrypt.php?encryptedBase64=' . urlencode($base64EncodedPayload);

// in fact, just for the reference, you don't even need base64_encode - urlencode also works at byte level
// base64_encode takes about 1.33 more space, but urlencode takes 3 times more than original for non-safe symbols, so base_64 will probably be shorter
$link2 = 'http://localhost:8000/decrypt.php?encrypted=' . urlencode($payload);

?>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <pre><?php
            var_dump('Data:', $data);
            var_dump('Data size in bytes:', strlen($data));
            var_dump('Data size in characters - smaller, as 3 of the characters take 2 bytes:', mb_strlen($data, 'UTF-8'));
            var_dump('Encrypted data size in bytes - same as original:', strlen($encrypted));
            var_dump('Encrypted data size in characters - will be pseudo-random each time:', mb_strlen($encrypted, 'UTF-8'));

            var_dump('IV base64 encoded:', base64_encode($iv));
            var_dump('Encrypted string base64 encoded:', base64_encode($encrypted));
        ?></pre>
        <!-- Link will not contain any special characters, so htmlentities should not make any difference -->
        <!-- In any case, I would still recommend to use right encoding at the right context to avoid any issues if something changes -->
        <a href="<?php echo htmlentities($link, ENT_QUOTES, 'UTF-8');?>">Link to decrypt</a><br/>
        <a href="<?php echo htmlentities($link2, ENT_QUOTES, 'UTF-8');?>">Link to decrypt2</a>
    </body>
</html>

decrypt.php:

<?php
$key = 'LedsoilgarvEwAbDavVenpirabUfjaiktavKekjeajUmshamEsyenvoa';

if (isset($_GET['encryptedBase64'])) {
    // just get base64_encoded symbols (will be ASCII - same in UTF-8 or other encodings)
    $base64EncodedPayload = $_GET['encryptedBase64'];
    $payload = base64_decode($base64EncodedPayload);
} else {
    // just get binary string from URL
    $payload = $_GET['encrypted'];
}

$td = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_CBC, '');

$ivSize = mcrypt_enc_get_iv_size($td);

$iv = substr($payload, 0, $ivSize);
$encrypted = substr($payload, $ivSize);

mcrypt_generic_init($td, $key, $iv);

/* Decrypt encrypted string */
$decrypted = mdecrypt_generic($td, $encrypted);

/* Terminate decryption handle and close module */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

?>
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <pre><?php
            var_dump('IV base64 encoded:', base64_encode($iv));
            var_dump('Encrypted string base64 encoded:', base64_encode($encrypted));
            var_dump('Result:', $decrypted);
        ?></pre>
    </body>
</html>
 Anthony Rutledge15 мар. 2017 г., 00:01
Примечание: я не использую фреймворк.
 Anthony Rutledge16 мар. 2017 г., 01:18
Мариус, эй, прежде чем я углублюсь в это, возможно, вы могли бы взглянуть на код в моем ответе на этот вопрос о кодировании. Это всего лишь два метода класса, но он может иметь отношение.stackoverflow.com/questions/7979567/...
 Marius Balčytis15 мар. 2017 г., 00:08
И то и другоеlink а такжеlink2 работает - он просто нуждается в тех же шагах (в обратном порядке) на конце расшифровки (см.if вdecrypt.php). ВыМожно используйте URL-безопасное кодирование Base64, это просто не нужно, если вы делаетеurlencode, Если это понятнее - вы можете сделать это, просто сделайте обратные действия при расшифровке. Про рамки - я неправильно понял это из твоего комментария (where all inputs are assured to be UTF-8 via mb string functions in my filter framework)
 Anthony Rutledge21 мар. 2017 г., 22:25
Вы упускаете суть. Когда данные возвращаются на мой веб-сервер, мое PHP-приложение собирается проверить кодировку (или вы не знаете о таком шаге). Я не занимаюсь шифрованием только в PHP. Я дам вам знать, как оно идет.
 Anthony Rutledge15 мар. 2017 г., 00:14
Хорошо, я дам вам знать, как это происходит. На самом деле я сейчас нахожусь в процессе настройки VirtualHost, но через несколько часов я могу захотеть поиграть. Я буду на связи. Будем надеяться, что подписание не вызовет проблемы.
 Anthony Rutledge20 мар. 2017 г., 17:56
Я тут думал. Последовательность событий, отображаемая в вашем ответе, не совпадает с последовательностью событий при вводе черезGET запрос. В частности, отсутствует какая-либо попытка проверить кодировку ввода. Тем не менее, когда я получу реорганизацию моего входного дезинфицирующего средства, я дам вам знать, как это происходит.
 Marius Balčytis21 мар. 2017 г., 19:04
Как я пытался объяснить в своем ответе - в двоичных данных нет «кодирования». Это просто двоичные данные, вот и все. Вы можете кодировать его base64 перед отправкой и декодировать после добавления в другой скрипт, но даже это не является необходимым.
 Anthony Rutledge14 мар. 2017 г., 23:59
Таким образом, последовательность расшифровки, которую вы изображаете, будет дляlink2, Я посмотрю, смогу ли я применить некоторые ваши рекомендации UTF-8 к криптографическому коду (class Blowfish extends Cipher). Я постараюсь исключить любое использование функций mb_ * в любое время в процессе шифрования или дешифрования. Я верю, что все еще разумно, чтобы url-безопасный base64 кодировал зашифрованный текст после того, как он был подписан (hash_hmac('sha512', $string, $this->hmacKey, false)), потому что это дешевый шаг скрытия, который удерживает подчеркивание и два других символа в строке запроса. Но если это вызывает проблемы, это должно идти.

Ваш ответ на вопрос