Gioca in sicurezza negli iframe con sandbox

Per creare un'esperienza completa sul web odierno, è quasi inevitabile che incorporare componenti e contenuti sui quali non hai alcun reale controllo. I widget di terze parti possono favorire il coinvolgimento e svolgere un ruolo fondamentale nel contesto complessivo esperienza utente e i contenuti generati dagli utenti a volte sono ancora più importanti rispetto ai contenuti nativi di un sito. L'astensione da una delle due non è un'opzione, ma entrambi aumentano il rischio che si verifichi qualcosa di bruttaTM sul vostro sito. Ciascuna widget che incorpori (ogni annuncio, ogni widget di social media) è un potenziale vettore d’attacco per utenti malintenzionati:

Criterio di sicurezza del contenuto (CSP) possono mitigare i rischi associati a entrambi questi tipi di contenuti offrendo è possibile autorizzare fonti di script specifiche affidabili e altre contenuti. Si tratta di un grande passo nella giusta direzione, ma vale la pena notare che la protezione offerta dalla maggior parte delle direttive CSP è binaria, ovvero consentito o non lo è. Ci sono momenti in cui sarebbe utile dire "Non sicuro che mi fido di questa fonte di contenuti, ma è troppo bello. Incorpora per favore, Browser, ma non lasciare che il mio sito venga danneggiato".

Privilegio minimo

In sostanza, stiamo cercando un meccanismo che ci permetta di concedere ai contenuti Deve incorporare solo il livello minimo di capacità necessario a svolgere il proprio lavoro. Se un widget non deve aprire una nuova finestra popup, rimuovendo così l'accesso a window.open ferito. Se non richiede Flash, la disattivazione del supporto dei plug-in non dovrebbe essere problema. Siamo più sicuri possibile se seguiamo il principio di almeno privilegio e bloccare ogni singola funzione non direttamente pertinente con quella che vorremmo per l'utilizzo. Il risultato è che non dobbiamo più fidarci ciecamente del fatto che di contenuti incorporati non sfruttano privilegi che non dovrebbe utilizzare. it non avranno accesso a queste funzionalità.

Gli elementi iframe sono il primo passo verso un buon framework per una soluzione di questo tipo. Il caricamento di un componente non attendibile in un iframe fornisce una misura di separazione tra la tua applicazione e i contenuti che vuoi caricare. I contenuti all'interno di frame non avrà accesso al DOM della tua pagina o ai dati che hai archiviato localmente, né essere in grado di disegnare in posizioni arbitrarie sulla pagina; è limitato all'ambito il contorno del frame. Tuttavia, la separazione non è realmente robusta. La pagina contenuta continua ad avere una serie di opzioni per comportamenti fastidiosi o dannosi: la riproduzione automatica video, plug-in e popup sono la punta dell'iceberg.

Attributo sandbox dell'elemento iframe ci fornisce esattamente ciò di cui abbiamo bisogno per restringere i limiti dei contenuti racchiusi in frame. Possiamo indica al browser di caricare i contenuti di un frame specifico con privilegi limitati dell'ambiente, consentendo solo il sottoinsieme di capacità necessarie per fare le attività devono essere svolte.

Accorciare, ma verificare

"Tweet" di Twitter è un ottimo esempio di funzionalità che può essere incorporate in modo sicuro nel tuo sito tramite una sandbox. Twitter ti consente di incorporare tramite un iframe con il seguente codice:

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
        style="border: 0; width:130px; height:20px;"></iframe>

Per capire cosa possiamo bloccare, esaminiamo attentamente le funzionalità richiesto dal pulsante. L'HTML che viene caricato nel frame esegue una parte JavaScript dai server di Twitter e genera una finestra popup popolata da un tweeting quando selezionato. Questa interfaccia richiede l'accesso ai cookie per collegare il tweet all'account corretto e richiede la possibilità per inviare il modulo per i tweet. Questo è praticamente tutto: per il frame non serve caricare plug-in, non è necessario navigare nella finestra di primo livello o in nessuna di altri bit di funzionalità. Poiché non richiede questi privilegi, rimuoviamole con il sandboxing dei contenuti del frame.

