
Современный PHP уже давно вышел за рамки инструмента для создания простых сайтов. Сегодня на этом языке функционируют сложные банковские системы, CRM-платформы, интернет-магазины, корпоративные порталы и API. Однако одна из старейших особенностей языка продолжает оставаться источником критических уязвимостей — речь идет о механизме автоматического преобразования типов данных.
Для программиста это выглядит как удобная функция, позволяющая сравнивать строки с числами без явного приведения типов. Для специалиста по тестированию безопасности — как потенциальная возможность обойти проверку паролей, HMAC-подписей, токенов или логики авторизации. Неудивительно, что задания на PHP Type Juggling регулярно встречаются в CTF-соревнованиях, лабораториях PortSwigger и устаревших версиях реальных приложений.
В данном материале мы детально рассмотрим принципы работы приведения типов в PHP, объясним опасность оператора ==, раскроем понятие magic hash и перечислим основные признаки Type Juggling, на которые стоит обращать внимание при тестировании безопасности.
В завершении статьи вас ждет практическое задание из бесплатного курса «Профессия Белый Хакер», где можно на практике изучить работу небезопасной десериализации в учебном приложении.
Исторические предпосылки Type Juggling
PHP принадлежит к категории языков с динамической типизацией. Это означает, что переменные не имеют жестко зафиксированного типа данных, а интерпретатор постоянно пытается адаптировать их к наиболее подходящему формату.
Рассмотрим пример:
$a = "10"; $b = 10; var_dump($a == $b); // true
На поверхностный взгляд такое поведение кажется логичным. Если строка содержит числовое значение, почему бы не сравнить его с числом.
Проблемы возникают, когда в сравнении участвуют значения, которые не являются числами, но PHP все равно пытается интерпретировать их как числовые выражения.
Именно в таких ситуациях появляются неожиданные результаты, способные кардинально изменить логику работы приложения.
Две модели сравнения — два уровня безопасности

В PHP существует два основных оператора сравнения.
Первый — нестрогий:
==
Второй — строгий:
===
Разница кажется незначительной, но именно она определяет степень защищенности приложения.
При использовании === происходит одновременная проверка:
- типа данных;
- значения.
"10" === 10
Результат:
false
Потому что первый операнд представляет собой строку, а второй — целое число.
Оператор == работает по другому принципу. Перед сравнением PHP пытается привести оба значения к единому типу.
"10" == 10
Результат:
true
Само по себе это не является ошибкой. Опасность возникает, когда разработчик применяет == при проверке:
- паролей;
- токенов доступа;
- HMAC-подписей;
- идентификаторов;
- результатов криптографических операций.
Практически все известные атаки Type Juggling берут начало именно отсюда.
Наиболее рискованные преобразования
Для понимания последующих примеров достаточно запомнить несколько ключевых правил.
Если PHP обнаруживает строку, напоминающую число, он попытается преобразовать ее в числовой формат.
Например:
"15" == 15
вернет
true
Если строка начинается с цифр, интерпретатор использует только числовую часть.
"123admin" == 123
Результат:
true
В ранних версиях PHP ситуация была еще более интересной.
"0admin" == 0
возвращало
true
Аналогичное поведение наблюдалось и со строками, не содержащими цифр.
"admin" == 0
В PHP 7 результатом также было
true
Именно такое поведение годами становилось причиной обхода различных проверок. Начиная с PHP 8 часть подобных сравнений была изменена, однако старые приложения продолжают использовать предыдущие версии языка, поэтому подобные конструкции до сих пор регулярно встречаются при тестировании безопасности.
Научная нотация — самый известный прием
Наиболее известная особенность Type Juggling связана с научной записью чисел.
PHP интерпретирует строку
1e3
как число
1000
Поэтому сравнение
"1e3" == 1000
вернет
true
Но гораздо интереснее выглядит другой случай.
"0e12345"
Для человека это обычная текстовая строка.
Для PHP — числовое выражение
0 × 10^12345
То есть обычный ноль.
Следовательно,
"0e12345" == 0
будет истинным.
На этом принципе построен один из самых известных классов атак — magic hash.

