2016-03-18 16 views
1

So habe ich ein wenig Code, der eine Instanz einer Klasse erstellt.Java set Sicherheitsberechtigung der erstellten Instanzen

Class<?> c = Class.forName("MyClass"); 
Constructor<?> cons = c.getConstructor(); 
cons.setAccessible(true); 
Object instance = cons.newInstance(); 

Jetzt möchte ich einige Einschränkungen für diese Instanz festlegen. Wenn ich anrufe:

instance.doSomething(); 

Ich möchte Einschränkungen für dieses Bit des Codes (der Instanz) festlegen. Daher können die Methoden, die von dieser Inntance aufgerufen werden, nichts Faules machen (Systemaufrufe, Dateioperationen ...).

Ich habe versucht, einen Sicherheitsmanager zu setzen, aber das beschränkt den gesamten Code (ich möchte immer noch Dateien für den Rest meines Codes lesen/schreiben). Kann man nur bestimmte Objekte einschränken?

Antwort

1

TL; DR: Code


Die Frage ist im Wesentlichen „Wie kann ich eine Methode auf eine bestimmte Instanz aufrufen, mit Privilegien niedriger als normal?“. Hierfür gibt es drei Voraussetzungen:

  1. Der Code ist pro Instanz zuzulassen. Eine Instanz ist standardmäßig privilegiert.
  2. Eine Instanz kann selektiv auf eine schwarze Liste gesetzt werden, d. H. Sie kann niedrigere Privilegien erhalten als normalerweise für die Dauer eines Methodenaufrufs, den sie empfängt.
  3. Blacklisting muss sich in Code umsetzen, der im Namen des Empfängers ausgeführt wird, insbesondere alle Objekte des gleichen Typs, mit denen es interagiert, selbst eingeschlossen; andernfalls, wenn, sagen wir, waren der Empfänger wiederum

    AccessController.doPrivileged((PrivilegedAction<Void>)() -> { 
        this.doSomethingElse(); 
        return null; 
    }); 
    

    doSomethingElse() die Sandbox entkommen würde zu nennen.

Alle drei sind problematisch:

  • Die erste erreichbar ist nicht wirklich, denn es setzt voraus, dass die Laufzeit maintain- und -Informationen über die Instanzen aussetzen (und nicht nur die Klassen) auf Threads 'Ausführung Stacks, die es nicht .
  • Die zweite und dritte sind nur erreichbar, solange jeder Blacklisted Code nicht seine eigenen (standardmäßigen, klassenbasierten) Privilegien über AccessController.doPrivileged(...) geltend machen, die, von Entwurf, kann es jederzeit zu wählen.

Gibt es eine Alternative?
Nun, wie weit sind Sie bereit zu gehen? Ändern AccessController/AccessControlContext Interna? Oder noch schlimmer, Interna der VM? Oder stellen Sie vielleicht Ihre eigene SecurityManager zur Verfügung, die die Funktionalität der oben genannten Komponenten von Grund auf neu implementiert, in einer Weise, die Ihren Anforderungen entspricht? Wenn die Antwort für alle "Nein" lautet, befürchte ich, dass Ihre Möglichkeiten begrenzt sind.

Als Nebenwirkung, sollten Sie idealerweise die Lage sein, eine binäre Wahl zu treffen, wenn gefragt: „Kann oder kann nicht dieser bestimmte Code, das heißt Klasse, mit den besonderen Privilegien übertragen werden?“, würde dies enorm vereinfachen Dinge. Leider kannst du nicht; und, um die Dinge noch schlimmer zu machen, können Sie vermutlich auch die Implementierung der Klasse nicht so ändern, dass alle Instanzen entweder - in Bezug auf eine bestimmte Gruppe von Privilegien - vertrauenswürdig sind oder nicht, und Sie auch nicht einfach markieren möchten die Klasse, und damit alle ihre Instanzen, als nicht vertrauenswürdig (was ich glaube, du solltest!) und damit leben.

Weiter zu der vorgeschlagenen Problemumgehung. Um die oben aufgeführten Mängel zu überwinden, wird die ursprüngliche Frage wie folgt umformuliert: "Wie rufe ich eine Methode mit erhöhten Privilegien die Methode Empfänger ProtectionDomain?" Ich werde stattdessen diese Derivat Frage beantworten, was darauf hindeutet, im Gegensatz zum Original, dass:

  1. -Code durch die ProtectionDomain seine Klasse zugelassen werden soll, wie es normalerweise der Fall ist. Der Code ist standardmäßig sandboxed.
  2. Code kann selektiv für die Dauer eines Methodenaufrufs unter einem bestimmten Thread auf die weiße Liste gesetzt werden.
  3. Whitelisting muss zu Code der gleichen Klasse propagieren, die vom Empfänger aufgerufen wird.

Die überarbeiteten Anforderungen werden mittels einer benutzerdefinierten ClassLoader und DomainCombiner erfüllt. Der Zweck der ersten ist es, eine eindeutige ProtectionDomain pro Klasse ; der andere ist es, die Domänen einzelner Klassen innerhalb der aktuellen für "On-Demand-Whitelisting" Zwecke vorübergehend zu ersetzen. Die SecurityManager wird zusätzlich erweitert, um die Erstellung von Threads durch nicht privilegierten Code zu verhindern.


