Una piattaforma di apprendimento che consente agli studenti di comporre query MongoDB ha abbandonato il modulo vm di Node.js e ora analizza ogni query in un albero di sintassi astratta (AST) prima dell'esecuzione. Il cambiamento rimuove un sandbox che poteva essere violato, proteggendo il backend da codice arbitrario che una stringa malformata potrebbe iniettare.
Perché il sandbox originale è fallito
La prima implementazione avvolgeva un handle del database attivo in una chiamata vm.runInContext e cercava di limitare gli utenti tramite un'espressione regolare. Consentiva solo nomi di metodi come find o aggregate; ogni altro identificatore doveva essere filtrato.
Due difetti rendevano quell'approccio insicuro:
- Il filtraggio tramite regex è aggirabile. JavaScript consente al codice di utilizzare la notazione tra parentesi quadre (
obj["constructor"]) per raggiungere qualsiasi proprietà. Un attaccante può recuperare il costruttoreFunction, creare una nuova funzione ed eseguire qualsiasi codice desideri. La regex non vede mai l'accesso alla proprietà sottostante perché il codice sorgente può essere riscritto in innumerevoli modi. vmnon è un confine di sicurezza. La documentazione di Node afferma chevmisola l'oggetto global ma non l'intero processo. Iniettando una connessione al database attiva nel sandbox, il codice all'interno del contesto manteneva la capacità di chiamare qualsiasi metodo su quella connessione, inclusi quelli che scrivono o eliminano dati. Il sandbox non impediva al codice di influenzare il processo host.
La soluzione basata su AST
Il team ha sostituito l'esecuzione del codice con l'analisi statica. Le stringhe delle query vengono ora elaborate dal parser acorn, che produce un AST — una rappresentazione ad albero della struttura sintattica del codice. L'AST viene esaminato nodo per nodo rispetto a una whitelist rigorosa:
- Letterali, array e oggetti sono consentiti solo quando appaiono come valori semplici.
- Le chiamate ai metodi sono limitate a un set predefinito (
find,sort,limit, ecc.). Qualsiasi altra chiamata viene rifiutata. - L'accesso a proprietà calcolate (ad es.
obj[expr]) o qualsiasi tipo di nodo non esplicitamente elencato provoca un errore immediato.
Poiché il parser lavora sull'albero e non sul testo grezzo, non può essere ingannato da varianti di scrittura o trucchi con la notazione tra parentesi quadre. Una catena di costruttori che sarebbe sfuggita alla regex appare come un nodo non riconosciuto e viene rifiutata prima che venga eseguito qualsiasi codice.
Cosa significa per la sicurezza
Il nuovo design segue una filosofia di "negazione predefinita" (deny by default):
- Definisci ciò che è permesso, non ciò che è proibito. L'allow-listing testuale non può essere esaustivo; un AST ha un set finito di tipi di nodi, rendendo fattibile un controllo completo.
- Non esporre mai risorse attive all'interno di un sandbox. Passare un handle del database in un contesto isolato fornisce al codice nel sandbox una linea diretta verso il backend. L'approccio basato sul parser non passa mai un oggetto attivo al codice dell'utente; estrae solo l'intento della query.
- Valida la struttura, quindi esegui in sicurezza. Una volta che l'AST supera la validazione, la piattaforma traduce le chiamate consentite in effettivi metodi del driver MongoDB utilizzando il proprio percorso di codice affidabile.
Cosa monitorare in seguito
- Effettua un audit di ogni utilizzo di
eval,new Functionovmnel tuo codebase. Anche una whitelist può essere sovvertita dalla natura dinamica di JavaScript. - Adotta il parsing AST per il codice generato dagli utenti ove possibile. Librerie come
acorn,esprimaobabel-parserrendono la trasformazione semplice. - Limita l'esposizione degli oggetti attivi. Se una connessione al database, un handle di un file o un socket di rete deve essere raggiungibile, racchiudilo in un proxy che esponga solo i metodi minimi che intendi consentire.
- Automatizza il testing dei casi limite. Genera query che utilizzano la notazione tra parentesi quadre, chiavi calcolate o manipolazione del prototipo per verificare che il tuo parser le rifiuti.
Conclusione
Affidarsi a vm.runInContext come sandbox fornisce un falso senso di sicurezza; i filtri regex non possono coprire la sintassi flessibile di JavaScript e il sandbox non isola le risorse del processo. Analizzare l'input dell'utente in un AST e inserire in whitelist solo i nodi che comprendi fornisce una barriera concreta e manutenibile che blocca il codice malevolo prima che venga eseguito. Se la tua piattaforma consente agli utenti di scrivere codice, sostituisci oggi stesso l'esecuzione in stile eval con l'analisi statica.
