L’autorizzazione è probabilmente il fattore più importante all’interno di un’architettura di rete Zero Trust Security (ZTS) e, in quanto tale, autorizzare un utente o un servizio non dovrebbe essere fatto con leggerezza (come spesso invece accade nei modelli di sicurezza tradizionali ancora operativi in tante, tantissime aziende, anche italiane).
Ogni flusso e/o richiesta richiede una decisione ponderata. I database e i sistemi di supporto che illustrerò sono i sistemi chiave che occorrono per prendere e influenzare tali decisioni: sono fondamentali per il controllo degli accessi, quindi devono essere rigorosamente isolati.
Occorre fare un’attenta distinzione tra queste responsabilità, in particolare quando si decide se comprimerle in un unico sistema, cosa che generalmente dovrebbe essere evitata (se possibile). Concentriamoci, quindi, sull’architettura di alto livello illustrando i componenti necessari per prendere decisioni in una rete ZTS, nonché su come si adattano e applicano tali decisioni.
Zero Trust Security: cos’è, quali sono i principi cardine e i fondamenti applicativi
Indice degli argomenti
Componenti dell’architettura decisionale di una rete ZTS
Un rete ZTS comprende quattro componenti principali, come mostrato nella figura seguente:
- Enforcement;
- Policy engine;
- Trust engine;
- Data stores.
Questi quattro componenti hanno responsabilità differenti e, di conseguenza, dovrebbero essere trattati come sistemi separati, ergo, isolati l’uno dall’altro, prestando particolare attenzione alla loro posizione, locazione e sicurezza informatica.
Il componente Enforcement è presente in gran numero in tutto il sistema, ed è il più vicino possibile al carico di lavoro.
È quello che incide maggiormente sull’esito delle decisioni di autorizzazione, e in genere si manifesta come un sistema di bilanciamento del carico (load balancer), una batteria di proxy e/o di firewall.
Questo componente interagisce con il Policy engine, che verrà utilizzato per prendere la decisione effettiva.
Il componente Enforcement garantisce che i client vengano autenticati, e passa il contesto di ogni flusso/richiesta al Policy engine. A sua volta, il Policy engine confronta la richiesta e il relativo contesto con i criteri, informando il responsabile dell’applicazione se la richiesta è stata o meno consentita.
Il Trust engine è utilizzato dal Policy engine per scopi di analisi dei rischi. Sfrutta più fonti dati per calcolare un punteggio di rischio (“risk score”), similmente a un punteggio di credito. Questo punteggio può essere utilizzato per combattere minacce informatiche sconosciute, aiutando a mantenere le policy efficaci, senza complicarle con firme e casi limite.
Viene utilizzato dal Policy engine come componente aggiuntivo mediante il quale è possibile prendere una decisione di autorizzazione. Infine sono presenti i vari archivi di dati (Data store) che rappresentano “fonte di verità” per i dati utilizzati per definire l’autorizzazione.
Utilizzando bit di dati autenticati come chiavi di ricerca primarie (ad esempio, un nome utente o il numero seriale di un dispositivo), questi dati vengono utilizzati per realizzare un quadro contestuale completo di un particolare flusso di richiesta.
I data store, siano essi dati dell’utente, del dispositivo o di altro, sono ampiamente sfruttati sia dal Policy engine che dal Trust engine, e rappresentano il supporto su cui verranno misurate tutte le decisioni.
Il componente Enforcement
Il componente Enforcement è il punto di partenza: sua è la “prima linea” del flusso autorizzativo, e continuerà ad essere responsabile dell’espletamento delle decisioni prese dal resto dell’architettura decisionale.
L’Enforcement si avvale di due fasi. In prima battuta verifica l’interazione con il Policy engine, ed è in questa fase che viene presentata la richiesta di autorizzazione (ad esempio, un bilanciatore che riceve una richiesta e che dovrà sapere se è stata o meno autorizzata).
Nella seconda fase avverrà l’installazione effettiva e l’applicazione della policy. Sebbene queste due fasi rappresentino un singolo componente nel mondo delle architetture ZTS, è possibile scegliere se evaderle insieme o separatamente.
Il modo in cui sceglierai di gestire l’Enforcement dipenderà probabilmente dal tuo use case. Ad esempio, un proxy che verifica l’Identità potrà contattare il Policy engine per autorizzare attivamente una richiesta che ha ricevuto e, allo stesso tempo, utilizzare la risposta a entrambi i servizi piuttosto che rifiutare la richiesta. In alternativa, un servizio di pre-autorizzazione potrebbe ricevere una richiesta di accesso a un particolare servizio, che quindi contatterà il Policy engine per l’autorizzazione.
A seguito di autorizzazione affermativa, il servizio potrà manipolare le regole del firewall locale per consentire la richiesta specifica.
Con questo approccio ci affidiamo a meccanismi di enforcement “standard” che sono pilotati dal piano di controllo ZTS. Va notato, tuttavia, che lato client questo approccio richiede di notificare al piano di controllo la richiesta di autorizzazione.
Questo aspetto può essere o meno accettabile a seconda del piano di controllo sui dispositivi e applicazioni.
Il corretto posizionamento del componente Enforcement è fondamentale poiché rappresenterà il tuo avamposto di controllo all’interno del piano dati, e pertanto devi assicurarti che i componenti delle applicazioni siano posizionati il più vicino possibile agli endpoint.
In caso contrario, il livello di “trust” potrebbe raggrupparsi dietro il componente Enforcement, minando la sicurezza ZTS. Fortunatamente il componente Enforcement può essere modellato come una sorta di client, nonché liberamente applicato in tutto il sistema, ma questo è apertamente in contrasto con il resto dei componenti di autorizzazione, che sono modellati come servizi.
Il componente Policy engine
È il componente a cui è stato assegnato il potere di prendere decisioni: lo fa confrontando la richiesta in arrivo (dal componente Enforcement) con la policy, al fine di determinare se la richiesta è o meno autorizzata. Una volta terminato il confronto, il risultato è restituito al componente Enforcement per l’effettiva realizzazione della policy.
La disposizione dei layer di Enforcement e del Policy engine consente di prendere decisioni dinamiche e puntuali, concedendo un’eventuale rapida revoca. Pertanto è importante che questi componenti siano gestiti separatamente e indipendentemente. Questo non vuol dire, tuttavia, che non possano essere collocati in maniera congiunta.
A seconda di una serie di fattori è possibile trovare un Policy engine ospitato accanto al componente di Enforcement: un esempio potrebbe essere un load balancer che autorizza le richieste tramite comunicazioni inter-processo (IPC) piuttosto che chiamate remote.
Il vantaggio più interessante di questo modello architetturale consiste nella minor latenza che occorre per autorizzare la richiesta. Un sistema di autorizzazione a bassa latenza consente un’autorizzazione dettagliata e completa dell’attività di rete; ad esempio, le singole richieste HTTP potrebbero essere autorizzate al posto dell’autorizzazione a livello di sessione che normalmente viene applicata.
È più opportuno mantenere l’isolamento a livello di processo tra il Policy engine ed il layer di Enforcement. Il componente Enforcement trovandosi nel percorso dati dell’utente, è più esposto: pertanto, integrando il Policy engine nello stesso processo potrebbe esporlo a rischi indesiderati.
L’implementazione del Policy engine come processo a sé stante contribuisce notevolmente a garantire che i bug nel livello di applicazione non comportino una compromissione delle policy.
Occorre anche memorizzare le regole referenziate dal Policy engine.
Le regole espresse dalle policy devono essere configurate nel Policy engine, ma è vivamente consigliato acquisire le regole dall’esterno del Policy engine stesso. Salvare le regole delle policy in un sistema di controllo delle versioni (VCS) è l’ideale, e apporta diversi benefici:
- le modifiche alle policy potranno essere monitorate nel tempo;
- le motivazioni che hanno portato alla modifica della policy in esame vengono tracciate puntualmente nel sistema VCS;
- lo stato previsto dalla policy corrente potrà essere convalidato rispetto all’applicazione effettiva dei pattern di Enforcement.
Molti di questi vantaggi, storicamente sono stati implementati utilizzando rigorose procedure di change management. Con un tale meccanismo, le modifiche alla configurazione del sistema verranno richieste e approvate prima di essere applicate.
Il log del sistema di change management risultante potrà essere utilizzato per determinare perché il sistema si trova nello stato attuale.
Spostare le definizioni delle policy all’interno di un VCS è la logica conclusione delle procedure di change management quando il sistema può essere configurato con poche righe di scripting. Invece di fare affidamento sui sysadmin (al di là delle eventuali certificazioni conseguite, quanti sono realmente affidabili, tempestivi e precisi?) per configurare la policy desiderata nel sistema, possiamo acquisire la policy come “dati” che un programma potrà caricare e applicare agilmente.
In un certo qual senso, quindi, caricare le policy sarà quindi simile alla distribuzione di software.
Di conseguenza i sysadmin potranno utilizzare le consuete procedure di distribuzione software (pipeline e affini) per gestire variazioni di policy.
Le policy in una rete ZTS sono granulari, meticolose. Il dettaglio di granularità varia in base alla complessità della rete, ma l’obiettivo desiderato è che le policy che hanno come ambito singole risorse da proteggere, vengano doverosamente applicate e tracciate.
Il modello ZTS inizia a divergere dalla tradizionale sicurezza perimetrale già nei meccanismi di controllo utilizzati per definire le policy: invece di definire la policy in termini di dettagli d’implementazione (indirizzi IP e intervalli), la policy è definita assai più approfonditamente in termini di componenti logici nella rete.
Questi componenti sono costituiti da:
- servizi di rete;
- classi di device endpoint;
- ruoli degli utenti.
La definizione delle policy dai componenti logici esistenti nella rete ZTS consente al Policy engine di calcolare le decisioni di Enforcement in base alla sua conoscenza dell’effettivo stato della rete. In termini concreti, un web service in esecuzione su un server oggi, domani potrebbe trovarsi su un server differente o persino spostarsi automaticamente come indicato da un workload scheduler.
Per adattarsi a questa realtà, le policy che definirai dovranno essere separate da questi “dettagli di implementazione”.
Un esempio di questo stile di policy tratto da un progetto Kubernetes:
(In questo esempio di Network Policy Kubernetes i criteri utilizzano delle etichette per gestire il workload, applicando le sottostanti regole basate su IP quando e dove necessario).
Le policy in una rete ZTS si basano anche sui punteggi di affidabilità (“trust scores”) per rilevare (e possibilmente anticipare) eventuali vettori di attacco sconosciuti. Definendo la policy con un punteggio di attendibilità, i sysadmin saranno in grado di mitigare i rischi che viceversa potrebbero non essere rilevati con una tradizionale policy specifica.
Le policy ovviamente non faranno affidamento solo sul punteggio di affidabilità: nella definizione delle policy potranno far parte anche caratteristiche peculiari della richiesta che deve essere autorizzata. Un esempio di questo scenario potrebbe essere costituito da determinati ruoli utente a cui si decide di fornire accesso solo e soltanto ad un determinato servizio in orari e giorni predefiniti da una determinata postazione censita.
Chi definisce le policy? Le policy di una rete ZTS devono essere granulari, il che significa dover imporre un onere straordinario ai sysadmin per mantenere aggiornate le policy. Per distribuire il carico di queste configurazioni, la maggior parte delle aziende decide di distribuire la definizione delle policy tra i team, in modo che possano contribuire proattivamente a mantenere le policy per i servizi in loro gestione.
Tuttavia, consentire a un’intera organizzazione la (ri)definizione delle policy comporta alcuni potenziali rischi, come ad esempio utenti in buona fede che creano policy lasche, aumentando così la superficie di attacco del sistema che intendevano limitare.
Le reti e i sistemi ZTS si basano su due flussi di lavoro organizzativi per contrastare questa esposizione.
In primo luogo, poiché le policy vengono in genere archiviate mediante sistemi VCS, collaborare con un’altra persona che rianalizzi le modifiche alle policy migliora la possibilità che le modifiche stesse siano ben strutturate.
I team SOC potranno così analizzare le richieste di modifica e porre eventuali domande investigative per garantire che la policy in esame sia la più stretta possibile.
La seconda misura organizzativa utilizzabile consiste nel sovrapporre una policy dell’infrastruttura ampia ad una policy certosina. Ad esempio, un gruppo di infrastrutture potrebbe giustamente richiedere che solo un determinato insieme di ruoli sia autorizzato ad accettare il traffico da Internet piuttosto che da una determinata subnet.
Il team dell’infrastruttura definirà quindi la policy che impone tale restrizione, e nessuna policy definita dall’utente verrà autorizzata ad aggirarla. L’applicazione di questo vincolo potrebbe assumere diverse forme: un test automatizzato della policy proposta, piuttosto che un Policy engine che rifiuti categoricamente istanze di policy eccessivamente lasche provenienti da fonti non attendibili. Tale applicazione può essere utile anche per la conformità e i requisiti normativi.
Il componente Trust engine
In una rete ZTS il componente Trust engine esegue l’analisi dei rischi di una particolare richiesta o azione. La responsabilità di questo componente è quella di produrre una valutazione numerica della “rischiosità” di eseguire una particolare richiesta o azione, rischiosità che a sua volta il Policy engine utilizzerà per prendere una decisione definitiva sull’autorizzazione.
Il Trust engine estrae spesso dati contenuti negli inventari e/o nei sistemi di asset management al fine di verificare gli attributi di un’entità durante il calcolo del suo punteggio.
Un inventario dei dispositivi, ad esempio, può fornire al Trust engine informazioni chiave come l’ultima volta che un dispositivo è stato verificato, oppure se quel certo dispositivo dispone di una particolare funzionalità di sicurezza hardware.
Creare una valutazione numerica del rischio non è un compito banale. Un approccio semplicistico è quello di definire un insieme di regole ad hoc che valutino la rischiosità di un’entità, di un asset o di un utente. Ad esempio, un dispositivo a cui mancano le ultime patch software potrebbe subire una riduzione del punteggio.
Allo stesso modo, un utente che spesso non riesce ad autenticarsi potrebbe subire un’automatica riduzione del punteggio di affidabilità. Sebbene il punteggio di attendibilità ad hoc è semplice da implementare, un insieme di valori e regole definite staticamente saranno insufficienti per raggiungere l’obiettivo desiderato, ossia quello di difendersi da attacchi imprevisti.
Di conseguenza, oltre a utilizzare regole statiche, in ambienti professionali per derivare le funzione di punteggio di norma vengono utilizzati Trust engine abbinati a tecniche di Machine Learning (ML).
Sfruttando il ML è possibile ottenere una funzione di punteggio (di rischiosità) calcolando fatti osservabili da un sottoinsieme di dati di attività noti come dati di addestramento. I dati di addestramento sono osservazioni non elaborate che sono state associate a entità attendibili o non attendibili.
Da questi dati, le caratteristiche vengono estratte e utilizzate per ricavare una funzione di punteggio generata computazionalmente.
Questa funzione di punteggio, un modello in termini di ML, viene quindi applicata su di un insieme di dati nello stesso formato dei dati di addestramento.
I punteggi risultanti vengono confrontati con le valutazioni del rischio definite dall’analista, e la qualità del modello può quindi essere perfezionata in base alla sua capacità di prevedere il rischio dei dati analizzati.
Sebbene il ML sia sempre più utilizzato, ciò non elimina la necessità di definire regole più esplicite nel Trust engine. A causa della limitazione dei modelli di punteggio derivati – o per le personalizzazioni desiderate della funzione di punteggio – i Trust engine utilizzano in genere una combinazione di metodi di punteggio ad hoc e di ML.
Decidere quali componenti di una rete ZTS debbano essere valutati è una questione che richiede più di qualche riunione: i punteggi devono essere calcolati per ogni singola entità (utenti, dispositivi e applicazioni), per l’agent di rete nel suo insieme o per entrambi? Diamo un’occhiata ad alcuni scenari.
Immagina che le credenziali di un utente possano essere forzate (ad esempio con un attacco a forza bruta) da un attacker. Alcuni sistemi mitigheranno questa minaccia bloccando l’account dell’utente.
Se dovessimo assegnare un punteggio negativo a un utente in base a quest’attività, una rete ZTS soffrirebbe dello stesso problema.
Un approccio migliore consiste nel rendersi conto che stiamo autenticando l’agent di rete, e quindi l’agent dell’attacker verrà neutralizzato, lasciando intatto l’agent di rete dell’utente legittimo.
Questo esempio dimostra come sia l’agent di rete la giusta entità a cui dovrebbe essere assegnato il punteggio in questo scenario.
Ma il solo punteggio dell’agent può non essere sufficiente contro altri vettori di attacco. Consideriamo un dispositivo che è stato associato ad attività dannose. L’agent di un utente su quel dispositivo potrebbe non mostrare segni di comportamenti sospetti, ma il fatto che l’agent venga formato con un dispositivo sospetto dovrebbe chiaramente avere un impatto sul punteggio di affidabilità per tutte le richieste provenienti da quel dispositivo.
Questo scenario ci suggerisce fortemente che il dispositivo dovrebbe essere schedato.
Infine, bisogna considerare che un insider (la famigerata minaccia interna) potrebbe utilizzare più dispositivi per esfiltrare segreti commerciali o brevetti industriali. Mentre l’insider passa da un dispositivo all’altro (come negli scenari dei lateral movement), ci piacerebbe che l’algoritmo di affidabilità (utilizzato dal Trust engine) riconoscesse questo comportamento, e riflettesse un livello di affidabilità decrescente nel punteggio di affidabilità per tutte le future decisioni di autorizzazione.
Anche in questo caso, il punteggio dell’agent da solo non è sufficiente per mitigare le minacce comuni, infatti, nel suo insieme sembra che la soluzione più attendibile sia quella di assegnare un punteggio sia all’agent sia alle entità sottostanti che lo compongono.
Questi punteggi verranno trasmessi al Policy engine, che potrà così scegliere i componenti più adeguati da autorizzare in base alle policy definite.
Tuttavia, quando si progetta una policy, presentare così tanti punteggi che occorre prendere in considerazione potrebbe rendere il compito più difficile nonché soggetto a errori. In un mondo ideale, un singolo punteggio sarebbe sufficiente, ma tale approccio presenta ulteriori requisiti di disponibilità del Trust engine.
Un sistema che si avvale di valutazioni a punteggio singolo dovrebbe passare a un modello on-line, in cui il Trust engine verrà interrogato in modo interattivo durante il processo decisionale delle policy. Al motore verrà fornito un contesto sulla base della richiesta autorizzata, in modo che potrà scegliere la miglior funzione di punteggio per quella particolare richiesta.
Questo approccio è chiaramente più complesso da costruire e gestire, inoltre, per quelle policy in cui i sysadmin desiderano specificamente gestire un particolare componente (ad esempio, consentire solo implementazioni da dispositivi con un punteggio superiore a X), questo metodo risulta piuttosto indiretto.
Sebbene i punteggi assegnati alle entità in una rete ZTS non siano considerati riservati, si dovrebbe evitare di esporre i punteggi agli utenti finali del sistema.
Poter visionare il proprio punteggio potrebbe avvantaggiare aspiranti attacker che stanno pianificando un attacco informatico o che hanno già ottenuto un accesso ai sistemi.
Questo desiderio di nascondere i punteggi dovrebbe essere bilanciato con la frustrazione della capacità, che hanno gli utenti finali, di capire come le loro azioni stanno influenzando la loro fiducia nel sistema.
Ad esempio, un buon compromesso nei sistemi antifrode consiste nel mostrare raramente agli utenti i loro punteggi, e parallelamente nell’evidenziare i fattori che contribuiscono alla determinazione del loro punteggio.
Il componente Data store
Gli archivi di dati (“Data stores”) utilizzati per prendere decisioni di autorizzazione costituiscono “fonti di verità” per lo stato attuale e passato del sistema.
Le informazioni provenienti da questi archivi di dati fluiscono attraverso i sistemi del piano di controllo, fornendo gran parte della base su cui vengono prese le decisioni di autorizzazione, come mostrato nella seguente figura:
Nell’apertura di questo articolo accennavo a come il Trust engine sfrutti i Data store per produrre un punteggio di attendibilità, che a sua volta alimenta il Policy engine. In questo modo, le informazioni nei Data store del Piano di controllo contattano il sistema autorizzativo, raggiungendo infine il Policy engine in cui verrà presa la decisione.
I data store sono utilizzati dal Policy engine sia direttamente che indirettamente, ma possono essere utili anche ad altri sistemi che necessitano di dati autorevoli sullo stato della rete.
Le reti ZTS tendono ad avere molti data store organizzati per funzione. Ne esistono due tipi principali: inventario e storico. Un (data store) inventario costituisce un’unica fonte coerente di verità, che registra lo stato attuale delle risorse che rappresenta.
Un esempio è un inventario degli utenti che memorizza tutte le informazioni sugli utenti piuttosto che un inventario dei dispositivi che raccoglie le informazioni sui dispositivi noti all’azienda. In un inventario esisterà una chiave primaria che rappresenta in modo univoco l’entità tracciata.
Nel caso di un utente, la scelta più probabile è il nome utente; per un dispositivo è più appropriato un numero di serie. Quando un agent ZTS viene sottoposto ad autenticazione, autentica la propria identità rispetto a questa chiave primaria nell’inventario.
Altro esempio, un utente si autentica con un dato nome utente: il Policy engine verrà a conoscenza del nome utente, e che è stato correttamente autenticato. Il nome utente viene quindi utilizzato come chiave primaria per la ricerca nell’inventario degli utenti. A seconda delle tue particolari scelte di implementazione e autenticazione, tenere a mente questo flusso e questo scopo ti aiuterà a scegliere le chiavi primarie giuste.
Un (data store) archivio è leggermente diverso. Gli archivi di dati storici vengono conservati principalmente per scopi di analisi dei rischi. Sono utili per esaminare comportamenti e modelli recenti/passati al fine di valutare il rischio in relazione a una particolare richiesta o azione.
Il Trust engine attinge a questi dati proprio perché le determinazioni di affidabilità/rischio sono la responsabilità primaria di questo componente. Si possono creare molti archivi di dati storici e, quando si tratta di Analisi dei rischi, tecnicamente parlando il limite è l’infinito. Alcuni esempi comuni includono il logging degli account utente e i dati sFlow. Indipendentemente dai dati archiviati, dovranno essere interrogabili utilizzando la chiave primaria da uno dei data store inventario. Più avanti approfondirò meglio il concetto.