Sandbox

La sandbox è uno strato di sicurezza che vi dà il controllo su quali tag, funzioni PHP, metodi e così via possono essere usati nei template. Grazie alla modalità sandbox potete collaborare in tutta sicurezza con un cliente o con un collaboratore esterno alla creazione dei template, senza temere che l'applicazione venga compromessa o che vengano eseguite operazioni indesiderate.

Come funziona? Definiamo semplicemente cosa vogliamo permettere nel template. All'inizio tutto è vietato e via via concediamo i permessi. Il codice seguente consente all'autore del template di usare i tag {block}, {if}, {else} e {=} (quest'ultimo è il tag per stampare una variabile o un'espressione) e tutti i filtri:

$policy = new Latte\Sandbox\SecurityPolicy;
$policy->allowTags(['block', 'if', 'else', '=']);
$policy->allowFilters($policy::All);

$latte->setPolicy($policy);

Possiamo anche consentire l'accesso a singole funzioni globali, a metodi o a proprietà degli oggetti:

$policy->allowFunctions(['trim', 'strlen']);
$policy->allowMethods(Nette\Security\User::class, ['isLoggedIn', 'isAllowed']);
$policy->allowProperties(Nette\Database\Row::class, $policy::All);

I permessi concessi con allowMethods() e allowProperties() valgono anche per le istanze delle sottoclassi della classe indicata (il controllo usa is_a()).

Non è fantastico? Potete controllare tutto a un livello molto basso. Se il template tenta di chiamare una funzione non consentita oppure di accedere a un metodo o a una proprietà non consentiti, viene sollevata l'eccezione Latte\SecurityViolationException.

Policy predefinita sicura

Creare una policy da zero, in cui tutto è vietato, può non essere comodo: potete quindi partire da una base sicura:

$policy = Latte\Sandbox\SecurityPolicy::createSafePolicy();

Questa base sicura significa che sono consentiti tutti i tag standard tranne contentType, debugbreak, dump, extends, import, include, layout, php, sandbox, snippet, snippetArea, templatePrint, varPrint, embed. Sono consentiti tutti i filtri standard tranne datastream, noescape e nocheck. Infine è consentito l'accesso ai metodi e alle proprietà dell'oggetto $iterator.

Attivare la sandbox

Le regole valgono per il template che inseriamo con il tag {sandbox}. È in qualche modo analogo a {include}: attiva la modalità sandbox e, come {include}, non passa automaticamente le variabili circostanti. Potete però passarle esplicitamente, per esempio {sandbox 'untrusted.latte', a: 1, b: 2}:

{sandbox 'untrusted.latte'}

Il layout e le singole pagine possono così usare liberamente tutti i tag e tutte le variabili; le restrizioni si applicheranno solo al template untrusted.latte.

Alcune violazioni, come l'uso di un tag o di un filtro vietato, vengono rilevate in fase di compilazione. Altre, come la chiamata a metodi non consentiti di un oggetto, vengono rilevate in fase di esecuzione. Il template può inoltre contenere qualsiasi altro errore. Perché un'eccezione proveniente dal template in sandbox non interrompa l'intero processo di rendering, potete definire un gestore di eccezioni personalizzato che, per esempio, si limiti a registrarla nel log.

Se volessimo attivare la modalità sandbox direttamente per tutti i template, è semplice:

$latte->setSandboxMode();

Controllo del codice generato

Per evitare che un utente inserisca nella pagina codice PHP sintatticamente corretto ma vietato, che provoca un PHP Compile Error, consigliamo di far controllare i template dal linter di PHP. Potete attivare questa funzionalità con il metodo Engine::enablePhpLinter(). Poiché per il controllo deve chiamare il binario di PHP, passatene il percorso come parametro:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');

Ciò che la sandbox non sorveglia

Oltre ai tag, alle funzioni, ai metodi e alle proprietà che consentite, la sandbox vieta incondizionatamente alcuni costrutti indipendentemente dalla policy: l'operatore new, la variabile $this, le variabili variabili ($$var) e il filtro |noescape.

La sandbox sorveglia in modo affidabile le operazioni esplicite: le chiamate a funzioni, metodi e filtri, così come l'accesso alle proprietà degli oggetti. C'è però una cosa che i suoi controlli non riescono a raggiungere, ed è bene conoscerla.

Quando stampate un oggetto o lo usate in un qualsiasi contesto stringa, PHP chiama automaticamente il suo metodo magico __toString(). La policy non controlla questa conversione implicita, che quindi avviene anche se il metodo __toString() non è tra quelli consentiti. L'autore del template può così attivare __toString() su qualunque oggetto raggiungibile nel template. È diverso dalla forma esplicita {$obj->__toString()}, che la sandbox blocca:

{$obj}              {* chiama __toString() *}
{$obj . '!'}        {* lo stesso (concatenazione) *}
{="price: $obj"}    {* lo stesso (interpolazione di stringa) *}
{$obj|upper}        {* lo stesso (attraverso un filtro) *}

Lo scopo di __toString() è produrre una rappresentazione testuale dell'oggetto, quindi la sua raggiungibilità di solito non fa danni. Il problema sorge solo quando __toString() ha effetti collaterali (come scrivere su un database o interrogarlo) oppure quando restituisce dati sensibili.

Latte potrebbe intercettare i casi più diretti, ma non in modo affidabile in tutti. La conversione di un oggetto in stringa non è una chiamata di metodo, ma un'operazione integrata nel linguaggio che si verifica in molti punti di un'espressione. In alcuni di essi (per esempio nel confronto di un oggetto con una stringa, oppure dentro una funzione o un filtro chiamati) non sarebbe possibile intercettarla in modo affidabile senza bloccare anche usi legittimi. Per questo dovete tenere conto del fatto che __toString() è raggiungibile.

Non esponete alla sandbox oggetti il cui __toString() ha effetti collaterali o rivela dati sensibili. Questo vale non solo per gli oggetti che passate direttamente al template, ma anche per quelli che l'autore ottiene come valore di ritorno di una funzione, di un metodo o di una proprietà consentiti.

versione: 3.x