La limitazione tramite sandbox funziona sulla base di una lista consentita. Iniziamo rimuovendo tutti autorizzazioni possibili, quindi riattiva le singole funzionalità aggiungendo alla configurazione della sandbox. Per il widget Twitter, abbiamo abbiamo deciso di attivare JavaScript, popup, invio di moduli e l'interfaccia cookie. Possiamo farlo aggiungendo un attributo sandbox a iframe con il valore seguente:

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
    src="https://platform.twitter.com/widgets/tweet_button.html"
    style="border: 0; width:130px; height:20px;"></iframe>

È tutto. Abbiamo fornito al frame tutte le funzionalità di cui ha bisogno e browser gli negherà utile l'accesso a qualsiasi privilegio per cui non abbiamo concederla esplicitamente tramite il valore dell'attributo sandbox.

Controllo granulare sulle funzionalità

Nell'esempio precedente abbiamo visto alcuni dei possibili flag di sandbox, ora i meccanismi interni dell'attributo in dettaglio.

Dato che un iframe con un attributo sandbox vuoto, il documento nel frame verrà completamente limitato tramite sandbox, sottoponendolo alle seguenti restrizioni:

  • JavaScript non verrà eseguito nel documento all'interno di frame. Non solo include JavaScript viene caricato esplicitamente tramite tag di script, ma anche gestori di eventi incorporati e JavaScript: URL. Ciò significa anche che i contenuti dei tag noscript esattamente come se l'utente avesse disattivato lo script.
  • Il documento all'interno di una cornice viene caricato in un'origine unica, il che significa che tutti i controlli della stessa origine non andranno a buon fine; uniche origini non corrispondono mai ad altre, persino loro stessi. Tra gli altri effetti, ciò significa che il documento non ha Accesso ai dati memorizzati nei cookie di qualsiasi origine o in qualsiasi altro meccanismo di archiviazione (archiviazione DOM, database indicizzato ecc.).
  • Il documento all'interno di una cornice non può creare nuove finestre o finestre di dialogo (tramite window.open o target="_blank", ad esempio).
  • Impossibile inviare moduli.
  • I plug-in non verranno caricati.
  • Il documento all'interno di una cornice può essere visualizzato solo e non in quello principale di primo livello. L'impostazione di window.top.location genererà un'eccezione e facendo clic sul link con target="_top" non produrrà alcun effetto.
  • Funzionalità che si attivano automaticamente (elementi del modulo incentrati automaticamente, riproduzione automatica video ecc.) sono bloccati.
  • Impossibile ottenere il blocco del puntatore.
  • L'attributo seamless viene ignorato nei contenuti iframes contenuti nel documento all'interno di frame.

È un po' draconiana e un documento è stato caricato in un iframe completamente sandbox comporta davvero pochi rischi. Ovviamente, non può essere molto utile: potrebbe riuscire a utilizzare una sandbox completa per alcuni contenuti statici, ma la maggior parte delle volte è consigliabile allentare le cose.

Ad eccezione dei plug-in, ognuna di queste restrizioni può essere rimossa aggiungendo un flag al valore dell'attributo sandbox. I documenti con sandbox non possono mai eseguire plug-in, poiché i plug-in sono codice nativo senza sandbox, ma tutto il resto è giusto gioco:

  • allow-forms consente l'invio di moduli.
  • allow-popups consente i popup (sconto!)
  • allow-pointer-lock consente (una sorpresa!) il blocco del puntatore.
  • allow-same-origin consente al documento di mantenere la sua origine. pagine caricate da https://example.com/ manterrà l'accesso ai dati di quell'origine.
  • allow-scripts consente l'esecuzione di JavaScript e le funzionalità di si attivano automaticamente (perché sarebbe semplice implementarlo tramite JavaScript).
  • allow-top-navigation consente al documento di uscire dall'inquadratura del durante la navigazione nella finestra di primo livello.

