sexta-feira, 24 de julho de 2009

Segurança de aplicações Java

Galera, finalmente escrevo aqui no blog. Para o meu post inicial escolhi um tema bastante comentado em nossas conversas: segurança em aplicações Java.
Antes de tudo, vou falar sobre a segurança nativa a qualquer aplicação Java, provida já pela JRE. Esta segurança é feita através de um mecanismo de permissões associadas a um gerenciador de segurança (instância da classe SecurityManager), associados a uma execução do sistema (representada pela classe System).
Um SecurityManager é responsável por aplicar uma política de segurança ao sistema. Esta classe define vários métodos, com a assinatura do tipo "checkXXX(argumento, ...)", responsáveis por verificar se uma determinada operação é possível de ser executada.
Por exemplo os seguintes métodos:

  • checkAccept(String host, int port) - indica se é possível aceitar conexões de Sockets a partir de um determinado host e de uma determinada porta;
  • checkExit(int status) - indica se é possível terminar a execução da JVM com um determinado status;
  • checkDelete(String file) - indica se é permitido remover um determinado arquivo.
Um programador pode obter o SecurityManager atual a partir da chamada System.getSecurityManager( ) e pode definí-lo através de código, System.setSecurityManager(securityManager), passando como parâmetro uma subclasse de SecurityManager, ou definindo no momento da inicialização da JVM um "policy file" contendo as definições de segurança para a aplicação: -Djava.security.policy=arquivo.policy.
Este "arquivo de polícia" pode ser definido manualmente (utililizando um editor de textos qualquer) ou através da ferramenta policytool (que acompanha qualquer distribuição do JDK).

O formato deste arquivo é o seguinte:
grant {

permission CLASSE_DA_PERMISSÃO "PARÂMETRO_1", "PARÂMETRO_2", "PARÂMETRO_N";

};
Exemplo:
grant {

permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read";

};
Ao iniciar uma aplicação a JVM verifica se foi definido algum Security Manager, caso negativo, ele introduz uma implementação de acordo com o tipo da aplicação. Para applets, é introduzido um Security Manager altamente restritivo, que não permite que as aplicações acessem os recursos da máquina em que executa, por exemplo; para aplicações web o servidor web/servidor de aplicações introduz o seu próprio Security Manager, que normalmente não permite a leitura/escrita em áreas protegidas do sistema de arquivos; finalmente, para aplicações desktop, o Security Manager padrão não realiza quaisquer restrições.
Façamos agora um pequeno estudo de caso. Aplicações RMI merecem bastante atenção em relação à segurança, já que instâncias de classes serão carregadas dinâmicamente pela rede, ou seja, caso um usuário malicioso conheça o endereço do "RMI registry" e as interfaces utilizadas, é muito fácil introduzir um código malicioso neste registro, que poderá vir a ser executado por outros usuários desavisados.
No caso do RMI, a implementação atual de Java define um gerenciador de segurança (RMISecurityManager) e algumas classes de permissão que devem ser parametrizadas para a correta configuração da aplicação.
Para utilizar o gerenciador de segurança RMI, um usuário deve simplesmente fazer:
System.setSecurityManager(new RMISecurityManager());
As classes de permissão definidas para ser utilizadas por este gerenciador são:
  • java.io.FilePermission;
  • java.net.SocketPermission;
  • java.security.SecurityPermission;
  • java.lang.RuntimePermission.
Estas permissões podem ser configuradas a partir de um objeto Properties, passado para o gerenciador de segurança, ou a patir de um arquivo de polícia (opção mais comum e recomendada). Um exemplo típico de um arquivo de polícia para uma aplicação RMI é:

grant {

permission java.net.SocketPermission "*:1024-", "connect,accept";

permission java.io.FilePermission "/home/public_html/classes/-", "read";

};

grant codeBase "http://exemplo.com/classes/" {

permission java.security.AllPermission;

}


Neste exemplo acima nós podemos ver que para aplicação em questão é permitido o acesso à porta 1024 a partir de qualquer host; também é permitido apenas a leitura aos arquivos contidos no diretório "/home/public_html/classes"; finalmente, é permitido o download de classes a partir da url "http://exemplo.com/classes".
Também é possível definir as suas próprias permissões e utilizá-las em suas aplicações. Para isto, nos métodos onde serão verificadas as permissões, deve-se adicionar um código de verificação tal como:

SecurityManager sm = System.getSecurityManager();

if (sm != null) {

sm.checkPermission(new ClasseDePermissao());

}

Desta forma, se não for definido nenhum gerente de segurança a aplicação irá correr normalmente, porém caso seja definido um, a sua execução será condicionada pelas permissões definidas. Para definir permissões personalizadas, um usuário tanto pode criar subclasses da classe abstrata Permission, ou pode simplesmente parametrizar a classe RuntimePermission, ex:

SecurityManager sm = System.getSecurityManager();

if (sm != null) {

sm.checkPermission(new RuntimePermission("excluirRegistro"));

}

Finalmene, uma dica bem legal é utilizar um ProfilingSecurityManager, que registra todas as tentativas de acesso de uma determinada aplicação aos recursos de segurança da JVM. Desta forma nós poderemos encontrar "furos" e/ou realizar um ajuste fino da sua configuração de segurança. No link a seguir podemos ver uma discussão pormenorizada sobre a implementação/utilização de um ProfilingSecurityManager:

Link

Pessoal, é isso, concluo aqui este meu primeiro post, espero que vocês tenham gostado e que comentem, prometo complementá-lo com mais dois contendo informações sobre JAAS e Spring Security.

Nenhum comentário:

Postar um comentário