2010-05-05 4 views
18

Ich möchte einen Webservice über JSONP anbieten und frage mich, ob ich den Wert aus dem Callback-Parameter bereinigen muss.Muss ich den Rückrufparameter von einem JSONP-Aufruf bereinigen?

Mein aktueller Server-Seite Skript sieht wie folgt zur Zeit (mehr oder weniger-Code in PHP ist, aber wirklich alles sein könnte..):

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

Dies ist eine klassische XSS-Schwachstelle.

Wenn ich es sterilisieren muss, wie? Ich konnte nicht genügend Informationen darüber finden, was Callback-Strings zulässt. Ich führe aus Wikipedia:

Während die Polsterung (prefix) typischerweise der Name einer Rückruffunktion ist, die in den Ausführungskontext des Browsers definiert ist, es kann auch eine variable Zuordnung, eine if-Anweisung oder eine beliebige sein, anderes Javascript-Statement-Präfix.

Antwort

10

Ja, wenn callback wie

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

Wenn Sie von php echo zurück, wird aussehen wie

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

Funktion xss läuft und Böse() Senden einige Codes sein können Cookies woanders hin.

Also, sanieren sie nur gültige Funktionsnamen, zum Beispiel, es

+0

gut aussieht. Ich werde es versuchen. –

+3

Sie können ctype_alpha ($ _GET ['callback']) verwenden, um in diesem Fall leicht zu überprüfen, ob Sie in einem englischen Gebietsschema sind, ansonsten funktionieren die Filterfunktionen gut. Beachten Sie auch, dass die Kopfzeile auf 'content-type: application/json; charset = utf-8 ', wenn eine jsonp-Antwort zurückgegeben wird. – Josh

+0

Korrigiert. Und danke für die Information. –

3

Ja, um alphanumerische zu begrenzen. Da JSONP im Grunde ein selbst auferlegter XSS-Angriff ist (ob vorübergehend oder nicht), fügt er ein Skript-Tag in einen anderen Hostnamen ein und lässt es eine globale Funktion oder Methode eines globalen Objekts aufrufen), ist es wichtig, zumindest einige Vorsichtsmaßnahmen zu treffen Sie beschränken "Rückruf" auf nichts mehr als einen Rückruf.

Also im Grunde jede gültige Kennung. Und Sie könnten eine Ausnahme für Objektmitglieder machen. Ich würde nicht empfehlen, es den Klammern zu erlauben, es einfach zu halten, da dies Funktionsaufrufe erlaubt und was nicht.

Hier ist ein Beispiel, wie ich eine grundlegende API erstellen würde, die sowohl JSON als auch JSONP unterstützt. Das folgende Beispiel ist in PHP (vereinfacht wie die API von MediaWiki funktioniert), aber ähnliche Strukturen können auch in anderen Programmiersprachen erstellt werden.

<?php 

$responseData = array(
    'foo' => 'bar', 
    'count' => array('one', 'two', 'three'), 
    'total' => 3, 
); 

$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 = ')'; 
} 

header('Content-Type: ' . $ctype . '; charset=UTF-8', true); 

print $prefix . json_encode($responseData) . $suffix; 

exit; 
11

Sie möchten sicherstellen, dass der Rückruf ein gültiger Bezeichner ist, der alphanumerisch, Unterstrich oder $ sein kann. Es kann auch kein reserviertes Wort sein (und nur um gründlich zu sein, würde ich sicherstellen, dass es nicht undefined, NaN oder Infinity ist). Dies ist der Test, den ich verwenden:

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); 
} 

Viele der reservierten Wörter sind sinnlos, aber einige von ihnen könnten Fehler oder Endlosschleifen führen.

Wichtig: nicht nur die Eingabe durch Ersetzen von Zeichen bereinigen; Der modifizierte Rückruf könnte ohne Fehler ausgeführt werden, und die zurückgegebenen Daten werden nicht ordnungsgemäß verarbeitet (oder könnten sogar von der falschen Funktion verarbeitet werden). Sie möchten testen, ob die Eingabe gültig ist, und einen Fehler ausgeben, wenn dies nicht der Fall ist. Dies verhindert unerwartetes Verhalten und benachrichtigt den Entwickler, dass ein anderer Rückruf erforderlich ist.

Hinweis: Dies ist eine sicherere, aber eingeschränkte Version von JSONP, die keine Ausdrücke oder Verfeinerungen zulässt. Ich habe festgestellt, dass es für die meisten Anwendungen funktioniert, vor allem, wenn Sie jQuery und $.getJSON

4

Ja verwenden.

Wie von @YOU beschrieben, könnte ein Angreifer einen Rückrufparameter erstellen, der zu bösartigem JavaScript oder schlimmer noch malicious Flash führt.

Die Validierung, dass der Callback kein reserviertes Wort ist und alphanumerisch ist, wie von @ Brett-Wejrowski beschrieben, ist ein guter Anfang.

Google, Facebook und Github mindern die Rosetta Flash-Schwachstelle, indem sie einen leeren Kommentar wie/**/an den jsonp-Callback anhängen.

Ein anderer Ansatz wäre einen sichereren JavaScript-Ausdruck zurück wie Express.js tut:

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