Como limitar setAccessible apenas para usos “legítimos”?

Quanto mais eu aprendi sobre o poder de java.lang.reflect.AccessibleObject.setAccessible , mais surpreso estou com o que ele pode fazer. Isso é adaptado da minha resposta para a pergunta ( usando a reflection para alterar o arquivo final estático File.separatorChar para teste de unidade ).

 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" } } 

Você pode fazer coisas realmente escandalosas:

 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" } } 

Presumivelmente, os projetistas de APIs percebem o quão setAccessible pode ser, mas deve ter admitido que tem usos legítimos para fornecê-lo. Então minhas perguntas são:

  • Quais são os usos verdadeiramente legítimos do setAccessible ?
    • Poderia Java foi projetado para não ter essa necessidade em primeiro lugar?
    • Quais seriam as conseqüências negativas (se houver) de tal projeto?
  • Você pode restringir setAccessible para usos legítimos?
    • É somente através do SecurityManager ?
      • Como funciona? Whitelist / blacklist, granularidade, etc?
      • É comum ter que configurá-lo em seus aplicativos?
    • Posso escrever minhas aulas para serem setAccessible à setAccessible access, independentemente da configuração do SecurityManager ?
      • Ou eu estou à mercê de quem quer que gere a configuração?

Eu acho que mais uma pergunta importante é: eu preciso me preocupar com isso ???

Nenhuma das minhas aulas tem qualquer semelhança de privacidade executável. O padrão singleton (colocando dúvidas sobre seus méritos de lado) é agora impossível de aplicar. Como mostram os meus trechos acima, até algumas suposições básicas de como os trabalhos fundamentais do Java não estão nem perto de serem garantidos.

SÃO ESTES PROBLEMAS NÃO REAIS?


Ok, eu acabei de confirmar: graças ao setAccessible , as strings Java NÃO são imutáveis.

 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!" } } 

Eu sou o único que acha que essa é uma grande preocupação?

EU PRECISO PREOCUPAR-SE COM ISSO ???

Isso depende inteiramente dos tipos de programas que você está escrevendo e de que tipo de arquitetura.

Se você está distribuindo um componente de software chamado foo.jar para as pessoas do mundo, você está completamente à mercê de qualquer maneira. Eles poderiam modificar as definições de class dentro de seu .jar (por meio de engenharia reversa ou manipulação direta de bytecode). Eles podem executar seu código em sua própria JVM, etc. Nesse caso, não será bom para você se preocupar.

Se você estiver escrevendo um aplicativo da Web que faça interface apenas com pessoas e sistemas via HTTP e controle o servidor de aplicativos, também não é uma preocupação. Claro que os colegas codificadores da sua empresa podem criar códigos que quebrem seu padrão singleton, mas somente se eles realmente quiserem.

Se o seu trabalho futuro é escrever código na Sun Microsystems / Oracle e você está encarregado de escrever código para o núcleo Java ou outros componentes confiáveis, é algo que você deve estar ciente. Preocupar-se, no entanto, só vai fazer você perder seu cabelo. De qualquer forma, eles provavelmente farão com que você leia as Diretrizes de Codificação Segura junto com a documentação interna.

Se você vai escrever applets Java, o framework de segurança é algo que você deve estar ciente. Você verá que os applets não assinados que tentam chamar setAccessible resultarão em um SecurityException.

setAccessible não é a única coisa que gira em torno de verificações de integridade convencionais. Há uma class Java não API, chamada sun.misc.Unsafe, que pode fazer praticamente qualquer coisa, incluindo o access à memory diretamente. O código nativo (JNI) também pode contornar esse tipo de controle.

Em um ambiente de área restrita (por exemplo, Java Applets, JavaFX), cada class tem um conjunto de permissions e o access a Unsafe, setAccessible e a definição de implementações nativas são controladas pelo SecurityManager.

“Os modificadores de access Java não pretendem ser um mecanismo de segurança.”

Isso depende muito de onde o código Java está sendo executado. As principais classs Java usam modificadores de access como um mecanismo de segurança para impor a checkbox de proteção.

Quais são os usos verdadeiramente legítimos do setAccessible?

As classs principais do Java usam isso como uma maneira fácil de acessar coisas que devem permanecer privadas por razões de segurança. Como um exemplo, a estrutura de Java Serialization usa-a para invocar construtores de objects privados ao desserializar objects. Alguém mencionou System.setErr, e seria um bom exemplo, mas curiosamente os methods de class System setOut / setErr / setIn usam código nativo para definir o valor do campo final.

Outro óbvio uso legítimo são os frameworks (persistência, frameworks web, injeção) que precisam espiar o interior dos objects.

Depuradores, na minha opinião, não se enquadram nesta categoria, como eles normalmente não são executados no mesmo processo da JVM, mas sim a interface com a JVM usando outros meios (JPDA).

Poderia Java foi projetado para não ter essa necessidade em primeiro lugar?

Essa é uma pergunta bem profunda para responder bem. Eu imagino que sim, mas você precisa adicionar alguns outros mecanismos que podem não ser todos preferíveis.

Você pode restringir setAccessible apenas para usos legítimos?

A restrição OOTB mais direta que você pode aplicar é ter um SecurityManager e permitir setAccessible somente para código vindo de certas fonts. Isso é o que o Java já faz – as classs Java padrão que vêm de seu JAVA_HOME têm permissão para fazer setAccessible, enquanto as classs de applet não assinadas de foo.com não podem fazer setAccessible. Como foi dito antes, essa permissão é binária, no sentido de que uma delas tem ou não. Não existe uma maneira óbvia de permitir que o setAccessible modifique certos campos / methods enquanto desabilita outros. Usando o SecurityManager, você pode, no entanto, impedir que as classs façam referência a certos pacotes completamente, com ou sem reflection.

