Valutazione del codice dinamico in Java - Clever o Sloppy?


30

Sto cercando di creare un framework ACL flessibile in Java per la mia applicazione.

Molti framework ACL sono basati su una whitelist di regole, in cui una regola ha la forma di proprietario: azione: risorsa . Per esempio,

  • "JOHN può VISUALIZZARE la risorsa FOOBAR-1"
  • "MARY può VISUALIZZARE la risorsa FOOBAR-1"
  • "MARY può MODIFICARE la risorsa FOOBAR-1"

Questo è interessante perché le regole possono essere facilmente serializzate / persistenti in un database. Ma la mia applicazione ha una logica aziendale complessa. Per esempio,

  • "Tutti gli utenti del dipartimento 1 con oltre 5 anni di anzianità possono VISUALIZZARE la risorsa FOOBAR-1, altrimenti non autorizzato"
  • "Tutti gli utenti del dipartimento 2, se la data è successiva al 15/03/2016, possono VISUALIZZARE la risorsa FOOBAR-2, altrimenti non autorizzata"

A prima vista, sarebbe un incubo escogitare uno schema di database in grado di gestire regole infinitamente complesse come queste. Pertanto, sembra che avrei bisogno di "inserirli" nell'applicazione compilata, valutarli per ciascun utente e quindi produrre il proprietario: azione: regole delle risorse a seguito della valutazione. Voglio evitare di inserire la logica nell'applicazione compilata.

Quindi, stavo pensando di rappresentare una regola sotto forma di predicato : azione: risorsa , dove il predicato è un'espressione booleana che determina se un utente è autorizzato. Il predicato sarebbe una stringa di un'espressione JavaScript che potrebbe essere valutata dal motore Rhino di Java. Per esempio,

  • return user.getDept() == 1 && user.seniority > 5;

In tal modo, i predicati potrebbero essere facilmente mantenuti nel database.

È intelligente ? Questo è sciatto ? È ingannevole ? È troppo ingegnerizzato ? È sicuro (a quanto pare, Java può eseguire il sandbox del motore di Rhino).


8
Qual è il vantaggio di provare a inserire queste regole di business in un database piuttosto che inserire la logica nell'applicazione compilata?
Winston Ewert,

6
@WinstonEWert L'esternalizzazione delle regole elimina la necessità di ricompilare e ridistribuire l'applicazione in caso di modifica, aggiunta o rimozione di una regola.
Twittopher,


2
Domanda interessante! Mi piacerebbe vedere una risposta che non si concentri tanto sulla sicurezza ma piuttosto sugli aspetti di manutenzione, affidabilità e facilità d'uso di tale soluzione.
Oliver,

6
Sembra simile alle regole di posta elettronica di Outlook che è essenzialmente un motore di regole che è configurabile dall'utente.

Risposte:


37

Il piping di dati dinamici in un interprete del linguaggio di implementazione è di solito una cattiva idea, poiché aumenta il potenziale di corruzione dei dati in un potenziale di acquisizione di applicazioni dannose. In altre parole, stai facendo del tuo meglio per creare una vulnerabilità nell'iniezione di codice .

Il tuo problema può essere risolto meglio da un motore di regole o forse da un linguaggio specifico del dominio (DSL) . Guarda quei concetti, non è necessario reinventare la ruota.


16
Ma JavaScript non sarebbe usato come linguaggio di scripting simile a DSL qui? Impostiamo i dati necessari (sola lettura), racchiudiamo lo snippet in una funzione e lo valutiamo in sicurezza. Poiché il codice non può fare altro che restituire un valore booleano, qui non ci sarebbero opportunità dannose.
amon,

6
@Twittopher Trascinare un intero motore JavaScript per valutare alcuni predicati mi sembra ancora 1) overkill completo, 2) rischioso e 3) soggetto a errori. Caso in questione, hai usato ==invece che ===nel tuo esempio. Vuoi davvero fornire completezza turing quando tutte le regole dovrebbero probabilmente sempre terminare? Invece di saltare attraverso i cerchi per assicurarsi che tutte le interazioni tra Java e JavaScript siano kosher, perché non scrivere un semplice parser e un interprete come suggerito da Kilian? Sarà molto più facile adattarsi alle tue esigenze e al sicuro. Usa ANTLR o qualcosa del genere.
Doval,

