2010-03-20 15 views
90

Più ho imparato a conoscere la potenza di java.lang.reflect.AccessibleObject.setAccessible, più mi stupisco di quello che può fare. Questo è adattato dalla mia risposta alla domanda (Using reflection to change static final File.separatorChar for unit testing).Come limitare setAccessible ai soli usi "legittimi"?

import java.lang.reflect.*; 

public class EverythingIsTrue { 
    static void setFinalStatic(Field field, Object newValue) throws Exception { 
     field.setAccessible(true); 

     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 

     field.set(null, newValue); 
    } 
    public static void main(String args[]) throws Exception {  
     setFinalStatic(Boolean.class.getField("FALSE"), true); 

     System.out.format("Everything is %s", false); // "Everything is true" 
    } 
} 

È possibile fare cose veramente scandaloso:

public class UltimateAnswerToEverything { 
    static Integer[] ultimateAnswer() { 
     Integer[] ret = new Integer[256]; 
     java.util.Arrays.fill(ret, 42); 
     return ret; 
    } 
    public static void main(String args[]) throws Exception { 
     EverythingIsTrue.setFinalStatic(
     Class.forName("java.lang.Integer$IntegerCache") 
      .getDeclaredField("cache"), 
     ultimateAnswer() 
    ); 
     System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42" 
    } 
} 

Presumibilmente i progettisti API rendono conto di quanto abusable setAccessible può essere, ma deve avere ammesso che esso ha usi legittimi di fornirle. Quindi le mie domande sono:

  • Quali sono gli usi veramente legittimi per setAccessible?
    • Java potrebbe essere stato progettato in modo da NON avere questo bisogno in primo luogo?
    • Quali sarebbero le conseguenze negative (se presenti) di tale progettazione?
  • È possibile limitare lo setAccessible solo a usi legittimi?
    • È solo tramite SecurityManager?
      • Come funziona? Lista bianca/lista nera, granularità, ecc.?
      • È comune doverlo configurare nelle applicazioni?
    • Posso scrivere le mie classi di essere setAccessible -proof indipendentemente dalla configurazione SecurityManager?
      • O sono alla mercé di chi gestisce la configurazione?

Credo che ancora una domanda importante è: DEVO preoccupare di questo ???

Nessuna delle mie classi ha una parvenza di privacy applicabile che mai. Il modello singleton (mettendo da parte i dubbi sui suoi meriti) è ora impossibile da far rispettare. Come mostrano i miei frammenti sopra, anche alcune ipotesi di base su come funziona il fondamentale di Java non è nemmeno vicino ad essere garantito.

QUESTI PROBLEMI NON SONO REALI ???


Okay, ho appena confermato: grazie alla setAccessible, stringhe Java sono NON immutabile.

import java.lang.reflect.*; 

public class MutableStrings { 
    static void mutate(String s) throws Exception { 
     Field value = String.class.getDeclaredField("value"); 
     value.setAccessible(true); 
     value.set(s, s.toUpperCase().toCharArray()); 
    } 
    public static void main(String args[]) throws Exception { 
     final String s = "Hello world!"; 
     System.out.println(s); // "Hello world!" 
     mutate(s); 
     System.out.println(s); // "HELLO WORLD!" 
    } 
} 

Sono l'unico a ritenere che questa sia una preoccupazione ENORME?

+24

Dovresti vedere cosa possono fare in C++! (Oh, e probabilmente C#.) –

+5

+1 per la fantastica domanda ... :) – Legend

risposta

93

DEVO PREOCCUPARSI DI QUESTO ???

Ciò dipende interamente dal tipo di programmi che si sta scrivendo e dal tipo di architettura.

Se stai distribuendo un componente software chiamato foo.jar per le persone del mondo, sei completamente alla loro mercè comunque. Potrebbero modificare le definizioni di classe all'interno del tuo .jar (tramite reverse engineering o manipolazione diretta del codice). Potrebbero far girare il tuo codice nella loro JVM, ecc. In questo caso, preoccuparti non ti farà bene.

Se si sta scrivendo un'applicazione Web che si interfaccia solo con persone e sistemi tramite HTTP e si controlla il server delle applicazioni, non è un problema. Certo, gli altri codificatori della tua azienda possono creare un codice che rompe il tuo modello di singleton, ma solo se lo desiderano davvero.

Se il tuo lavoro futuro è scrivere codice su Sun Microsystems/Oracle e hai il compito di scrivere codice per il core Java o altri componenti affidabili, è qualcosa di cui dovresti essere a conoscenza. La preoccupazione, tuttavia, ti farà solo perdere i capelli. In ogni caso probabilmente ti faranno leggere lo Secure Coding Guidelines insieme alla documentazione interna.

Se state per scrivere applet Java, il framework di sicurezza è qualcosa che dovreste essere a conoscenza. Scoprirai che le applet non firmate che tentano di chiamare setAccessible generano solo una SecurityException.