Tenendo conto di questi aspetti, possiamo valutare esattamente il motivo per cui siamo arrivati insieme di flag di sandbox nell'esempio di Twitter in alto:

  • allow-scripts è obbligatorio, perché la pagina caricata nel frame esegue alcune JavaScript per gestire l'interazione dell'utente.
  • allow-popups è obbligatorio, perché il pulsante fa apparire un modulo di tweeting in una nuova finestra.
  • Il campo allow-forms è obbligatorio, perché il modulo per i tweeting deve essere inviato.
  • La proprietà allow-same-origin è necessaria, come altrimenti i cookie di twitter.com essere inaccessibile e l'utente non è riuscito ad accedere per pubblicare il modulo.

Una cosa importante da notare è che i flag della sandbox applicati a un frame vengono applicate a tutte le finestre o i frame creati nella sandbox. Ciò significa che abbiamo per aggiungere allow-forms alla sandbox del frame, anche se il modulo esiste solo nella finestra che compare.

Con l'attributo sandbox attivo, il widget riceve solo le autorizzazioni che e funzionalità come plug-in, navigazione principale e blocco del puntatore rimangono bloccato. Abbiamo ridotto il rischio di incorporare il widget, senza effetti negativi. È un vantaggio per tutti.

Separazione dei privilegi

Limitazione tramite sandbox dei contenuti di terze parti per eseguire codice non attendibile in un un ambiente con privilegi limitati è ovviamente vantaggioso. Ma che dire codice? Ti fidi di te, vero? Perché preoccuparsi della sandbox?

Ritengo la domanda: se il codice non ha bisogno di plug-in, perché dargli l'accesso ai plug-in? Nella migliore delle ipotesi, è un privilegio che non usi mai, nel peggiore dei casi è un un potenziale vettore per gli aggressori per entrare nella porta. Il codice di ognuno ha bug e praticamente ogni applicazione è vulnerabile allo sfruttamento in un modo o un altro. La limitazione tramite sandbox del proprio codice implica che, anche se un utente malintenzionato riesce la tua applicazione, non avrà accesso completo al l'origine dell'applicazione; Potranno svolgere solo attività che l'applicazione che possono fare. Ancora pessimo, ma non così male.

Puoi ridurre ulteriormente il rischio suddividendo l'applicazione in e la limitazione tramite sandbox di ogni elemento con il minor privilegio possibile. Questa tecnica è molto comune nel codice nativo: Chrome, ad esempio, si rompe in un processo del browser con privilegi elevati che ha accesso al disco rigido locale e può effettuare connessioni di rete, e molti processi di renderer con privilegi limitati eseguire il lavoro pesante di analisi di contenuti non attendibili. I renderer non devono toccare disco, il browser si occupa di fornire tutte le informazioni necessarie eseguire il rendering di una pagina. Anche se un hacker astuto trova un modo per corrompere un renderer, non è arrivato molto lontano, perché il renderer non può fare molto interesse da solo: tutti gli accessi con privilegi elevati devono essere indirizzati tramite il processo del browser. Gli aggressori dovranno trovare diversi punti deboli in diverse parti del sistema fare qualche danno, il che riduce enormemente il rischio che il pegno vada a buon fine.

Sandboxing sicuro di eval()

Con la sandbox e API postMessage, il successo di questo modello è abbastanza semplice da applicare al web. Pezzi di la tua applicazione può trovarsi in iframe con sandbox e il documento principale può le comunicazioni tra di loro pubblicando messaggi e ascoltando diverse. Questo tipo di struttura garantisce che gli exploit in qualsiasi minimizzi i danni possibili. Ha anche il vantaggio di costringerti a creare punti di integrazione chiari, così sai esattamente dove devi essere e presta molta attenzione alla convalida di input e output. Vediamo un esempio di giocattolo solo per capire come potrebbe funzionare.