Hinweis: Ich habe den Code auf this gist verschoben, um die Länge des Beitrags unter dem Grenzwert zu halten.
Standard Disclaimer: Proof-of-Concept Code-Take mit mehreren Esslöffeln Salz!


Ausführen des Beispiel
Compile und den Code bereitstellen, wie durch die beispielhaften Richtlinienkonfigurationsdatei vorgeschlagen, das heißt, es sollte zwei sein unabhängige Classpath Einträge (zB Geschwister Verzeichnisse an Dateisystemebene) -one für Klassen des com.example.trusted-Pakets und ein weiteres für com.example.untrusted.Nasty.

Stellen Sie außerdem sicher, dass Sie die Richtlinienkonfiguration durch die Beispielkonfiguration ersetzt haben und die zugehörigen Pfade entsprechend geändert haben.

Schließlich laufen (nach geeigneter Weise mit modifiziert, um die Classpath-Einträge, natürlich):

java -cp /path/to/classpath-entry-for-trusted-code:/path/to/classpath-entry-for-untrusted-code -Djava.system.class.loader=com.example.trusted.DiscriminatingClasspathClassLoader com.example.trusted.Main 

Der erste Aufruf der nicht vertrauenswürdige Methode sollte hoffentlich gelingen, und die zweite nicht.


Es wäre vielleicht für Instanzen einer speziell gestalteten Klasse möglich sein (mit beispielsweise eine Domäne ihrer eigenen, durch eine vertrauenswürdige Komponente zugewiesen) ihre eigenen Privilegien auszuüben sich (welche nicht zutrifft In diesem Fall, da Sie keine Kontrolle über die Implementierung der Klasse instance haben, erscheint es). Dies würde jedoch immer noch nicht die zweite und dritte Anforderung erfüllen.
Daran erinnern, dass, unter dem Standard SecurityManager, ein Permission gewährt wird, wenn alle ProtectionDomain s zu denen normalerweise Klassen, anstatt Fälle abgebildet-of sind der diese Berechtigung AccessControlContextimply Thread. Sie müssten dann einfach Berechtigungen auf Richtlinienebene erteilen, wenn Sie die Klasse als vertrauenswürdig erachten, oder sonst gar nichts gewähren, anstatt sich um Berechtigungen pro Instanz pro Sicherheitskontext und so weiter kümmern zu müssen.
Das ist eine harte Entscheidung: Wenn nicht eine Whitelist nachfolgende Aufgerufenen des gleichen Typs beeinflussen würde, würde die Instanz nicht in der Lage sein, alle Privilegien erfordernden Methoden aufrufen auf sich. Nun, da es andererseits eine andere Instanz des gleichen Typs gibt, mit der der ursprüngliche Empfänger auf der weißen Liste interagiert, werden Sie auch privilegiert! Sie müssen also sicherstellen, dass der Empfänger keine "nicht vertrauenswürdigen" Instanzen seiner Art aufruft. Es ist aus dem gleichen Grund eine schlechte Idee, dem Empfänger zu erlauben, irgendwelche Threads zu spawnen.
Als ClassLoader, von der Standardanwendung verwendete die Strategie, die im Gegensatz zu allen Klassen Gruppe ist, die innerhalb einer einzelnen ProtectionDomain unter demselben classpath Eintrag befinden.
Der Grund für die Unannehmlichkeiten ist, dass die ProtectionDomain, die unsere eigene Anwendung ClassLoader ‚s-Klasse von ihrer Mutter abgebildet wird, ein CodeSource hat impliziert all CodeSource s auf Dateien unter dem Classpath Eintrag des Laders bezieht. So weit, ist es gut. Wenn wir nun aufgefordert werden, eine Klasse zu laden, versucht unser Loader, zwischen System-/Erweiterungsklassen (deren Laden an den übergeordneten Klassen delegiert wird) und Anwendungsklassen zu unterscheiden, indem getestet wird, ob sich die .class-Datei unter JAVA_HOME befindet. Um dies zu ermöglichen, benötigt es natürlich einen Lesezugriff auf den Unterbaum des Dateisystems unter JAVA_HOME. Leider gewährt die Gewährung der entsprechenden Berechtigung an die (notorisch breite) Domäne des Ladeprogramms implizit die Berechtigung für die Domänen aller anderen Klassen, die sich unterhalb des Klassenpfadeintrags des Ladeprogramms befinden, einschließlich nicht vertrauenswürdiger. Und das sollte hoffentlich erklären, warum die Klassenpfad-Entry-Level-Isolation zwischen vertrauenswürdigem und nicht vertrauenswürdigem Code notwendig ist. Wie immer gibt es natürlich Workarounds; z.B. die Verpflichtung, dass vertrauenswürdiger Code zusätzlich signiert wird, um irgendwelche Privilegien zu erhalten; oder vielleicht unter Verwendung eines flexibleren URL-Schemas zur Codequellenidentifikation und/oder zur Änderung der Semantik der Codequellenimplikation.
Weiterführende Literatur:


Historische Anmerkung: Ursprünglich war diese Antwort eine nahezu identische Lösung vorgeschlagen th bei missbraucht verließ JAAS SubjectDomainCombiner, anstatt eine benutzerdefinierte, für die dynamische Änderung der Privilegien. Ein "spezielles" Principal würde an spezifische Domänen angehängt werden, die dann bei der Auswertung durch die Policy, basierend auf ihrer zusammengesetzten Permission s, zusätzliche Identität erhalten würden.