Суть Magic Hash
Представим, что приложение проверяет пароль следующим образом:
if (md5($password) == $stored_hash) { login(); }
Разработчик полагает, что сравнивает две строки.
Но оператор == работает иначе.
Если обе строки выглядят как научная запись числа, PHP преобразует их в числовой формат.
Например:
0e462097431906509019562988736854
и
0e830400451993494058024219903391
после преобразования становятся нулем.
Поэтому
"0e462097431906509019562988736854" == "0e830400451993494058024219903391"
вернет
true
Хотя сами строки совершенно различны.
Подобные значения называют magic hash — хешами, начинающимися с 0e, после которых следуют только цифры.
Если приложение использует слабое сравнение, подобрать второй такой хеш иногда оказывается достаточным для обхода проверки.
Именно поэтому во многих старых CTF при виде конструкции
if (md5($input) == $hash)
первой мыслью становится поиск magic hash.
Причины длительного существования проблемы
На первый взгляд кажется, что проблема очевидна. Почему же она существовала столько лет?
Ответ прост.
Type Juggling — не ошибка PHP.
Это особенность языка.
В большинстве случаев она действительно удобна и позволяет сократить объем кода. Проблема возникает тогда, когда разработчик применяет автоматическое приведение типов в ситуациях, требующих криптографической точности.
Особенно часто это встречалось в старых проектах, созданных во времена PHP 5 и ранних версий PHP 7, когда использование == считалось практически нормой.
Позже ситуация изменилась. В PHP 8 разработчики языка пересмотрели правила сравнения строк и чисел, сделав многие неочевидные преобразования невозможными. Однако это не означает, что Type Juggling исчез полностью. Если приложение работает на старой версии PHP или использует небезопасную логику сравнения внутри собственного кода, риск остается вполне реальным.
Методика поиска Type Juggling при тестировании
Во время тестирования безопасности не стоит пытаться вслепую перебирать magic hash.
Гораздо эффективнее сначала определить, существуют ли вообще условия для эксплуатации.
В первую очередь следует обратить внимание на:
- использование оператора == вместо ===;
- сравнение результатов md5(), sha1(), hash() или hash_hmac();
- проверку токенов и цифровых подписей;
- старые проекты на PHP 5.x или PHP 7.x;
- нестандартные механизмы аутентификации;
- проверки, где пользователь может влиять на тип входных данных.
Нередко одного взгляда на исходный код достаточно, чтобы понять: приложение потенциально уязвимо к Type Juggling.
Во второй части мы перейдем от теории к практике эксплуатации. Рассмотрим, как использовать массивы вместо строк, почему некоторые функции возвращают NULL, каким образом это позволяет обходить проверки HMAC и как подобные ошибки выглядят в реальных CTF и при аудите веб-приложений.
Массивы как инструмент обхода аутентификации
Magic Hash — далеко не единственный способ эксплуатации Type Juggling. На практике специалисты по безопасности гораздо чаще сталкиваются с другой особенностью PHP: возможностью передать массив вместо ожидаемой строки.
На первый взгляд это кажется невозможным. Если приложение ожидает строку, откуда возьмется массив?
Ответ кроется в механизме обработки HTTP-параметров.
Если отправить запрос вида:
?token[]=123
то PHP автоматически создаст массив:
$_GET['token'] = ['123'];
То же самое работает для POST-параметров:
username=admin password[]=123
и даже для Cookie:
Cookie: session_data[]=test
После обработки запроса в $_COOKIE окажется уже массив. Многие разработчики забывают об этой особенности и предполагают, что пользователь всегда передаст строку. Именно здесь начинается самое интересное.
Особенности возвращаемых значений функций
Ранние версии PHP отличались довольно мягким отношением к ошибкам.
Многие встроенные функции при получении параметра неправильного типа не завершали выполнение программы. Вместо этого они выводили предупреждение (Warning) и возвращали NULL.

