Проблемы сериализации при атаке «Звезды смерти»
2017/02/26Оказался я пару дней назад в весьма неприятной ситуации.
Есть огромный SQL-дамп базы проекта, который работал на сервере под управлением 64-битной ОС.
Некоторые поля записей содержат данные, обработанные функцией serialize()
.
После разворачивания дампа БД на сервере с 32-битной ОС проект оказался частично нерабочим: попытки десериализации некоторых данных заканчивались неудачей. Я бы махнул на это рукой, но требования к работоспособности были достаточно четкие: «шоб везде».
Разберемся для начала, откуда вообще растут ноги проблемы.
Многие со школы помнят следующую формулу:
Imax = 2n – 1 – 1,
где:
- n — разрядность системы,
- Imax — максимальное значение знакового целого числа, возможного в данной разрядности.
Так, для 32-битной системы, Imax равно 2147483647), а для 64-битной — 9223372036854775807.
Результаты сериализации чисел, значение которых превышает выше упомянутое 2147483647, для разных ОС также будут различными. Рассмотрим на примере 4294967296:
vagrant@trusty64:~ $ php -r 'var_dump(serialize(4294967296));'
string(13) "i:4294967296;"
vagrant@trusty32:~ $ php -r 'var_dump(serialize(4294967296));'
string(13) "d:4294967296;"
Как видно, на 64-битной системе число было сериализовано с типом i
, integer (целое число), а на 32-битной — c типом d
, double precision (число двойной точности).
Соответственно, при попытке десериализации i:4294967296;
на 32-битной ОС будет предпринята попытка поместить в целое число значение превышающее максильно допустимое, что приведет к возникновению ошибки.
Посмотрев на вывод функции, я решил подойти к решению проблемы в лоб и заменить в дампе все целочисленные значения на значения двойной точности:
vagrant@trusty64:~ $ sed -i.bak -E -e 's/i:([0-9]+)\;/d:\1\;/g' dump.sql
Однако это не обеспечило работоспособности приложения, напротив, породило только большее количество ошибок.
Вместо этого я решил положиться на утиную типизацию в PHP и заменил все числовые значения на строки: так, i:4294967296;
стало s:10:"4294967296";
.
В процессе я накидал простой скрипт facepalm.php: он проходится по скормленному ему файлу и заменяет все представления целых чисел строками. Этим скриптом я прошерстил дамп проекта, что позволило завести его как в 32-, так и в 64-битной ОС.