Как заменить/экранировать символы U+2028 или U+2029 в PHP, чтобы остановить взлом моего API JSONP


Хорошо, я запускаю общедоступный API JSONP, данные которого передаются с моего PHP-сервера. Я только что прочитал эту статью:

В принципе, если мои строки JSON содержат символ U+2028 (разделитель строк Юникода) или символ U+2029 (разделитель абзацев Юникода), то это вполне допустимый JSON. Однако при использовании JSONP JSON выполняется как JavaScript и ни одна строка в JavaScript не может содержать литерал U+2028 или U+2029, так как это нарушит JavaScript. По-видимому, это обычно не проблема, если вы используете правильный синтаксический анализатор JSON, но в случае JSONP браузер является синтаксическим анализатором JSON.

По сути, если бы эти символы находились внутри строк в моих данных JSONP, отправляемых клиенту, это привело бы к разрыву строки или абзаца в строке, что нарушило бы JavaScript и остановило бы его выполнение. Это возможность, поскольку API отправляет обратно некоторые введенные клиентом данные. Кто-то потенциально может ввести U+2028 или U+2029 в базу данных, поэтому, когда я отправлю это обратно в виде JSONP, это нарушит любую реализацию с использованием моего API.

Итак, мой вопрос в том, как в PHP я могу очистить/вывести данные JSON, чтобы удалить или избежать символов U+2028 и U+2029 перед отправкой клиенту?

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

Другое дело, что я все равно не уверен, как избежать символов U+2028 и U+2029 в PHP. Могу ли я просто сделать str_replace? Я не уверен, что str_replace безопасен для нескольких байтов, и нет функции mb_str_replace, если я не использую какую-то специально созданную функцию. Итак, как вы удаляете/экранируете эти символы юникода?

Большое спасибо.

Author: hakre, 2013-01-06

2 answers

Вы можете заменить U+2028, U+2029 с "\u2028", "\u2029" либо на стороне PHP, либо на стороне JavaScript, либо на обоих, это не имеет значения, если это произойдет хотя бы один раз (это идемпотентно).

Вы можете просто использовать обычные функции замены строк. Они не должны быть "многобайтовыми безопасными", и вы можете сделать это так же легко в любой кодировке Юникода (UTF-8, UTF-16, UTF-32 одинаково хороши). В PHP не было escape-последовательностей Юникода, когда я проверял в прошлый раз, и это еще одна причина, по которой PHP - это шутка, но вы можете использовать \x побег с UTF-8...

(Короче говоря, причина отсутствия функции замены многобайтовой строки заключается в том, что она была бы избыточной - она была бы точно такой же, как функция замены не многобайтовой строки.)

// Javascript
data = data.replace("\u2028", "\\u2028").replace("\u2029", "\\u2029");

// PHP
$data = str_replace("\xe2\x80\xa8", '\\u2028', $data);
$data = str_replace("\xe2\x80\xa9", '\\u2029', $data);

Или вы могли бы вообще ничего не делать, так как PHP по умолчанию экранирует символы, отличные от Юникода, в json_encode():

// Safe
echo json_encode("\xe2\x80\xa9");
--> "\u2029"

// Correct JSON, but invalid Javascript...
// (Well, technically, JSON root must be array or object)
echo json_encode("\xe2\x80\xa9", JSON_UNESCAPED_UNICODE);
--> "
"
 19
Author: Dietrich Epp, 2013-07-08 19:52:22

Стоит отметить, что в этом больше нет необходимости.

По умолчанию, json_encode() кодирует все символы, отличные от ASCII (включая U+2028 и U+2029), а также избегает прямой косой черты, хотя это не обязательно должно быть экранировано спецификацией JSON. Избежать этого не повредит, и в определенных контекстах это может быть безопаснее. Таким образом, по умолчанию эти символы все равно экранируются.

Константа JSON_UNESCAPED_UNICODE выводит неэкранированный Юникод, который может экономить байты. Однако, просто поскольку символ косой черты экранирован, потому что он может быть опасным в некоторых контекстах, то и U+2028 и U+2029 также экранированы, потому что они тоже опасны в некоторых контекстах. Это было не так в то время, когда вы задавали свой вопрос: эта функция была добавлена в PHP совсем недавно.

(Эти дополнительные побеги можно отключить с помощью JSON_UNESCAPED_SLASHES и JSON_UNESCAPED_LINE_TERMINATORS соответственно.)

 0
Author: TRiG, 2017-07-14 16:35:54