2010-05-05 3 views
18

Je voudrais offrir un webservice via JSONP et je me demandais, si j'ai besoin d'assainir la valeur du paramètre de rappel.Ai-je besoin de désinfecter le paramètre de rappel d'un appel JSONP?

Mon script courant côté serveur ressemble à ceci actuellement (plus ou moins de code est en PHP, mais pourrait être quelque chose de vraiment..):

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

Ceci est un XSS-vulnérabilité classique.

Si j'ai besoin de le désinfecter, alors comment? J'ai été incapable de trouver suffisamment d'informations sur ce qui pourrait être autorisé des chaînes de rappel. Je cite Wikipedia:

Alors que le rembourrage (préfixe) est généralement le nom d'une fonction de rappel qui est définie dans le contexte d'exécution du navigateur, il peut également être une affectation de variable, une instruction if, ou tout autre préfixe d'instruction Javascript.

Répondre

10

Oui, quand callback est comme

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

Lorsque vous Retransmet de php, semblera comme

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

courront et le mal() peut être certains codes d'envoi des cookies ailleurs.

Ainsi, Désinfectez les noms de fonction valides uniquement, par exemple, limiter à caractères alphanumériques

+0

semble bon. Je vais essayer ça. –

+3

Vous pouvez utiliser ctype_alpha ($ _ GET [ « callback »]) pour vérifier facilement dans ce cas, si vous êtes sur un lieu anglais, sinon les fonctions de filtrage fonctionnent bien. Notez également que l'en-tête doit être défini sur 'content-type: application/json; charset = utf-8 'lors du retour d'une réponse jsonp. – Josh

+0

Fixé. Et merci pour l'information. –

3

Oui. Puisque JSONP est essentiellement une attaque XSS auto-infligée (insérer (ou non temporairement) une étiquette de script sur un autre nom d'hôte et la laisser appeler une fonction ou une méthode globale d'un objet global), il est important de prendre au moins quelques précautions vous limitez le "callback" pour n'être plus qu'un callback.

Donc, fondamentalement, tout identifiant valide. Et vous pourriez faire une exception pour les membres d'objet. Je ne recommanderais pas d'autoriser les parenthèses à rester simples, car cela permet des invocations de fonctions et ce n'est pas le cas.

Voici un exemple de création d'une API de base qui prend en charge à la fois JSON et JSONP. L'exemple ci-dessous est en PHP (simplifié par le fonctionnement de l'API de MediaWiki), mais des structures similaires peuvent être créées dans d'autres langages de programmation.

<?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

Vous voulez vous assurer que le rappel est un identifiant valide, qui peut être alphanumérique, soulignement ou $. Il ne peut pas non plus être un mot réservé (et juste pour être complet je m'assurerais que ce n'est pas undefined, NaN, ou Infinity). C'est le test que j'utilise:

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

Beaucoup de mots réservés sont inutiles, mais certains d'entre eux pourrait provoquer des erreurs ou des boucles infinies.

Important: ne pas simplement nettoyer l'entrée en remplaçant les caractères; le rappel modifié peut s'exécuter sans erreur et les données renvoyées ne seront pas traitées correctement (ou pourraient même être traitées par la mauvaise fonction). Vous voulez tester si l'entrée est valide, et lancer une erreur si ce n'est pas le cas. Cela évitera un comportement inattendu et informera le développeur qu'un rappel différent est nécessaire.

note: Ceci est une version plus sûre, mais limitée, de JSONP qui ne permet pas d'expressions ou de raffinement. J'ai trouvé que cela fonctionne très bien pour la plupart des applications, en particulier si vous utilisez jQuery et $.getJSON

4

Oui.

Comme décrit par @YOU, un attaquant pourrait créer un paramètre de rappel qui évalue à un javascript malveillant, ou pire, malicious Flash.

que le rappel VALIDATION DE n'est pas un mot réservé et est alphanumérique tel que décrit par @ Brett-Wejrowski est un bon début.

Google, Facebook et Github atténuent la vulnérabilité Rosetta Flash en pré-attente un commentaire vide, comme/** /, au rappel jsonp.

Une autre approche serait de retourner une expression javascript plus sûr comme express.js fait:

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