Il sistema di estensioni di Chrome applica un Criterio di sicurezza del contenuto (CSP) predefinito piuttosto rigoroso.
Le restrizioni dei criteri sono chiare: lo script deve essere spostato fuori riga in file separati
I file JavaScript e i gestori di eventi incorporati devono essere convertiti per utilizzare addEventListener
e eval()
è
disattivata. Le app di Chrome hanno norme ancora più rigide e siamo molto soddisfatti della sicurezza
proprietà offerte da questi criteri.
Riconosciamo, tuttavia, che varie librerie utilizzano costrutti simili a eval()
e eval
, ad esempio
new Function()
per l'ottimizzazione delle prestazioni e la facilità di espressione. Le librerie di modelli
particolarmente incline a questo tipo di implementazione. Mentre alcuni (come Angular.js) supportano CSP
molti dei framework più diffusi non sono ancora stati aggiornati a un meccanismo compatibile
estensioni in meno di eval
. La rimozione del supporto per tale funzionalità si è quindi dimostrata maggiore
problematici del previsto per gli sviluppatori.
Questo documento introduce la sandbox come meccanismo sicuro per includere queste librerie nei tuoi progetti senza compromettere la sicurezza. Per brevità, utilizzeremo il termine estensioni in tutto, ma il concetto si applica anche alle applicazioni.
Perché la sandbox?
eval
è pericoloso all'interno di un'estensione perché il codice che esegue ha accesso a tutti gli elementi in
nell'ambiente con autorizzazioni elevate dell'estensione. Sono disponibili numerose API chrome.*
potenti che potrebbero
avere un impatto significativo sulla sicurezza e sulla privacy di un utente; l'esfiltrazione di dati semplice è l'ultima delle nostre preoccupazioni.
La soluzione offerta è una sandbox in cui eval
può eseguire il codice senza accedere
i dati dell'estensione o le API di alto valore dell'estensione. Nessun dato, nessuna API, nessun problema.
A tal fine, elenchiamo file HTML specifici all'interno del pacchetto dell'estensione come sottoposti a sandbox.
Ogni volta che una pagina con sandbox viene caricata, viene spostata in un'origine unica e viene negata
e l'accesso alle API di chrome.*
. Se carichiamo questa pagina con sandbox nella nostra estensione tramite un iframe
, possiamo
trasmetterlo, lasciarlo agire in qualche modo su quei messaggi e attendere che ci ritrasmetta
o il risultato finale. Questo semplice meccanismo di messaggistica ci offre tutto ciò di cui abbiamo bisogno per includere in modo sicuro le campagne eval
nel flusso di lavoro delle nostre estensioni.
Creazione e utilizzo di una sandbox.
Se vuoi approfondire direttamente il codice, recupera l'estensione di esempio della sandbox e segui disattivata. È un esempio pratico di una piccola API di messaggistica basata sui manubri di modelli e dovrebbe fornirti tutto ciò di cui hai bisogno per iniziare. Per chi di voi una spiegazione in più, analizziamo insieme l'esempio.
Elenca i file nel file manifest
Ogni file che deve essere eseguito all'interno di una sandbox deve essere elencato nel manifest dell'estensione aggiungendo un'etichetta
sandbox
proprietà. Si tratta di un passaggio fondamentale ed è facile da dimenticare, quindi controlla con attenzione
il file con sandbox è elencato nel file manifest. In questo esempio, stiamo eseguendo il sandboxing del file abilmente
denominato "sandbox.html". La voce del file manifest ha il seguente aspetto:
{
...,
"sandbox": {
"pages": ["sandbox.html"]
},
...
}
Carica il file con sandbox
Per poter fare qualcosa di interessante con il file con sandbox, dobbiamo caricarlo in un contesto in cui
può essere affrontato dal codice dell'estensione. In questo caso, sandbox.html è stato caricato
Pagina dell'evento (eventpage.html) dell'estensione tramite un iframe
. eventpage.js contiene codice
che invia un messaggio nella sandbox ogni volta che viene fatto clic sull'azione del browser cercando iframe
nella pagina ed eseguendo il metodo postMessage
nella tabella contentWindow
. Il messaggio è un oggetto
contenente due proprietà: context
e command
. Tra poco analizzeremo entrambi.
chrome.browserAction.onClicked.addListener(function() {
var iframe = document.getElementById('theFrame');
var message = {
command: 'render',
context: {thing: 'world'}
};
iframe.contentWindow.postMessage(message, '*');
});
postMessage
, consulta la documentazione di postMessage
relativa a MDN . È abbastanza completo e vale la pena leggerlo. In particolare, tieni presente che i dati possono essere trasmessi solo se sono serializzabili. Le funzioni, ad esempio, non lo sono.Fai qualcosa di pericoloso
Una volta caricato sandbox.html
, carica la libreria Handlebars, quindi crea e compila una
nel modo in cui Handlebars suggerisce:
<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
<div class="entry">
<h1>Hello, !</h1>
</div>
</script>
<script>
var templates = [];
var source = document.getElementById('hello-world-template').innerHTML;
templates['hello'] = Handlebars.compile(source);
</script>
Non fallire! Anche se Handlebars.compile
utilizza new Function
, l'operazione funziona
esattamente come previsto, per cui viene creato un modello compilato in templates['hello']
.
Ritrasmetti il risultato
Rendiamo disponibile questo modello impostando un listener di messaggi che accetti comandi
dalla pagina dell'evento. Utilizzeremo il valore command
trasmesso per determinare l'operazione da eseguire (potresti
immagina di fare qualcosa di più che eseguire il rendering; magari creando modelli? Magari gestendole in alcune
?) e context
verrà trasmesso direttamente al modello per il rendering. L'HTML sottoposto a rendering
verrà restituito alla pagina dell'evento in modo che l'estensione possa utilizzarlo in un secondo momento:
<script>
window.addEventListener('message', function(event) {
var command = event.data.command;
var name = event.data.name || 'hello';
switch(command) {
case 'render':
event.source.postMessage({
name: name,
html: templates[name](event.data.context)
}, event.origin);
break;
// case 'somethingElse':
// ...
}
});
</script>
Tornando alla pagina dell'evento, riceveremo questo messaggio e faremo qualcosa di interessante con l'html
dati che ci sono stati trasmessi. In questo caso, la segnaleremo tramite una notifica desktop, ma
puoi utilizzare questo codice HTML in tutta sicurezza nell'interfaccia utente dell'estensione. Inserendo il dispositivo tramite
innerHTML
non rappresenta un rischio per la sicurezza significativo, come nemmeno una compromissione completa della sandbox
mediante un attacco intelligente non sarebbe in grado di iniettare contenuti di script o plug-in pericolosi
il contesto dell'estensione con autorizzazione elevata.
Questo meccanismo rende semplice la creazione di modelli, ma ovviamente non si limita alla creazione di modelli. Qualsiasi codice che non funziona subito dopo un rigido criterio di sicurezza del contenuto può essere limitato tramite sandbox; nel Infatti, spesso è utile creare una sandbox per i componenti delle estensioni che sarebbero eseguiti correttamente, limitare ogni parte del programma al minor numero di privilegi necessari affinché correttamente. La presentazione Scrittura di app web sicure ed estensioni di Chrome di Google I/O 2012 offre alcuni buoni esempi pratici di queste tecniche e vale la pena di 56 minuti nel tempo.