Posso escrever minhas aulas para serem ajustadas à prova de Acesso, independentemente da configuração do SecurityManager? … Ou eu estou à mercê de quem quer que gere a configuração?

Você não pode e você certamente é.

  • Quais são os usos verdadeiramente legítimos do setAccessible ?

Testes unitários, internos da JVM (por exemplo, implementando System.setError(...) ) e assim por diante.

  • Poderia Java foi projetado para não ter essa necessidade em primeiro lugar?
  • Quais seriam as conseqüências negativas (se houver) de tal projeto?

Muitas coisas seriam impossíveis de implementar. Por exemplo, várias injeções de persistência, serialização e dependência de Java dependem da reflection. E praticamente tudo que se baseia nas convenções do JavaBeans em tempo de execução.

  • Você pode restringir setAccessible para usos legítimos?
  • É somente através do SecurityManager ?

Sim.

  • Como funciona? Whitelist / blacklist, granularidade, etc?

Depende da permissão, mas acredito que a permissão para usar setAccessible é binária. Se você deseja granularidade, é necessário usar um carregador de classs diferente com um gerenciador de segurança diferente para as classs que deseja restringir. Eu acho que você poderia implementar um gerenciador de segurança personalizado que implementa uma lógica mais refinada.

  • É comum ter que configurá-lo em seus aplicativos?

Não.

  • Posso escrever minhas aulas para serem setAccessible à setAccessible access, independentemente da configuração do SecurityManager ?
    • Ou eu estou à mercê de quem quer que gere a configuração?

Não, você não pode e sim você é.

A outra alternativa é “impor” isso via ferramentas de análise de código-fonte; por exemplo, regras personalizadas de pmd ou findbugs . Ou revisão de código seletivo de código identificado por (digamos) grep setAccessible ...

Em resposta ao acompanhamento

Nenhuma das minhas aulas tem qualquer semelhança de privacidade executável. O padrão singleton (colocando dúvidas sobre seus méritos de lado) é agora impossível de aplicar.

Se isso te preocupa, suponho que você precisa se preocupar. Mas realmente você não deveria estar tentando forçar outros programadores a respeitar suas decisões de design. Se eles são estúpidos o suficiente para criar gratuitamente múltiplas instâncias de seus singletons (por exemplo), eles podem viver com as conseqüências.

E se você quer dizer “privacidade” para abranger o significado de proteger informações confidenciais da divulgação, você também está latindo na tree errada. A maneira de proteger dados confidenciais é não permitir código não confiável na checkbox de proteção de segurança que lida com dados confidenciais. Modificadores de access Java não se destinam a ser um mecanismo de segurança.

– Eu sou o único que acha que essa é uma grande preocupação?

Provavelmente não é o único :-). Mas IMO, isso não é uma preocupação. É aceito que códigos não confiáveis ​​devem ser executados em uma sandbox. Se você tem um código confiável / um programador confiável fazendo coisas como esta, então seus problemas são piores que ter Strings inesperadamente mutáveis. (Pense bombas lógicas …)

A reflection é de fato ortogonal à segurança sob essa perspectiva.

Como podemos limitar a reflection?

Java tem gerenciador de segurança e ClassLoader como fundamentos para seu modelo de segurança. No seu caso, eu acho que você precisa olhar para java.lang.reflect.ReflectPermission .

Mas isso não resolve completamente o problema da reflection. Os resources refletivos disponíveis devem estar sujeitos a um esquema de autorização refinado, o que não é o caso agora. Por exemplo, para permitir que determinada estrutura use a reflection (por exemplo, Hibernate), mas não o resto do seu código. Ou permitir que um programa reflita apenas de uma maneira somente leitura, para fins de debugging.

Uma abordagem que pode se tornar mainstream no futuro é o uso dos chamados espelhos para separar os resources reflexivos das classs. Veja Mirrors: Princípios de Design para Instalações Meta-level . No entanto, existem várias outras pesquisas que abordam essa questão. Mas eu concordo que o problema é mais severo para linguagem dinâmica do que linguagens estáticas.

Deveríamos estar preocupados com a superpotência que a reflection nos dá? Sim e não.

Sim, no sentido de que a plataforma Java deve ser protegida com o Classloader e o gerenciador de segurança. A capacidade de mexer com a reflection pode ser vista como uma violação.

Não, no sentido de que a maioria dos sistemas não é totalmente segura. Muitas classs podem freqüentemente ser subclassificadas e você pode abusar do sistema apenas com isso. Claro que as aulas podem ser final , ou seladas para que não possam ser subclassificadas em outro jarro. Mas apenas algumas classs são seguras corretamente (por exemplo, String) de acordo com isso.

Veja esta resposta sobre a aula final para uma boa explicação. Veja também o blog de Sami Koivu para mais java hacking em torno da segurança.

O modelo de segurança do Java pode ser visto como insuficiente para alguma consideração. Algumas linguagens como o NewSpeak adotam uma abordagem ainda mais radical à modularidade, na qual você tem access apenas ao que é explicitamente dado a você por inversão de dependência (por padrão, nada).

Também é importante notar que a segurança é de qualquer maneira relativa . No nível da linguagem, você pode, por exemplo, não impedir que um módulo consumindo 100% da CPU ou consumindo toda a memory até um OutOfMemoryException . Tais preocupações precisam ser abordadas por outros meios. Nós talvez veremos no futuro o Java estendido com cotas de utilização de resources, mas não é para amanhã 🙂

Eu poderia expandir mais sobre o assunto, mas acho que fiz o meu ponto.