Одной из таких функций долгое время была hash_hmac().
Предположим, приложение проверяет подпись следующим образом:
if (hash_hmac('sha256', $_COOKIE['session'], $key) == $_COOKIE['hmac']) { // пользователь авторизован }
Все выглядит корректно.
Но если вместо строки передать массив:
Cookie: session[]=admin Cookie: hmac=
в старых версиях PHP произойдет следующее:
- hash_hmac() получит массив вместо строки.
- Функция выдаст Warning.
- Вернет NULL.
- Затем выполнится слабое сравнение.
Фактически получится:
NULL == ""
Для оператора == такое сравнение оказывается истинным.
В результате проверка подписи завершается успешно, хотя пользователь не предоставил корректный HMAC. Именно такой сценарий часто встречается в учебных лабораториях и хорошо демонстрирует, почему опасно одновременно использовать слабое сравнение и не проверять типы входных данных.
Ограниченность исправлений в PHP 8
С выходом PHP 8 разработчики значительно пересмотрели правила нестрогого сравнения.
Например, конструкция
"admin" == 0
теперь возвращает false, тогда как в PHP 7 результатом было true.
Это устранило множество неожиданных сравнений строк и чисел.
Однако важно понимать одну вещь.
Type Juggling не исчез полностью.
Во-первых, огромное количество корпоративных приложений до сих пор работает на PHP 7.4 и более ранних версиях.
Во-вторых, многие уязвимости возникают вовсе не из-за сравнения строки с числом, а из-за некорректной обработки NULL, массивов или результатов криптографических функций.
Поэтому при тестировании безопасности всегда необходимо учитывать используемую версию PHP.
Методика поиска Type Juggling при пентесте
Если у вас есть доступ к исходному коду, задача значительно упрощается.
В первую очередь стоит искать:
- оператор ==;
- оператор !=;
- вызовы md5(), sha1(), hash(), hash_hmac();
- проверки токенов;
- сравнение пользовательских данных с вычисленным хешем;
- проверки Cookie;
- нестандартную реализацию авторизации.
Если доступен только веб-интерфейс, полезно попробовать изменить тип входных данных.
Например:
- заменить строку массивом;
- отправить несколько параметров с одинаковым именем;
- использовать JSON-массив вместо строки в API;
- проверить обработку пустых значений;
- обратить внимание на Warning в ответах сервера.
Иногда простое изменение типа параметра позволяет продвинуться значительно дальше по сценарию авторизации.
Характерные признаки CTF-задач
После решения нескольких подобных задач такие уязвимости начинают узнаваться практически сразу.
Наиболее распространенные признаки:
- приложение написано на PHP;
- используется md5() или sha1();
- в коде присутствует ==;
- сравниваются HMAC или токены;
- параметр можно передать в виде массива;
- сервер работает на старой версии PHP.
Во многих CTF этого уже достаточно, чтобы понять направление дальнейшей эксплуатации.
Например, увидев конструкцию
if ($_POST['password'] == md5($secret))
стоит задуматься не только о подборе значения, но и о том, как повлиять на тип сравниваемых данных.
Меры защиты
Хорошая новость заключается в том, что большинство подобных уязвимостей устраняется достаточно простыми правилами.
Минимальный набор рекомендаций выглядит так:
- всегда использовать === и !== вместо == и !=;
- проверять тип пользовательских данных до выполнения бизнес-логики;
- использовать hash_equals() для сравнения криптографических значений;
- не полагаться на автоматическое приведение типов;
- обновлять приложения до современных версий PHP;
- явно отклонять массивы там, где ожидается строка;
- использовать строгий режим анализа кода и статические анализаторы.
Отдельно стоит упомянуть функцию in_array(). По умолчанию она также использует нестрогое сравнение.
Вместо
in_array($value, $array)
следует использовать
in_array($value, $array, true)
Третий параметр включает строгое сравнение и устраняет целый класс логических ошибок.
Ключевые выводы
Type Juggling — отличный пример того, как удобная особенность языка превращается в проблему безопасности.
Само по себе автоматическое приведение типов не является уязвимостью. Уязвимость появляется тогда, когда разработчик использует его при проверке данных, от которых зависит безопасность приложения: паролей, HMAC, токенов или результатов криптографических функций.
Для специалиста по безопасности эта тема важна по двум причинам. Во-первых, подобные ошибки до сих пор встречаются в устаревших приложениях и регулярно появляются в CTF. Во-вторых, они учат анализировать не только значения параметров, но и их типы. Иногда достаточно передать массив вместо строки или воспользоваться особенностями нестрогого сравнения, чтобы полностью изменить логику работы приложения.
Именно поэтому при анализе PHP-кода стоит воспринимать каждый оператор == как потенциальную точку внимания. В большинстве случаев он окажется безобидным. Но если рядом выполняется проверка аутентификации, подписи или хеша, стоит разобраться подробнее — возможно, перед вами именно та логическая ошибка, которая приведет к успешной эксплуатации.
Практическое задание
Теории о PHP Type Juggling недостаточно. Чтобы уверенно находить такие ошибки при тестировании безопасности, важно самостоятельно пройти весь путь: проанализировать приложение, сформулировать гипотезу, проверить ее и добиться успешной эксплуатации.
Для этого подойдет задача «У Джамшута» на платформе ONE TASK.
По сюжету ваш знакомый Джамшут, владелец местной шаурмичной, предлагает необычную сделку: если найдете уязвимость в его веб-приложении, получите скидку на всю продукцию на целый год.
Задача построена так, чтобы участник самостоятельно обнаружил логическую ошибку и применил знания о PHP Type Juggling на практике. Это отличный способ закрепить материал статьи и понять, как подобные уязвимости выглядят не в искусственном примере из документации, а в приложении, максимально приближенном к реальному.
Для получения доступа к заданию зарегистрируйтесь на бесплатный курс «Профессия Белый Хакер» и откройте раздел ONE TASK.
Совместный разбор задач с экспертом
Если с первого раза решить задачу не получилось — это нормально. Именно поэтому для участников бесплатного курса «Профессия Белый Хакер» регулярно проводят открытые разборы задач ONE TASK.
На вебинарах эксперт не просто демонстрирует готовый ответ, а последовательно объясняет ход исследования: на какие детали обратить внимание, как проверять гипотезы, где обычно допускают ошибки и почему некоторые направления поиска оказываются тупиковыми.
Такой формат помогает понять логику работы специалиста по безопасности, а не просто запомнить конкретный эксплойт. Со временем именно этот навык позволяет быстрее находить уязвимости и увереннее решать более сложные CTF и реальные задачи по анализу защищенности.
Следите за анонсами вебинаров, регистрируйтесь на бесплатный курс «Профессия Белый Хакер» и используйте ONE TASK как регулярную практику для развития навыков offensive security.