Evalbox è un'applicazione davvero entusiasmante che prende una stringa e la valuta come JavaScript. Wow, vero? Esattamente cosa che hai aspettato da tutti questi lunghi anni. È un percorso abbastanza pericoloso un'applicazione, ovviamente, poiché consentire l'esecuzione di JavaScript arbitrario significa che qualsiasi e tutti i dati che un'origine ha da offrire sono a portata di mano. Riducono il rischio di Bad ThingsTM si verifica assicurando che il codice venga eseguito all'interno di una sandbox, il che rende tutto un po' più sicuro. Lavoreremo per analizzare il codice partendo dai contenuti del frame:

<!-- frame.html -->
<!DOCTYPE html>
<html>
    <head>
    <title>Evalbox's Frame</title>
    <script>
        window.addEventListener('message', function (e) {
        var mainWindow = e.source;
        var result = '';
        try {
            result = eval(e.data);
        } catch (e) {
            result = 'eval() threw an exception.';
        }
        mainWindow.postMessage(result, event.origin);
        });
    </script>
    </head>
</html>

All'interno dell'inquadratura è presente una minima parte di documento che semplicemente ascolta i messaggi da quello principale collegandoti all'evento message dell'oggetto window. Ogni volta che il publisher principale esegue postMessage nei contenuti dell'iframe, questo evento verrà attivato, dandoci accesso alla stringa che il nostro padre vorrebbe eseguire il deployment.

Nel gestore, prendiamo l'attributo source dell'evento, che è l'elemento principale finestra. Lo utilizzeremo per rinviare il risultato del nostro duro lavoro una volta che avremo fatto. Quindi ci occuperemo del lavoro pesante, passando i dati che ci sono stati forniti eval(). Questa chiamata è stata raggruppata in un blocco di prova, poiché operazioni vietate all'interno di un iframe con sandbox genererà spesso eccezioni DOM; troveremo e segnalare un semplice messaggio di errore. Infine, pubblichiamo il risultato torna alla finestra principale. È una cosa piuttosto semplice.

Anche il genitore è semplice. Creeremo una UI molto piccola con un textarea per il codice e button per l'esecuzione, e estrarremo frame.html tramite con sandbox iframe, che consente solo l'esecuzione di script:

<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
        id='sandboxed'
        src='frame.html'></iframe>

Ora collegheremo le cose per l'esecuzione. Innanzitutto, ascolteremo le risposte iframe e alert() ai nostri utenti. Presumibilmente un'applicazione reale potrebbe fare qualcosa di meno fastidioso:

window.addEventListener('message',
    function (e) {
        // Sandboxed iframes which lack the 'allow-same-origin'
        // header have "null" rather than a valid origin. This means you still
        // have to be careful about accepting data via the messaging API you
        // create. Check that source, and validate those inputs!
        var frame = document.getElementById('sandboxed');
        if (e.origin === "null" &amp;&amp; e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

Successivamente, collegheremo un gestore di eventi ai clic sulla button. Quando l'utente clic, prendiamo i contenuti correnti di textarea e li passiamo alla frame per l'esecuzione:

function evaluate() {
    var frame = document.getElementById('sandboxed');
    var code = document.getElementById('code').value;
    // Note that we're sending the message to "*", rather than some specific
    // origin. Sandboxed iframes which lack the 'allow-same-origin' header
    // don't have an origin which you can target: you'll have to send to any
    // origin, which might alow some esoteric attacks. Validate your output!
    frame.contentWindow.postMessage(code, '*');
}

document.getElementById('safe').addEventListener('click', evaluate);

Facile, no? Abbiamo creato un'API di valutazione molto semplice e possiamo essere certi che il codice valutato non ha accesso a informazioni sensibili come i cookie o archiviazione DOM. Allo stesso modo, il codice valutato non può caricare plug-in, aprire nuove finestre popup, o qualsiasi altra serie di altre attività fastidiose o dannose.

Puoi fare lo stesso con il tuo codice suddividendo le applicazioni monolitiche in e componenti monofunzionali. Ognuno può essere aggregato in una semplice API di messaggistica, come abbiamo scritto sopra. La finestra principale con privilegi elevati può fungere da controller e supervisore, inviando i messaggi in moduli specifici, il minor numero di privilegi possibile per svolgere il proprio lavoro, ascoltare i risultati e assicurando che ogni modulo contenga solo le informazioni necessarie.

Tieni presente, tuttavia, che devi prestare molta attenzione quando hai a che fare con contenuti racchiusi in frame. che provengono dalla stessa origine dell'elemento principale. Se una pagina del https://example.com/ incornicia un'altra pagina sulla stessa origine con una sandbox che includa entrambi i flag allow-same-origin e allow-scripts la pagina nel frame può arrivare fino all'elemento principale e rimuovere l'attributo sandbox completamente.

Gioca nella tua sandbox

La limitazione tramite sandbox è ora disponibile in diversi browser: Firefox 17 e versioni successive, IE10+ e Chrome al momento della stesura (caniuse, naturalmente, ha una versione assistenza). Applicazione sandbox in corso... a iframes che includi ti consente di concedere determinati privilegi ai contenuti visualizzati, solo i privilegi necessari per che i contenuti funzionino correttamente. Questo ti offre l'opportunità di ridurre il rischio associati all'inclusione di contenuti di terze parti, oltre a ciò che è già possibile con Content Security Norme.

Inoltre, la sandbox è una tecnica molto efficace per ridurre il rischio l’autore dell’attacco sarà in grado di sfruttare le falle nel tuo codice. Separando una un'applicazione monolitica in un set di servizi sandbox, ciascuno responsabile di una piccola parte di funzionalità autonome, gli aggressori saranno costretti a compromettono solo frame specifici ma anche il loro titolare. Si tratta di un un'attività molto più difficile, soprattutto perché il controller può essere notevolmente ridotto nell'ambito di applicazione. Puoi dedicare le tue attività di sicurezza al controllo di quel codice se chiedi aiuto al browser per il resto.

Ciò non vuol dire che la sandbox sia una soluzione completa al problema del la sicurezza su internet. Offre una difesa approfondita e, a meno che tu non abbia il controllo sui tuoi utenti non puoi ancora fare affidamento sul supporto browser per tutti i tuoi utenti (se controlli i tuoi utenti, clienti: un ambiente aziendale, ad esempio... evviva!). Un giorno... ma per ora la sandbox è un altro livello di protezione avanzata per rafforzare le difese, non è una difesa completa su cui di cui ti puoi fidare. I livelli sono comunque eccellenti. Ti consiglio di usarlo uno.

Per approfondire

  • "Separazione dei privilegi nelle applicazioni HTML5" è un articolo interessante che lavora alla progettazione di un piccolo framework, e la relativa applicazione a tre applicazioni HTML5 esistenti.

  • La sandbox può essere ancora più flessibile se combinata con altri due nuovi iframe attributi: srcdoc, e seamless. La prima ti consente di completare un frame con contenuti senza l'overhead di una richiesta HTTP e quest'ultima consente allo stile di fluire nei contenuti inquadrati. Al momento entrambi hanno un supporto del browser piuttosto scadente (Chrome e WebKit) notturne). ma sarà una combinazione interessante in futuro. Potresti: ad esempio, commenti nella sandbox su un articolo tramite il seguente codice:

        <iframe sandbox seamless
                srcdoc="<p>This is a user's comment!
                           It can't execute script!
                           Hooray for safety!</p>"></iframe>