setAccessible non è l'unica cosa che aggira i controlli di integrità convenzionale. Esiste una classe Java non API, chiamata sun.misc.Unsafe, che può fare praticamente tutto ciò che vuole, compreso l'accesso diretto alla memoria. Il codice nativo (JNI) può aggirare anche questo tipo di controllo.

In un ambiente sandbox (ad esempio Java Applet, JavaFX), ogni classe ha un set di autorizzazioni e accesso a Unsafe, setAccessible e la definizione di implementazioni native sono controllate da SecurityManager.

"I modificatori di accesso Java non sono pensati per essere un meccanismo di sicurezza."

Dipende molto da dove viene eseguito il codice Java.Le classi Java principali usano i modificatori di accesso come meccanismo di sicurezza per rafforzare la sandbox.

Quali sono gli usi legittimi di setAccessible?

Le classi di nucleo Java lo utilizzano come un modo semplice per accedere a cose che devono rimanere private per motivi di sicurezza. Ad esempio, il framework Serialization Java lo utilizza per richiamare i costruttori di oggetti privati ​​durante la deserializzazione degli oggetti. Qualcuno ha citato System.setErr e sarebbe un buon esempio, ma curiosamente i metodi della classe System setOut/setErr/setIn utilizzano tutti il ​​codice nativo per impostare il valore del campo finale.

Un altro uso legittimo e ovvio sono le strutture (persistenza, framework web, iniezione) che devono dare un'occhiata all'interno degli oggetti.

I debugger, a mio parere, non rientrano in questa categoria, in quanto normalmente non vengono eseguiti nello stesso processo JVM, ma invece nell'interfaccia con JVM che utilizza altri mezzi (JPDA).

Java potrebbe essere stato progettato in modo da NON avere questo bisogno in primo luogo?

Questa è una domanda piuttosto profonda per rispondere bene. Immagino di sì, ma avresti bisogno di aggiungere qualche altro meccanismo (s) che potrebbe non essere tutto ciò che preferibile.

È possibile limitare setAccessibile solo agli usi legittimi?

La restrizione OOTB più diretta che è possibile applicare è disporre di un SecurityManager e consentire setAccessible solo al codice proveniente da determinate origini. Questo è ciò che Java già fa: le classi Java standard che provengono dal tuo JAVA_HOME sono autorizzate a fare setAccessible, mentre le classi di applet unsigned da foo.com non possono fare setAccessible. Come detto prima, questa autorizzazione è binaria, nel senso che ce l'ha o no. Non esiste un modo ovvio per consentire a setAccessible di modificare determinati campi/metodi mentre disabilita gli altri. Utilizzando SecurityManager, tuttavia, è possibile impedire alle classi di fare riferimento a determinati pacchetti completamente, con o senza riflessione.

Posso scrivere le mie classi affinché siano impostateAccessible-proof indipendentemente dalla configurazione di SecurityManager? ... O sono in balia di chi gestisce la configurazione?

Non puoi e sicuramente lo sei.

+7

+1 e benvenuto in Stack Overflow :) – ewernli

+0

Grazie, gentile signore (anche per il link in arrivo al mio blog) :) –

+0

+1 Risposta di Greaaat, grazie! – reef

14
  • Quali sono gli usi veramente legittimi per setAccessible?

Unit testing, interni della JVM (ad esempio di attuazione System.setError(...)) e così via.

  • Potrebbe Java è stato progettato per non avere questa necessità, in primo luogo?
  • Quali sarebbero le conseguenze negative (se presenti) di tale progettazione?

Un sacco di cose non sarebbero implementabili. Ad esempio, varie iniezioni di persistenza, serializzazione e dipendenza Java dipendono dalla riflessione. E praticamente tutto ciò che si basa sulle convenzioni JavaBeans in fase di runtime.

  • Puoi restringere setAccessible ai soli usi legittimi?
  • È solo tramite SecurityManager?

Sì.

  • Come funziona? Lista bianca/lista nera, granularità, ecc.?

Dipende il permesso, ma credo che il permesso di utilizzare setAccessible è binario. Se si desidera la granularità, è necessario utilizzare un caricatore di classi diverso con un gestore di sicurezza diverso per le classi che si desidera limitare. Immagino che potresti implementare un gestore di sicurezza personalizzato che implementa una logica a grana più fine.

  • E 'comune avere configurarlo nelle vostre applicazioni?

No.

  • Posso scrivere le mie lezioni di essere setAccessible -proof indipendentemente dalla configurazione SecurityManager?
    • O sono alla mercé di chi gestisce la configurazione?

No non si può, e sì che sei.

L'altra alternativa è "applicare" questo tramite strumenti di analisi del codice sorgente; per esempio. regole personalizzate pmd o findbugs. O revisione del codice selettiva del codice identificato da (diciamo) grep setAccessible ....

