2010-05-05 3 views
18

Vorrei offrire un servizio web tramite JSONP e mi chiedevo se fosse necessario disinfettare il valore dal parametro callback.Devo disinfettare il parametro di callback da una chiamata JSONP?

Il mio script lato server attuale assomiglia a questo momento (più o meno Codice è in PHP, ma potrebbe essere qualsiasi cosa davvero..):

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

Questo è un classico XSS-vulnerabilità.

Se ho bisogno di igienizzare, allora come? Non sono riuscito a trovare abbastanza informazioni su quali potrebbero essere le stringhe di callback consentite. Cito da Wikipedia:

mentre l'imbottitura (prefisso) è in genere il nome di una funzione di callback che viene definito all'interno del contesto di esecuzione del browser, ma può anche essere un assegnamento di variabile, un'istruzione if, o qualsiasi altro prefisso dell'istruzione Javascript.

risposta

10

Sì, quando callback è come

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

Quando si echo indietro da PHP, si assomiglia

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

funzione XSS verrà eseguito e il male() può essere alcuni codici l'invio di biscotti da qualche altra parte.

Quindi, disinfettare a nomi di funzione solo validi, ad esempio, limitare il sistema ai alfanumerici

+0

sembra buono. Ci proverò. –

+3

Puoi usare ctype_alpha ($ _ GET ['callback']) per controllare facilmente in questo caso se sei su un locale inglese, altrimenti le funzioni di filtro funzionano bene. Si noti inoltre che l'intestazione deve essere impostata su 'content-type: application/json; charset = utf-8 'quando si restituisce una risposta jsonp. – Josh

+0

corretto. E grazie per l'informazione. –

3

Sì. Poiché JSONP è fondamentalmente un attacco XSS autoinflitto ((con o senza temporaneamente) l'inserimento di un tag script su un altro hostname e il suo richiamo a una funzione o metodo globale di un oggetto globale), è importante prendere almeno alcune precauzioni che si limita la "richiamata" ad essere nient'altro che una richiamata.

Quindi praticamente qualsiasi identificativo valido. E potresti fare un'eccezione per i membri degli oggetti. Non consiglierei di consentire alle parentesi di mantenerlo semplice, dal momento che consente le invocazioni delle funzioni e quali no.

Ecco un esempio di come creerei un'API di base che supporti sia JSON che JSONP. L'esempio seguente è in PHP (semplificato da come funziona l'API di MediaWiki), ma strutture simili possono essere create in altri linguaggi di programmazione.

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

Si desidera garantire che il callback sia un identificatore valido, che può essere alfanumerico, di sottolineatura o $. Inoltre, non può essere una parola riservata (e per essere esauriente mi assicurerei che non sia undefined, NaN o Infinity). Questa è la prova che uso:

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

Molte delle parole riservate sono inutili, ma alcuni di loro potrebbe causare errori o cicli infiniti.

Importante: non solo disinfettare l'input sostituendo i caratteri; il callback modificato potrebbe essere eseguito senza errori e i dati restituiti non verranno gestiti correttamente (o potrebbero anche essere gestiti dalla funzione errata). Vuoi verificare se l'input è valido e lanciare un errore se non lo è. Ciò eviterà comportamenti imprevisti e notificherà allo sviluppatore che è necessaria una richiamata diversa.

nota: questa è una versione più sicura ma limitata di JSONP che non consente espressioni o perfezionamento. Ho trovato funziona bene per la maggior parte delle applicazioni, soprattutto se si utilizza jQuery e $.getJSON

4

Sì.

Come descritto da @YOU, un utente malintenzionato può creare un parametro di callback che restituisce un javascript dannoso o, peggio, malicious Flash.

Convalidare che il callback non è una parola riservata ed è alfanumerico come descritto da @ Brett-Wejrowski è un buon inizio.

Google, Facebook e Github attenuano la vulnerabilità di Rosetta Flash anticipando un commento vuoto, come/** /, al callback di jsonp.

Un altro approccio sarebbe quello di restituire un espressione javascript più sicuro come ExpressJS fa:

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