Нужно ли мне дезинфицировать параметр обратного вызова из вызова JSONP?

Я хотел бы предложить веб-сервис через JSONP и задавался вопросом, нужно ли мне дезинфицировать значение из параметра обратного вызова.

Текущая серверная сторона script выглядит как это в настоящее время (больше или меньше). Код находится в PHP, но может быть что-то действительно.):

header("Content-type: application/json; charset=utf-8");
echo $_GET['callback'] . '(' . json_encode($data) . ')';

Это классическая уязвимость XSS.

Если мне нужно его дезинфицировать, то как? Мне не удалось найти достаточно информации о том, что может быть разрешено для строк обратного вызова. Я цитирую из Wikipedia:

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

Ответ 1

Да, когда callback похож на

(function xss(x){evil()})

Когда вы возвращаетесь обратно с php, будет выглядеть как

(function xss(x){evil()})(json)

функция xss будет запущена, а evil() может быть некоторыми кодами, отправляющими файлы cookie в другое место.

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

Ответ 2

Вы хотите, чтобы обратный вызов был действительным идентификатором, который может быть буквенно-цифровым, подчеркивающим или $. Он также не может быть зарезервированным словом (и только для того, чтобы быть тщательным, я бы удостоверился, что это не undefined, NaN, или Infinity). Это тест, который я использую:

function valid_js_identifier( $callback ){
    return !preg_match( '/[^0-9a-zA-Z\$_]|^(abstract|boolean|break|byte|case|catch|char|class|const|continue|debugger|default|delete|do|double|else|enum|export|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|volatile|void|while|with|NaN|Infinity|undefined)$/', $callback);
}

Многие зарезервированные слова бессмысленны, но некоторые из них могут вызывать ошибки или бесконечные циклы.

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

Примечание. Это более безопасная, но ограниченная версия JSONP, которая не позволяет выражать или уточнять. Я нашел, что он отлично работает для большинства приложений, особенно если вы используете jQuery и $.getJSON

Ответ 3

Да.

Как описано @YOU, злоумышленник может создать параметр обратного вызова, который оценивает вредоносный javascript или, что еще хуже, вредоносная Flash.

Утверждение, что обратный вызов не является зарезервированным словом и является буквенно-цифровым, как описано в Brett-Wejrowski, является хорошим началом.

Google, Facebook и Github смягчают уязвимость Rosetta Flash, предварительно ожидая пустой комментарий, например /**/, к обратному сообщению jsonp.

Другим подходом было бы вернуть более безопасное выражение JavaScript, например ExpressJS:

typeof callbackstring === 'function' && callbackstring(.....);

Ответ 4

Да, вам нужно очистить параметр обратного вызова.

JSONP - это, по сути, атака XSS, на которую наложены сами. Когда (временно) вставка тега script с URL-адресом к другому имени хоста и предоставление ему возможности вызывать глобальную функцию или метод на вашей странице, важно, по крайней мере, принять некоторые меры предосторожности, чтобы ограничить "обратный вызов" не более чем обратным вызовом название.

Имя обратного вызова, синтаксически, должно быть таким же простым, как и идентификатор. Вы можете учесть свойства объекта. Я бы рекомендовал не разрешать использование скобок, поскольку это может привести к вызовам функций и т.д.

Ниже приведен пример базового API, который поддерживает JSON и JSONP. Он написан на PHP (упрощенно от MediaWiki API), но подобные структуры могут быть созданы на других языках программирования.

<?php

// Simulate the response data
$responseData = [
    'foo' => 'bar',
    'count' => ['one', 'two', 'three'],
    'total' => 3,
];

// Prepare to send the response
$prefix = '';
$suffix = '';
$ctype = 'application/json';

if (isset($_GET['callback'])) {
    $ctype = 'text/javascript';
    // Sanitize callback
    $callback = preg_replace("/[^][.\\'\\\"_A-Za-z0-9]/", '', $_GET['callback']);

    $prefix = $callback . '(';
    $suffix = ')';
}

// Send the response
header("Content-Type: $ctype; charset=UTF-8", true);
print $prefix . json_encode($responseData) . $suffix;
exit;