6
@Doval Scrivere un piccolo DSL non è esattamente scienza missilistica, e potrei creare un linguaggio semplice in 3 ore a 5 giorni. Ma questo mi sembra 1) overkill completo, 2) rischioso e 3) soggetto a errori. Vuoi davvero scrivere un'intera mini-lingua? Che cosa succede se alcune regole aziendali sono più complicate del previsto e necessitano di un linguaggio completo? Soffri di sindrome di Non inventato qui? Invece di reinventare la ruota, perché non usi una lingua esistente? È molto più facile usare un linguaggio che è già stato testato in battaglia.
amon,

14
@amon Quando ciò accade, trovi un vero motore di regole (che JavaScript non è) come ha detto Killian. Equilibrare i rischi di entrambi gli approcci è fuorviante. Ci vuole solo un'omissione per distruggere tutti i tuoi sforzi per assicurare un interprete per un linguaggio completo; è un processo sottrattivo. È molto più difficile rendere pericoloso un piccolo DSL; è un processo additivo. Il tipo di errore che probabilmente commetterai è interpretare l'albero della sintassi in modo errato e può essere testato dall'unità. Probabilmente non darai accidentalmente all'interprete il formato di abilità un disco rigido.
Doval,

4
@amon: anche se lo snippet js potrebbe non avere effetti collaterali, potrebbe scegliere di non restituire un valore booleano:while (true) ;
Bergi,

44

L'ho fatto e ti consiglio di non farlo.

Quello che ho fatto è stato scrivere tutta la logica aziendale in Lua e archiviare lo script Lua in un database. All'avvio della mia applicazione si caricava ed eseguiva lo script. In questo modo ho potuto aggiornare la logica aziendale della mia applicazione senza distribuire un nuovo binario.

Ho sempre scoperto che avevo sempre bisogno di aggiornare il file binario quando apportavo modifiche. Alcune modifiche sono state apportate allo script Lua, ma invariabilmente avrei avuto un elenco di modifiche che dovevano essere apportate, e quindi ho quasi sempre dovuto apportare alcune modifiche al file binario e alcune modifiche allo script Lua. La mia immaginazione di poter evitare di distribuire i binari tutto il tempo semplicemente non ha funzionato.

Quello che ho trovato molto più utile è stato facilitare la distribuzione dei file binari. La mia applicazione verifica automaticamente la disponibilità di aggiornamenti all'avvio, i download e installa eventuali aggiornamenti. I miei utenti sono quindi sempre sugli ultimi binari che ho spinto. Non c'è quasi alcuna differenza tra una modifica nel binario e una modifica negli script. Se lo facessi di nuovo, farei ancora più sforzi per rendere l'aggiornamento senza soluzione di continuità.


3

Non vorrei che il database contenga codice. Ma puoi fare qualcosa di simile facendo in modo che il database contenga nomi di funzioni e quindi usi la reflection per chiamarli. Quando aggiungi una nuova condizione, devi aggiungerla al tuo codice e al tuo database, ma puoi combinare condizioni e parametri che gli vengono passati per creare valutazioni piuttosto complesse.

In altre parole, se si dispone di dipartimenti numerati, sarebbe facile disporre di un controllo UserDepartmentIs e di TodayIsAfter, quindi combinarli per avere un Department = 2 e Today> 15/03/2016. Se vuoi quindi avere un controllo TodayIsBefore in modo da poter terminare l'autorizzazione, devi scrivere la funzione TodayIsBefore.

Non l'ho fatto per le autorizzazioni utente, ma per la convalida dei dati, ma dovrebbe funzionare.


2

XACML è la soluzione che stai davvero cercando. È un tipo di motore delle regole focalizzato solo sul controllo degli accessi. XACML, uno standard definito da OASIS, definisce tre parti:

  • un'architettura
  • un linguaggio politico (che è davvero quello che vuoi)
  • uno schema di richiesta / risposta (come si richiede una decisione di autorizzazione).

