Quanto mais aprendo sobre o poder do java.lang.reflect.AccessibleObject.setAccessible
, mais espantado fico com o que ele pode fazer. Isso foi adaptado de minha resposta à pergunta ( Usando reflexão para alterar File.separatorChar final estático 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 verdadeiramente ultrajantes:
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 designers da API percebem o quão abusivo setAccessible
pode ser, mas devem ter admitido que ela tem usos legítimos para fornecê-la. Então, minhas perguntas são:
- Para que são os usos verdadeiramente legítimos
setAccessible
?- O Java poderia ter sido projetado para NÃO ter essa necessidade em primeiro lugar?
- Quais seriam as consequências negativas (se houver) de tal projeto?
- Você pode restringir apenas
setAccessible
para usos legítimos?- É apenas através
SecurityManager
?- Como funciona? Whitelist / blacklist, granularity, etc?
- É comum ter que configurá-lo em seus aplicativos?
- Posso escrever minhas aulas para serem à
setAccessible
prova, independentemente daSecurityManager
configuração?- Ou estou à mercê de quem gerencia a configuração?
- É apenas através
Acho que mais uma pergunta importante é: PRECISO ME PREOCUPAR COM ISSO ???
Nenhuma das minhas aulas tem qualquer aparência de privacidade obrigatória. O padrão singleton (colocando de lado as dúvidas sobre seus méritos) agora é impossível de aplicar. Como mostram meus trechos acima, mesmo algumas suposições básicas de como funciona o Java fundamental não estão nem perto de serem garantidas.
ESTES PROBLEMAS NÃO SÃO REAIS ???
Ok, acabei de confirmar: graças a setAccessible
, 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 isso é uma grande preocupação?
fonte
Respostas:
PRECISO ME PREOCUPAR COM ISSO ???
Isso depende inteiramente dos tipos de programas que você está escrevendo e para que tipo de arquitetura.
Se você está distribuindo um componente de software chamado foo.jar para as pessoas do mundo, você está completamente à mercê delas de qualquer maneira. Eles podem modificar as definições de classe 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 adiantará se preocupar.
Se você está escrevendo um aplicativo da web que faz interface apenas com pessoas e sistemas via HTTP e controla o servidor de aplicativos, também não é uma preocupação. Claro que os colegas programadores em sua empresa podem criar códigos que quebrem seu padrão singleton, mas apenas se realmente quiserem.
Se o seu trabalho futuro for escrever código na Sun Microsystems / Oracle e você tiver a tarefa de escrever código para o núcleo Java ou outros componentes confiáveis, é algo que você deve estar ciente. Preocupar-se, entretanto, só fará você perder o cabelo. Em qualquer caso, eles provavelmente farão você ler as Diretrizes de Codificação Segura junto com a documentação interna.
Se você for escrever miniaplicativos Java, a estrutura de segurança é algo que você deve conhecer. Você descobrirá que applets não assinados tentando chamar setAccessible resultarão apenas em SecurityException.
setAccessible não é a única coisa que evita as verificações de integridade convencionais. Existe uma classe Java principal não API chamada sun.misc.Unsafe que pode fazer praticamente tudo o que quiser, incluindo acessar a memória diretamente. O código nativo (JNI) pode contornar esse tipo de controle também.
Em um ambiente de área restrita (por exemplo, Java Applets, JavaFX), cada classe tem um conjunto de permissões e acesso a Unsafe, setAccessible e as implementações nativas definidas são controladas pelo SecurityManager.
"Os modificadores de acesso Java não se destinam a ser um mecanismo de segurança."
Isso depende muito de onde o código Java está sendo executado. As principais classes Java usam modificadores de acesso como um mecanismo de segurança para impor a sandbox.
Quais são os usos verdadeiramente legítimos de setAccessible?
As classes principais do Java o usam como uma maneira fácil de acessar coisas que devem permanecer privadas por razões de segurança. Como exemplo, a estrutura de serialização Java a usa para invocar construtores de objetos privados ao desserializar objetos. Alguém mencionou System.setErr, e seria um bom exemplo, mas curiosamente, todos os métodos da classe System setOut / setErr / setIn usam código nativo para definir o valor do campo final.
Outro uso legítimo óbvio são os frameworks (persistência, frameworks web, injeção) que precisam espiar dentro dos objetos.
Os depuradores, na minha opinião, não se enquadram nesta categoria, pois normalmente não rodam no mesmo processo JVM, mas sim na interface com a JVM por outros meios (JPDA).
O Java poderia ter sido projetado para NÃO ter essa necessidade em primeiro lugar?
Essa é uma pergunta muito profunda para responder bem. Imagino que sim, mas você precisaria adicionar alguns outros mecanismos que podem não ser tão 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 apenas para código vindo de certas fontes. Isso é o que Java já faz - as classes Java padrão que vêm de seu JAVA_HOME têm permissão para fazer setAccessible, enquanto classes de miniaplicativo não assinadas de foo.com não têm permissão para fazer setAccessible. Como foi dito antes, essa permissão é binária, no sentido de que se tem ou não. Não há uma maneira óbvia de permitir que setAccessible modifique certos campos / métodos enquanto desabilita outros. Usando o SecurityManager, você pode, no entanto, impedir que as classes façam referência a certos pacotes completamente, com ou sem reflexão.
Posso escrever minhas classes para serem definidas à prova de acessibilidade, independentemente da configuração do SecurityManager? ... Ou fico à mercê de quem gerencia a configuração?
Você não pode e com certeza é.
fonte
Teste de unidade, internos da JVM (por exemplo, implementação
System.setError(...)
) e assim por diante.Muitas coisas seriam impossíveis de implementar. Por exemplo, várias injeções de persistência, serialização e dependência de Java dependem de reflexão. E praticamente qualquer coisa que dependa das convenções JavaBeans em tempo de execução.
Sim.
Depende da permissão, mas acredito que a permissão de uso
setAccessible
seja binária. Se desejar granularidade, você precisará usar um carregador de classes diferente com um gerenciador de segurança diferente para as classes que deseja restringir. Eu acho que você poderia implementar um gerenciador de segurança personalizado que implemente uma lógica mais refinada.Não.
Não, você não pode, e sim você é.
A outra alternativa é "impor" isso por meio de ferramentas de análise de código-fonte; por exemplo, costume
pmd
oufindbugs
regras. Ou revisão seletiva do código identificado por (digamos)grep setAccessible ...
.Em resposta ao acompanhamento
Se isso o preocupa, suponho que você precise se preocupar. Mas realmente você não deve tentar forçar outros programadores a respeitar suas decisões de design. Se as pessoas são estúpidas o suficiente para usar a reflexão para criar gratuitamente várias instâncias de seus singletons (por exemplo), elas podem viver com as consequências.
Por outro lado, se você quer dizer "privacidade" para abranger o significado de proteger informações confidenciais da divulgação, você está errando na árvore. A maneira de proteger dados confidenciais em um aplicativo Java é não permitir que código não confiável na caixa de proteção de segurança que lida com dados confidenciais. Os modificadores de acesso Java não se destinam a ser um mecanismo de segurança.
Provavelmente não o único :-). Mas IMO, isso não é uma preocupação. É fato aceito que o código não confiável deve ser executado em uma sandbox. Se você tem um código de confiança / um programador de confiança fazendo coisas como essa, seus problemas são piores do que Strings mutáveis inesperadamente. (Pense em bombas lógicas, exfiltração de dados por meio de canais secretos , etc.)
Existem maneiras de lidar com (ou mitigar) o problema de um "mau ator" em sua equipe de desenvolvimento ou operações. Mas eles são caros e restritivos ... e exagerados para a maioria dos casos de uso.
fonte
SecurityManager
para isso, porque tudo o que você obtém é a verificação de permissão - tudo o que você realmente pode fazer é olhar para o acc e talvez percorrer a pilha, verificar o thread atual).grep java.lang.reflect.
A reflexão é de fato ortogonal à proteção / proteção sob essa perspectiva.
Como podemos limitar a reflexão?
Java possui um gerenciador de segurança e
ClassLoader
como base para seu modelo de segurança. No seu caso, acho que você precisa dar uma olhadajava.lang.reflect.ReflectPermission
.Mas isso não resolve completamente o problema da reflexão. As capacidades reflexivas disponíveis devem estar sujeitas a um esquema de autorização de baixa granularidade, o que não é o caso agora. Por exemplo, para permitir que determinado framework use reflexão (por exemplo, Hibernate), mas não o resto do seu código. Ou para permitir que um programa reflita apenas de forma somente leitura, para fins de depuração.
Uma abordagem que pode se tornar dominante no futuro é o uso dos chamados espelhos para separar os recursos reflexivos das classes. Consulte Espelhos: Princípios de Projeto para Instalações de Meta-nível . No entanto, existem várias outras pesquisas que abordam esse problema. Mas concordo que o problema é mais grave para linguagens dinâmicas do que linguagens estáticas.
Devemos nos preocupar com o superpoder que a reflexão nos dá? Sim e não.
Sim, no sentido de que a plataforma Java deve ser protegida com
Classloader
um gerenciador de segurança. A capacidade de mexer com a reflexão pode ser vista como uma violação.Não, no sentido de que a maioria dos sistemas não são totalmente seguros. Muitas classes podem frequentemente ser subclasses e você já pode abusar do sistema apenas com isso. É claro que as classes podem ser feitas
final
ou lacradas de forma que não possam ser subclassificadas em outro jar. Mas apenas algumas classes são protegidas 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 informações sobre hackers java em torno da segurança.
O modelo de segurança do Java pode ser visto como insuficiente para alguns aspectos. Algumas linguagens, como NewSpeak, têm uma abordagem ainda mais radical à modularidade, em que você tem acesso apenas ao que é explicitamente fornecido a você pela inversão de dependência (por padrão, nada).
Também é importante observar que a segurança é relativa . No nível do idioma, você não pode, por exemplo, evitar que um módulo consuma 100% da CPU ou consuma toda a memória até a
OutOfMemoryException
. Essas preocupações precisam ser tratadas por outros meios. Talvez veremos no futuro o Java estendido com cotas de utilização de recursos, mas não é para amanhã :)Eu poderia expandir mais o assunto, mas acho que fiz o que quero dizer.
fonte