In risposta al follow

Nessuna delle mie classi hanno alcuna parvenza di privacy esecutivi che-così-mai. Il modello singleton (mettendo da parte i dubbi sui suoi meriti) è ora impossibile da far rispettare.

Se questo ti preoccupa, suppongo che tu debba preoccuparti. Ma in realtà non dovresti provare forza altri programmatori per rispettare le tue decisioni di progettazione. Se sono abbastanza stupidi da creare gratuitamente più istanze dei tuoi singleton (ad esempio), possono vivere con le conseguenze.

E se intendi "privacy" per comprendere il significato di proteggere le informazioni sensibili dalla divulgazione, stai anche abbaiando dall'albero sbagliato. Il modo per proteggere i dati sensibili non è quello di consentire il codice non affidabile nella sandbox di sicurezza che tratta i dati sensibili. I modificatori di accesso Java non intendono essere un meccanismo di sicurezza.

< Esempio di stringa > - Sono l'unico a ritenere che questa sia una preoccupazione ENORME?

Probabilmente non è il solo uno :-). Ma IMO, questa non è una preoccupazione. È accettato il fatto che il codice non affidabile deve essere eseguito in una sandbox. Se si dispone di codice attendibile/un programmatore attendibile che fa cose come questa, allora i tuoi problemi sono peggiori di avere stringhe inaspettatamente mutabili. (Pensa alle bombe logiche ...)

+1

Utile anche per cose come le implementazioni di persistenza. Reflections non è molto utile per i debugger (sebbene originariamente lo fosse). Non è possibile modificare utilmente (sottoclasse) il 'SecurityManager' per questo, perché tutto ciò che si ottiene è il controllo del permesso - tutto ciò che si può veramente fare è guardare gli acc e forse camminare nello stack controllare il thread corrente). 'grep java.lang.reflect. –

+0

@Stephen C: +1 già, ma apprezzerei anche il tuo contributo su un'altra domanda che ho appena aggiunto. Grazie in anticipo. – polygenelubricants

+0

Nota che grep è una pessima idea. Ci sono così tanti modi per eseguire dinamicamente il codice in Java che quasi certamente mancherete uno. Il codice della lista nera in generale è una ricetta per il fallimento. – Antimony

11

La riflessione è davvero ortogonale alla sicurezza/sicurezza in questa prospettiva.

Come limitare la riflessione?

Java ha security manager e ClassLoader come basi per il suo modello di sicurezza. Nel tuo caso, suppongo che tu debba guardare a java.lang.reflect.ReflectPermission.

Ma questo non risolve completamente il problema della riflessione. Le capacità riflessive disponibili dovrebbero essere soggette a un regime di autorizzazione a grana fine che non è il caso ora. Per esempio. per consentire a determinati framework di utilizzare la reflection (ad esempio Hibernate), ma non il resto del codice. O consentire a un programma di riflettere solo in un modo di sola lettura, a scopo di debug.

Un approccio che potrebbe diventare mainstream in futuro è l'utilizzo dei cosiddetti specchi per separare le capacità riflettenti dalle classi. Vedi Mirrors: Design Principles for Meta-level Facilities. Esistono tuttavia vari other research che affrontano questo problema. Ma sono d'accordo sul fatto che il problema è più grave per il linguaggio dinamico rispetto ai linguaggi statici.

Dovremmo essere preoccupati per la superpotenza che la riflessione ci dà? Sì e no.

nel senso che la piattaforma Java deve essere protetta con Classloader e il gestore di sicurezza. La possibilità di confondere con la riflessione può essere vista come una violazione.

No nel senso che la maggior parte del sistema non è comunque del tutto sicuro.Molte classi possono essere frequentemente sottoclasse e potresti già abusare del sistema con questo. Naturalmente è possibile creare classi final o sealed in modo che non possano essere sottoclasse in altri jar. Ma solo poche classi sono protette correttamente (ad esempio, String) in base a questo.

Vedere questo answer about final class per una bella spiegazione. Vedi anche il blog da Sami Koivu per altri java hacking sulla sicurezza.

Il modello di sicurezza di Java può essere considerato insufficiente da un punto di vista. Alcuni linguaggi come NewSpeak adottano un approccio ancora più radicale alla modularità, in cui si ha accesso solo a ciò che viene esplicitamente fornito dall'inversione di dipendenza (per impostazione predefinita nulla).

È anche importante notare che la sicurezza è comunque relativa. A livello di linguaggio, ad esempio, non è possibile impedire a un modulo di modulo di consumare il 100% della CPU o di consumare tutta la memoria fino a un OutOfMemoryException. Tali preoccupazioni devono essere affrontate con altri mezzi. Vedremo in futuro Java esteso con le quote di utilizzo delle risorse, ma non è per domani :)

Potrei ampliare di più sull'argomento, ma penso di aver reso il mio punto.

Problemi correlati