L'architettura è la seguente:

  • il Policy Decision Point (PDP) è la parte centrale dell'architettura. È il componente che valuta le richieste di autorizzazione in entrata rispetto a una serie nota di criteri
  • il Policy Enforcement Point (PEP) è il pezzo di codice che protegge la tua applicazione / API / servizio. Il PEP intercetta la richiesta commerciale, crea una richiesta di autorizzazione XACML, la invia al PDP, riceve una risposta e applica la decisione all'interno della risposta.
  • il Policy Information Point (PIP) è il componente che può collegare il PDP a fonti esterne di dati, ad esempio un LDAP, un database o un servizio web. Il PIP è utile quando il PEP invia una richiesta, ad esempio "Alice può visualizzare il documento n. 12?" e il PDP ha una politica che richiede l'età dell'utente. Il PDP chiederà al PIP "dammi l'età di Alice" e sarà quindi in grado di elaborare le politiche.
  • il Policy Administration Point (PAP) è il luogo in cui è possibile gestire l'intera soluzione XACML (definizione degli attributi, scrittura delle politiche e configurazione del PDP).

eXtensible Access Control Markup Language - Architettura XACML

Ecco come appare il tuo primo caso d'uso:

/*
 * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
 * 
 */
 policy departmentOne{
    target clause department == 1
    apply firstApplicable
    /**
     * All users in department 1 with over 5 years of seniority can VIEW resource FOOBAR-1, else not authorized
     */
    rule allowFooBar1{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }

 }

Il tuo secondo caso d'uso sarebbe:

 /*
  * "All users in department 2, if the date is after 03/15/2016, can VIEW resource FOOBAR-2, else not authorized"
  *  
  */
  policy departmentTwo{
    target clause department == 1
    apply firstApplicable
    rule allowFooBar2{
        target clause resourceId=="FOOBAR-1" and seniority>=5 and currentDate>"2016/03/15":date and actionId=="VIEW"
        permit
    }
    rule denyOtherAccess{
        deny
    }
  }

È possibile combinare entrambi i casi d'uso in un singolo criterio utilizzando i riferimenti:

  policyset global{
    apply firstApplicable
    departmentOne
    departmentTwo
  }

E hai finito!

Puoi leggere di più su XACML e ALFA da:


0

Quello che vuoi davvero qui è XACML . Praticamente ti dà esattamente quello che vuoi. Non devi necessariamente implementare l'intera architettura con tutti i ruoli completamente separati ... se hai solo una singola applicazione, probabilmente puoi cavartela con l'integrazione di PDP e PEP nella tua app con balana e il PIP è qualunque il tuo database utenti esistente è.

Ora, ovunque nella tua app devi autorizzare qualcosa, crei una richiesta XACML che ha l'utente, l'azione e il contesto, e il motore XACML prenderà la decisione in base ai file delle politiche XACML che hai scritto. Questi file di criteri possono essere conservati nel database o nel filesystem o ovunque si desideri mantenere la configurazione. Axiomatics ha una bella alternativa alla rappresentazione XML XACML chiamata ALFA che è un po 'più facile da leggere rispetto all'XML grezzo e un plug-in Eclipse per generare XML XACML dalle politiche ALFA.


1
come risponde alla domanda posta?
moscerino del

Sta specificamente cercando di implementare un sistema di autorizzazione configurato esternamente. XACML è un sistema di autorizzazione configurato esternamente pronto per l'uso che copre molto bene il suo caso d'uso specifico. Devo ammettere che potrebbe non essere un'ottima risposta alla domanda più generale sull'esecuzione dinamica del codice, ma è una buona soluzione alla sua domanda specifica.
Gregorymons,

0

Lo abbiamo fatto presso la mia attuale azienda e siamo molto contenti dei risultati.

Le nostre espressioni sono scritte in js e le usiamo anche per limitare i risultati che gli utenti possono ottenere interrogando ElasticSearch.

Il trucco è assicurarsi che siano disponibili informazioni sufficienti per prendere la decisione, in modo che tu possa davvero scrivere tutto ciò che vuoi senza modifiche al codice, ma allo stesso tempo mantenerlo veloce.

Non siamo veramente preoccupati per gli attacchi di iniezione di codice, poiché le autorizzazioni sono scritte da coloro che non hanno bisogno di attaccare il sistema. E lo stesso vale per gli attacchi DOS come while(true)nell'esempio. Gli amministratori del sistema non hanno bisogno di farlo, potrebbero semplicemente rimuovere le autorizzazioni di tutti ...

Aggiornare:

Qualcosa come XACML sembra essere migliore come punto centrale di gestione delle autorizzazioni per un'organizzazione. Il nostro caso d'uso è leggermente diverso in quanto i nostri clienti in genere non hanno un reparto IT per eseguire tutto ciò. Avevamo bisogno di qualcosa di autonomo, ma abbiamo cercato di preservare la massima flessibilità possibile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.