Нужно ли дезинфицировать параметр обратного вызова из вызова JSONP? - PullRequest
18 голосов
/ 06 мая 2010

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

Мой текущий серверный скрипт выглядит следующим образом (более или менее. Код написан на PHP, но может быть чем угодно.):

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

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

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

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

Ответы [ 4 ]

11 голосов
/ 05 июня 2012

Вы хотите убедиться, что обратный вызов является допустимым идентификатором, который может быть буквенно-цифровым, подчеркиванием или $. Это также не может быть зарезервированным словом (и просто чтобы быть точным, я бы удостоверился, что это не 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

10 голосов
/ 06 мая 2010

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

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

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

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

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

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

4 голосов
/ 09 июля 2014

Да.

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

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

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

Другой подход - вернуть более безопасное выражение javascript, как это делает ExpressJS:

typeof callbackstring === 'function' && callbackstring(.....);
3 голосов
/ 11 января 2012

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

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;
...