Académique Documents
Professionnel Documents
Culture Documents
Segurança
Lição 1
Introdução a Segurança
Requerimentos de Software
NetBeans Enterprise Pack 5.5 executando sobre Java 2 Platform Standard Edition
Development Kit 5.0 ou superior (JDK 5.0, versão 1.5.0_01 ou superior), contemplando
a Java Runtime Environment, ferramentas de desenvolvimento para compilar, depurar,
e executar aplicações escritas em linguagem Java. Sun Java System Application Server
Platform Edition 9.
• Para Solaris, Windows, e Linux, os arquivos da JDK podem ser obtidos para
sua plataforma em http://java.sun.com/j2se/1.5.0/download.html
• Para Mac OS X, Java 2 Plataform Standard Edition (J2SE) 5.0 Release 4, pode
ser obtida diretamente da Apple's Developer Connection, no endereço: http://
developer.apple.com/java (é necessário registrar o download da JDK).
Segurança 2
JEDITM
Auxiliadores especiais
Coordenação do DFJUG
Agradecimento Especial
John Paul Petines – Criador da Iniciativa JEDITM
Rommel Feria – Criador da Iniciativa JEDITM
Segurança 3
JEDITM
1. Objetivos
Este módulo trata segurança na perspectiva da linguagem Java. Discutiremos porque Java é dito
seguro, o que significa segurança e, mais importante ainda, aprenderemos as melhores formas de
se utilizar os dispositivos de segurança da plataforma Java dentro de seus próprios projetos.
Veremos algumas das características básicas da plataforma Java que proporcionam segurança: o
Class Loader, o verificador de bytecode e o gerente de segurança. Também serão abordadas as
extensões que valorizam o modelo de segurança em Java através das assinaturas digitais,
provedores de segurança, e o controlador de acesso. O objetivo desta lição é fornecer uma
compreensão da arquitetura de segurança do modelo Java e como esse pode ser melhor utilizado.
Pressupõe-se que o leitor tenha um bom conhecimento sobre programação Java. Em particular,
como escrever projetos Java, pois avançados recursos de segurança e algoritmos de criptografia
serão discutidos, é feito de tal forma que o leitor é primariamente interessado em utilizar a
biblioteca para executar determinadas tarefas. Iremos analisar a um nível fundamental o que é
uma assinatura digital e como ela pode ser criada e utilizada; entretanto, não abordaremos a
teoria da criptografia por trás de uma assinatura digital ou provar que uma assinatura digital é
realmente segura.
Para aqueles que são suficientemente versados nestas matérias, mostraremos como as bibliotecas
podem ser incrementadas para suportar novos tipos de algoritmos criptográficos; porém, a
matemática rigorosa e definições de criptografia não serão discutidas neste material.
Ao final desta lição, o estudante será capaz de:
• Conhecer sobre os principais aspectos de segurança
• Aprender boas práticas de segurança
• Observar as práticas de segurança aplicadas à linguagem Java
Segurança 4
JEDITM
2. Introdução a Segurança
O Glossário Nacional de Segurança de Sistemas de Informação dos Estados Unidos (EUA) define
a Segurança de Sistemas de Informação (INFOSEC) como:
Proteção de sistemas de informação contra o acesso não-autorizado a informação ou sua
modificação, seja por meio de armazenamento, processamento ou tráfego, e contra a
negação de serviços aos usuários autorizados ou o fornecimento destes para usuários não
autorizados, incluindo medidas necessárias para detectar, documentar e bloquear tais
ameaças.
Um documento completo para maiores referencias pode ser encontrado em:
http://www.cnss.gov/Assets/pdf/nstissd_501.pdf
Uma forma simples para expressar esta definição é “prover informação e serviço correto para as
pessoas certas no momento certo”. Segurança da informação significa no fornecimento de dados
corretos para pessoas de quem possuem direitos do acesso a tais dados. Além disso, está
incluída o bloqueio da informação para pessoas sem permissão para sua visualização.
Há vários anos atrás, quando as redes de computadores não eram tão dominantes quanto são nos
dias de hoje, era relativamente fácil guardar as informações. Naquela época, para poder causar
algum dano ao sistema, era necessário ter acesso físico ao sistema. Acesso remoto, acessibilidade
das redes e a Internet mudaram tudo isso.
"A rede é o computador", uma expressão criada pela Sun Microsystems em meados dos anos
oitenta, representa uma das grandes verdades destes tempos. Temos mais e mais computadores
conectados à rede a cada dia que passa. Aplicações passaram de um sistema único (por exemplo,
época dos mainframes) para um modelo múltiplo de cooperação entre os diferentes módulos de
sistemas.
À medida que a rede cresce, a segurança da informação torna-se mais vulnerável. Sistemas estão
mais propensos a ataques e outras concessões de acesso e revelam uma janela aberta para
grupos ou indivíduos com motivações hostis.
Especialistas em segurança costumam dizer que a melhor maneira de manter seus dados seguros
é retirar o computador da rede e bloquear todas as portas para o acesso, isso inclui, portas USB,
unidades de CD-ROM, DVD-ROM ou mesmo disquetes.
Infelizmente, nesse estado, o poder do sistema não é explorado de forma eficaz. Em vez de tornar
o sistema indisponível para a rede, medidas de segurança devem ser definidas para proteger as
informações e os serviços.
Segurança 5
JEDITM
3.1. Confidencialidade
3.2. Integridade
Garante que as informações passadas não sejam corrompidas ou alteradas de alguma forma. Esta
propriedade não só assegura que sejam autênticas e completas como também, garante que
possam ser confiáveis e invocadas.
Integridade assegura:
● Modificações não são feitas por pessoal não autorizado aos dados ou processos
Segurança 6
JEDITM
● Alterações só podem ser realizadas por pessoal autorizado aos dados ou processos
● Os dados são internamente e externamente consistentes
No entanto, convém notar que a integridade dos dados não é lidar com a exatidão dos dados.
Essa propriedade garante que somente as informações disponíveis no sistema seja a mesma
informação que o usuário acessou em tempos trás. A integridade dos dados pode ser
comprometida quando a informação seja corrompida, voluntariamente ou involuntariamente,
antes que seja lida pelo seu destinatário. Assume-se que, se a informação foi gerada
corretamente, será preservada nesse estado.
A fonte da integridade deve ser de confiança em que o remetente da informação seja alguem que
realmente supomos que seja. A fonte de integridade pode ser comprometida quando um agente
burla ou forja a sua identidade através de informações incorretas enviadas para um destinatário.
Spoofing é uma situação em que os dados de identidade são falsificados por mascaramento numa
situação de verdade.
Uma espécie de falsificação é chamada de phising. Alguns bem conhecidos endereços WEB são
reproduzidos de forma a ter um visual parecido com o do endereço oficial. Este tipo de ataque é
freqüentemente realizado com um endereço falso, onde as vulnerabilidades dos navegadores WEB
são exploradas para encobrir a falha. Estes endereços procuram enganar os usuários solicitando
informações sobre seus dados pessoais, tais como, nome completo, nome de usuário, senha e
informações do cartão de crédito. Sem suspeitas, as vítimas são encaminhados para endereço e
no final uma mensagem de erro é exibida indicando que a senha está incorreta. Neste momento,
a informação já foi recolhida pelos atacantes para o seu próprio benefício.
Integridade de dados é ter confiança de que a informação não foi alterada entre a sua
transmissão e sua recepção. Note que, em um modo formal de segurança, a integridade é mais
interpretada restritivamente no sentido de proteção contra a destruição ou alteração não
autorizada de informações.
3.3. Disponibilidade
No momento em que a informação é necessária, será exibida e pronta para ser utilizada pelo
pessoal autorizado. Todos os recursos autorizados – dados, funcionalidades e serviços - devem
estar disponíveis quando necessários.
É possível que a confidencialidade e a integridade da informação estejam protegidas porém, para
um invasor, estes dados não devem ser acessíveis. Isso faz com que a informação seja inútil e,
portanto, sem garantia.
Um bom exemplo da violação de disponibilidade é o da “negação de serviço” ou “ataque DoS”.
Um “ataque DoS” é um tipo de ataque contra uma determinada rede que se destina a colocar as
redes em situação de submissão de dados, através da inundação de ataques, isto torna o tráfego
inútil. Talvez um dos mais populares foi o ataque "Code Red" realizado em 2001.
Code Red explorava a vulnerabilidade de um determinado servidor WEB para perturbar o serviço.
Code Red era um verme (um programa que conecta-se a outras máquinas e se auto-replica)
que começou a infectar as máquinas rodando suas várias versões no servidor. O vírus enviava seu
código através de uma solicitação HTTP e explorava uma determinada porta – descobrindo a
vulnerabilidade. O vírus era executado no computador do cliente. Em vez de voltar a corrigir uma
página da WEB, o vírus retornava seu próprio código HTML e exibia a seguinte mensagem:
Bem vindo ao http:// www.worm.com!
Atacado por chineses!
Outra versão do mesmo vírus tentou atacar um determinado endereço IP, enviando grandes
quantidades de dados inúteis. O referido endereço IP pertencia ao endereço www.whitehouse.gov.
Em vez de exibir o site oficial da Casa Branca, o texto apresentado acima era exibido. O tráfego
do site da Casa Branca foi redirecionado para um outro endereço IP e atacou o endereço IP numa
URL que não era mais válida.
Disponibilidade é a garantia de que os sistemas sejam responsáveis pela entrega,
Segurança 7
JEDITM
Segurança 8
JEDITM
4.2. Autorização
Segurança 9
JEDITM
É o conceito de garantir que um contrato ou um acordo, que tenha sido feito, não possa ser
negado por nenhuma das partes envolvidas em momento futuro.
● Não repudiação de origem protege contra o emissor que nega que a mensagem foi
enviada. Prova que o dado foi enviado
● Não repudiação de entrega protege contra o receptor que nega que os dados foram
recebidos. Prova que o dado foi recebido
Isto significa que a não repudiação garante que o emissor e o receptor foram as partes que
realmente enviaram e receberam as mensagens. Em outras palavras, isto assegura que o emissor
dos dados recebe uma prova da entrega da mensagem e que o receptor recebe uma prova da
identidade do emissor, ou seja, nenhum dos dois poderá negar o fato.
Muitas organizações querem tornar seguras as mensagens de email de seus colaboradores. MIME
Seguro é uma solução de segurança de email comumente suportada pelos servidores nos dias de
hoje pois, não provê somente confidencialidade, autenticação de dados e proteção de integridade
como também a não repudiação para mensagens de email usando assinaturas digitais.
4.5. Auditoria
Segurança 10
JEDITM
Segurança 11
JEDITM
JVM X X X
Carregadores de Classe X X
Gerenciadores de Segurança X X X X
Domínios de Segurança X X
Criptografar, Encriptar, SSL X
Sumário de Mensagens X X
Assinaturas e Certificados
X X
Digitais
Lista de Controle de Acesso X
JAAS X X
Tabela 1: Boas Práticas de Segurança e a linguagem Java
Segurança 12
Módulo 7
Segurança
Lição 2
Sandbox
1. Objetivos
Sempre que é discutida a segurança Java, normalmente ela é focada no modelo de Sandbox
(caixa de areia). Este modelo permite ao usuário final configurar restrições e permissões para um
programa.
Tradicionalmente, executamos segurança nos computadores baseados em um sistema de
confiança. Antes de executar um sistema, deveríamos ter a confiança no código-fonte deste. Uma
vez que a aplicação tenha passados por esta checagem, possui mandato no sistema em que está
sendo executada. Se por qualquer motivo o programa possui uma intenção maliciosa, poderá
destruir o sistema sem problemas, visto que não existem restrições ao programa. Expondo isto de
forma simples, para proteger o computador devemos restringir o que fazer em primeiro lugar.
Ao final desta lição, o estudante será capaz de:
• Identificar o modelo de segurança padrão empregado – Sandbox
• Conhecer os componentes da Sandbox
• Realizar as configurações dos componentes da Sandbox
• Definir os domínios de proteção de sua aplicação
• Aplicar a política de segurança, por intermédio das permissões
• Entender como as classes podem ser assinadas (certificação digital)
Segurança 4
JEDITM
2. Modelo Sandbox
O modelo Sandbox torna fácil proteger o computador de programas carregados a partir de fontes
não confiáveis. Em lugar de obter a confiança no recurso, a segurança é alcançada permitindo que
qualquer programa seja executado entretanto, restringindo suas ações que poderia danificar seu
computador. A vantagem é que invés de restringir ou checar qualquer coisa que entra em seu
computador, a Sandbox permite que qualquer programa seja executado e previne que este
realizar qualquer ação que poderia danificar o sistema.
O modelo de segurança Java gira ao redor desta idéia de uma Sandbox. Gira ao redor da idéia de
que quando é permitido que um programa seja executado no computador, deveríamos ser
capazes de fornecer um ambiente onde o programa pode fazer o que foi projetado para fazer e ao
mesmo tempo permite a habilidade de restringir acesso aos recursos que ele não deveria acessar.
Há diferentes tamanhos de Sandbox tipicamente configuradas para um programa Java. Estes são:
● Mínima – Onde um programa tem acesso à CPU, possui sua própria memória, bem como
acesso para dispositivos de interface humana (isto é, Monitor, Teclado, Mouse)
● Padrão – Similar a mínima, exceto que também fornece acesso ao servidor WEB da qual
o programa Java foi carregado
● Restrita – Similar a padrão, exceto que também fornece acesso para certos, mas não
todos, recursos do sistema operacional
● Aberta – Esta permite ao programa acessar qualquer recurso do sistema anfitrião
O modelo de segurança Java envolve todo aspecto de sua arquitetura. Para ser capaz de
assegurar que este está corretamente no lugar em que necessitamos olhar para diferentes partes
da arquitetura, bem como entender como estas tecnologias funcionam juntas.
Os seguintes componentes são os fundamentos do Modelo de Segurança Java:
• Características de segurança construídas na JVM (Máquina virtual Java)
• A arquitetura do carregador de classes
• O verificador de arquivos de classe
• O gerenciador de segurança e a API Java
Uma força chave do modelo da Sandbox Java é sua customização. Da lista acima, o carregador de
classes e o gerenciador de segurança são completamente customizáveis. Para definir sua própria
segurança, você define sua própria implementação ou subclasses de java.lang.SecurityManager.
Com sua implementação do gerenciador de segurança, você define seus próprios métodos para
controlar o acesso a um determinado recurso - como escrever em um arquivo, por exemplo.
Adicionalmente, é recomendado criar um gerenciador de segurança quando criamos um
carregador para instanciar classes a partir de recursos não confiáveis.
Segurança 5
JEDITM
Em Java, o carregador de classes carrega os bytecodes a partir das classes compiladas, força
limites de espaços de nome. Isto controla partes da JVM que o código pode acessar.
Classes carregadas a partir do sistema de arquivos local tem seu próprio nome de espaço.
Adicionalmente, um nome de espaço para cada fonte de rede também é definido. Isto assegura e
protege a JVM do conflito provocado por classes com o mesmo nome.
Uma vez que o carregador de classes carregou uma classe, chama o verificador de bytecodes. A
tarefa principal deste é conferir o código para ver se o mesmo está de acordo com a especificação
da linguagem de programação Java e procurar por quaisquer das seguintes violações:
● Regras da linguagem de Programação Java
● Restrições de nome de espaço
● Estouros de pilha (overflows e underflows)
● Conversão ilegal de tipos de dados
O verificador de bytecodes pode provar o seguinte:
● O arquivo de classe tem o formato correto
● Classes finais não possuem subclasses e métodos finais não estão realizando polimorfismo
por override
● Cada classe possui uma única superclasse
● Não existe conversão ilegal de atributos para tipos primitivos
● Não existe conversão ilegal em objetos
O propósito do gerenciador de segurança é determinar quais operações uma classe tem permissão
para executar. De forma simples, o gerenciador de segurança é responsável por determinar a
maioria dos parâmetros de uma Sandbox Java. Se um sistema em Java tenta escrever em um
arquivo ou conectar-se a um recurso de rede, primeiro deve obter permissão do gerenciador de
segurança. Além disso, quando um aplicativo deseja modificar o estado dos serviços, o
gerenciador de segurança controla tais operações se as julgar perigosas.
Por padrão, um gerenciador de segurança não é usado quando um programa está sendo
executado. Para habilitar uma aplicação Java a usá-lo, deve ser especificado:
–D java.security.manager
Segurança 6
JEDITM
4.1. Permissões
Cada classe tem um conjunto de permissões que define o que está autorizada a executar. A
Sandbox Java é então definida com base nestas permissões. Quando uma ação é executada por
uma classe, as permissões são checadas pela máquina virtual. Se determinada classe não deveria
ter a permissão para executar uma ação em particular, uma exceção é lançada e a operação é
bloqueada.
Classes do núcleo da API Java sempre possuem permissão para executar qualquer ação.
Quaisquer outras classes, mesmo que definidas na variável classpath, devem ter permissão para
executar qualquer ação sensível. Estas permissões são definidas em arquivos de política de
segurança que são administrados pelos usuários finais e são usados pela Sandbox para gerenciar
os arquivos de política de segurança.
Permissões empregadas pela máquina virtual são baseadas em um sistema de classes. Isto
significa que qualquer aplicação ou API pode definir suas próprias permissões e assegurar que os
usuários ou administradores possuem as permissões das APIs antes de executar. A próxima lista
descreve as permissões padrões usadas pelo núcleo de classes Java:
● java.io.FilePermission
○ Ações: ler, escrever, apagar e executar
● java.net.SocketPermission
○ Ações: aceitar, escutar, conectar e resolver
● java.util.PropertyPermission
○ Ações: ler e escrever
● java.lang.RuntimePermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.awt.AWTPermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.net.NetPermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.security.SecurityPermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.io.SerializablePermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.lang.reflect.ReflectPermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
● java.security.AllPermission
○ Ações: nenhuma, as classes tem ou não permissão para executar uma operação em
tempo execução
Segurança 7
JEDITM
São localizações que indicam de onde as classes serão carregadas. Pode incluir a URL das classes
bem como quem as assinou. É importante notar que ambos são opcionais. A URL da classe pode
ser o um arquivo de URL do sistema ou da rede.
Pode-se associar permissões baseadas na URL a partir da qual foi carregada ou baseada somente
em quem a assinou. Também é possível assinalar permissões de uma combinação de URL e do
assinador. Esta URL dentro do código fonte também é conhecida como codebase.
Code Sources são uma combinação de codebase e assinante. O campo assinante deve coincidir
com o pseudônimo armazenado na chave keystore. Codebases podem ser quaisquer URL válidas,
e como tais, usam barras "/" como separadores de níveis ou diretórios, mesmo que estejam no
sistema de arquivos local.
O final da URL também tem um impacto na definição. Existem quatro casos:
● URL especifica um arquivo .jar – Somente classes dentro daquele arquivo jar são parte do
codebase
● URL termina com uma barra – Somente arquivos .class no diretório são parte do codebase.
Arquivos não estão inclusos
● URL termina com um asterisco – Todos os arquivos .jar e .class no diretório pertencem ao
codebase
● URL termina com um hífen – Todos os arquivos .jar e .class pertencem ao diretório e todos
os subdiretórios pertencem ao codebase
Note que a estrutura de um diretório não é afetada pelo nome do pacote. Por exemplo, para
carregar a classe up.jedi.Login, não é necessário adicionar o diretório acima onde aparece. Por
exemplo, jedi.upd.edu.ph/, e não jedi.upd.edu.ph/up/jedi/.
Segurança 8
JEDITM
5. Domínios de Proteção
Um domínio de proteção é uma associação de permissões com um Code Source em particular.
Domínios de proteção são o conceito básico para uma Sandbox padrão. Informam como o código
carregado de um local em particular como por exemplo, www.sun.com tem a permissão de
escrever em um arquivo e possui o código assinado por quem tem a permissão para iniciar os
trabalhos de impressão.
5.1. KeyStores
Classes Java podem ser assinadas através do uso de certificados digitais juntamente com a
ferramenta jarsigner. Deste modo, temos a possibilidade de conceder permissões para código
assinado por uma determinada entidade.
O código assinado pode ser manipulado através do uso de uma keystore. A keystore é
basicamente onde é armazenado os certificados, de chave pública, que são usados para assinar o
código utilizado. Antes de executar qualquer código assinado, a chave pública é usada para
assiná-la dever ser obtida e então instalada na keystore do sistema. Alguns sistemas, como os
navegadores, aceitam certificados de chave pública quando os arquivos são carregados pela
primeira vez, sendo então assinados mas, usualmente, estes certificados devem ser
descarregados e instalados antes de se executar um aplicativo.
Keystores são administradas através do utilitário keytool fornecido na distribuição padrão Java.
Por padrão, a keystore é localizada em um arquivo chamado .keystore encontrada no diretório
home do usuário. Quando um certificado de chave pública é instalado em uma keystore, o
administrador insere no certificado um nome ou um apelido que é usado para uma futura
referência. Este apelido é usado na administração dos vários certificados e também é o nome
usado no arquivo de política de segurança.
Segurança 9
JEDITM
Que se informa o nome do arquivo de política de segurança que se deseja editar. Este argumento
não define o carregamento do arquivo $HOME/.java.policy no diretório do usuário; se este arquivo
não existir, por padrão, nenhum arquivo é carregado.
Segurança 10
JEDITM
policy.url.n = url
Onde n é um número. Por exemplo, os arquivos de política padrão são definidos como:
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy
Segurança 11
Módulo 7
Segurança
Lição 3
Gerenciadores de Segurança
1. Objetivos
De acordo com a perspectiva das API’s do Java, existe um gerenciador de segurança que se
encontra no controle do policiamento da segurança de uma aplicação. Existem também outras
facetas que são de importância para a segurança em Java, mas a regra utilizada pelo gerenciador
é vital na definição das rotinas de segurança que serão ativadas na execução de um programa em
particular.
Numa visão simplificada, o gerenciador de segurança é responsável por determinar a maioria dos
argumentos da Java Sandbox. Isto significa que todas as regras estabelecidas pelo gerenciador
determinam quais operações são permitidas ou não. Se um aplicativo Java tenta abrir um
arquivo, deseja se conectar com outra máquina em uma rede, ou se deseja alterar o estado de
um serviço, o gerenciador de segurança decide se autoriza tais operações ou as rejeita,
baseando-se nas regras definidas.
Ao final desta lição, o estudante será capaz de:
• Compreender a arquitetura dos Gerenciadores de Segurança
• Conhecer os métodos dos Gerenciadores de Segurança
• Construir um Gerenciador de Segurança customizado
Segurança 4
JEDITM
Outro método que pode ser utilizado é o setSecurityManager. Permite que o usuário configure
manualmente o gerenciador de segurança para o sistema. Como por exemplo:
System.getSecurityManager(new SecurityManagerImpl());
Segurança 5
JEDITM
A seguir, temos os métodos utilizados pelo gerenciador de segurança para rastrear o acesso a
arquivos.
public void checkRead(FileDescriptor fd)
public void checkRead(String file)
public void checkRead(String file, Object context)
Estes métodos verificam se o programa tem permissão para ler um arquivo. O primeiro destes
métodos obtém sucesso quando a proteção de domínio permite, em tempo de execução, acesso
ao arquivo por meio do FileDescriptor. Os próximos métodos obtém sucesso se a proteção de
domínio possui permissão de leitura em um arquivo com um nome que coincida ao argumento
contendo o nome do arquivo informado e a partir de um determinado contexto.
public void checkWrite(FileDescriptor fd)
public void checkWrite(String file)
Este método verifica se o programa tem permissão para eliminar um determinado arquivo. Obtém
sucesso se a proteção de domínio possuir uma permissão de exclusão de um arquivo com um
nome que coincida ao argumento contendo o nome do arquivo informado.
O gerenciador de segurança utiliza os seguintes métodos para verificar o acesso à rede:
public void checkConnect(String host, int port)
public void checkConnect(String host, int port, Object context)
Estes métodos servem para verificar se o programa pode abrir uma conexão com outro
computador em uma porta no computador host. Obtém sucesso se a proteção de domínio possuir
uma conexão via socket com o mesmo nome do host, porta e em determinado contexto.
public void checkListen(int port)
Este método verifica se o programa pode criar um novo servidor em determinada porta. A
proteção de domínio deve possuir uma permissão de monitoramento de socket onde o host seja
localhost, o alcance da porta inclua a porta especificada aberta com a ação de escutar.
public void checkAccept(String host, int port)
Este método verifica se o programa pode aceitar (em um servidor existente) a conexão de um
cliente que possa ser originária de um dado host e porta. A proteção de domínio deve possuir
uma permissão de socket onde o host e a porta sejam iguais aos argumentos passados para o
método.
public void checkMulticast(InetAddress addr)
public void checkMulticast(InetAddress addr, byte ttl)
Este método verifica se o programa pode criar um multicast em um determinado endereço IP.
Segurança 6
JEDITM
Este método verifica se o programa possui permissão para alterar a fábrica de sockets. Quando a
classe Socket é utilizada para criar o socket, recebe um novo socket da fábrica, que fornece um
socket padrão baseando-se no protocolo TCP. Entretanto, um programa poderia instalar uma
fábrica de sockets que fizesse com que todos os sockets fornecidos possuam semânticas
diferentes, tais como sockets que enviam informações sobre o rastreamento de algum dado. A
proteção de domínio deve ter permissão durante a execução de um programa através do método
setFactory.
A distinção realizada sobre as classes confiáveis e não confiáveis é baseada no local de onde a
classe é carregada. Como resultado, o carregador de classe toma uma importante decisão, desde
que o gerenciador de segurança esteja configurado para questionar ao carregador onde a classe
se encontra. O carregador é também responsável por certificar de que determinadas classes são
assinadas. Tipicamente, uma classe não confiável não pode ser permitida para realizar uma
criação de um carregador de classes. Este método é chamado somente pelo construtor da classe
através do Class Loader: se for possível realizar a criação de um objeto desta classe (ou obter
previamente a referência de um objeto) para fazer uso deste. Para obter sucesso, a proteção de
domínio deve possuir permissão durante a execução.
public void checkExec(String cmd)
Este método é utilizado para prevenir a execução arbitrária de comandos de sistema vindos de
classes não confiáveis. Classes não confiáveis não podem, por exemplo, executar um processo
distinto que remova todos os arquivos de seu disco rígido. Este processo necessitaria não ser
escrito em Java, é claro, pois, caso contrário, não existiria gerenciador de segurança que pudesse
restringir esta ação. Para obter sucesso, a proteção de domínio deve possuir uma permissão de
execução com o mesmo nome do comando dado através do argumento passado.
public void checkPropertiesAccess( )
public void checkPropertyAccess(String key)
A JVM possui um conjunto de propriedades globais (de sistema) que contém informações sobre o
usuário e a máquina: nome do usuário, diretório raiz, entre outras informações. Classes não
confiáveis têm, geralmente, acesso negado a algumas dessas informações em uma tentativa de
limitar a quantidade a informações (espionagem) que uma applet pode fazer. Normalmente, estes
métodos apenas previnem acesso às propriedades de sistema; uma classe não confiável é livre
para enviar suas próprias propriedades e compartilhá-las com outras classes se assim desejar.
Para ter sucesso, o domínio corrente de proteção deve carregar uma permissão de propriedade.
Se uma chave é especificada, então o nome da permissão de propriedade deve incluir
determinada chave e ter definida uma ação de leitura. De outra forma, o nome da permissão de
propriedade deve ser “*” e a ação deve ser de leitura e escrita.
public boolean checkTopLevelWindow(Object window)
Classes Java, sem levar em consideração se são confiáveis ou não confiáveis, normalmente tem
permissão para criar janelas top-level no desktop do usuário. Entretanto, há uma preocupação
acerca de uma classe não confiável apresentar uma janela que se pareça exatamente com outra
aplicação no desktop do usuário e, dessa forma, guiar o usuário a fazer algum procedimento que
não deveria ser realizado. Por exemplo, uma applet pode apresentar uma janela que se pareça
exatamente com uma sessão telnet e então capturar a senha do usuário quando este responder a
um pedido de informação da senha. Por essa razão, janelas top-level são criadas por classes não
Segurança 7
JEDITM
A API reflection é poderosa o bastante a tal ponto que, por inspeção, um programa pode
determinar atributos de instância e métodos particulares de uma classe (embora não possa
realmente acessar esses atributos ou chamar esses métodos). Todas as classes tem a permissão
de inspecionar qualquer outra classe e descobrir seus atributos e métodos públicos. Todas as
classes carregadas pelo mesmo Class Loader tem a permissão de inspecionar todos os atributos e
métodos de outras. De outra forma, o domínio de proteção corrente deve carregar uma permissão
em tempo de execução com o nome de accessDeclaredMembers. A implementação padrão desse
método é bastante frágil. Diferente de todos os outros métodos que foram vistos, é um erro lógico
sobrescrever esse método e depois chamar super.checkMemberAccess( ).
public void checkSecurityAccess(String action)
O pacote de segurança depende desse método no gerenciador de segurança para decidir quais
classes podem executar certas operações relacionadas a segurança. Como um exemplo, antes de
uma classe ter permissão de uma chave particular, esse método é chamado com um argumento
do tipo String indicando que uma chave particular está sendo lida. Como é esperado, apenas
classes confiáveis têm permissão de executar quaisquer operações relacionadas a segurança.
public void checkPackageAccess(String pkg)
public void checkPackageDefinition(String pkg)
Esses métodos são usados em conjunto com um Class Loader. Quando um Class Loader é
requisitado a carregar uma determinada classe com um nome de pacote em particular, primeiro
perguntará ao gerenciador de segurança se é permitido realizar esta ação através da chamada ao
método checkPackageAccess(). Isso permite ao gerenciador de segurança ter certeza que uma
classe não autorizada não está tentando usar classes específicas da aplicação que ela não deveria
ter conhecimento. Similarmente, quando um Class Loader realmente cria uma classe em um
pacote em particular, pergunta ao gerenciador de segurança se é permitido fazê-lo, através da
chamada ao método checkPackageDefinition(). Isso permite ao gerenciador de segurança prevenir
que uma classe não confiável carregue uma classe a partir da rede e armazene-a dentro, por
exemplo, do pacote java.lang.
Repare na distinção entre esses dois métodos: no caso do método checkPackageAccess(), a
pergunta é se o Class Loader pode referenciar a classe como um todo, isto é, se podemos chamar
uma classe no pacote da Sun. No método checkPackageDefinition(), os bytes da classe foram
carregados e o gerenciador de segurança está sendo questionado se eles podem pertencer a um
pacote em particular. Por padrão, o método checkPackageDefinition() nunca é chamado e o
método checkPackageAccess() é chamado apenas para pacotes listados na propriedade
package.access dentro do arquivo java.security. Para ter sucesso, o domínio de proteção corrente
deve ter uma permissão em tempo de execução com o nome de defineClassInPackage.+<pkg>
ou accessClassInPackage.+<pkg>.
Segurança 8
JEDITM
O gerenciador de segurança customizado para este programa solicita que o usuário entre com sua
senha antes de permitir um FileIO para escrever um texto em um arquivo ou ler um texto a partir
de um arquivo.
Import java.io.*;
if(!accessOK()){
super.checkRead(filename);
throw new SecurityException("Sem Chance!");
} else {
FilePermission perm = new FilePermission(
File.separatorChar + "home" +
Segurança 9
JEDITM
File.separatorChar + "monicap" +
File.separatorChar + "text2.txt", "read");
checkPermission(perm);
}
}
}
public void checkWrite(String filename) {
if((filename.equals(File.separatorChar + "home" +
File.separatorChar + "monicap" +
File.separatorChar + "text.txt"))){
if(!accessOK()){
super.checkWrite(filename);
throw new SecurityException("Sem chance!");
} else {
FilePermission perm = new FilePermission(
File.separatorChar + "home" +
File.separatorChar + "monicap" +
File.separatorChar + "text.txt" ,
"write");
checkPermission(perm);
}
}
}
}
Uma vez que as regras de segurança foram estabelecidas e a classe gerenciadora de segurança
que irá impor as regras feitas, deste modo, pode-se agora instalar o gerenciador de segurança em
Segurança 10
JEDITM
uma aplicação. Isso é feito usando-se o método estático setSecurityManager da classe System,
como apresentado a seguir.
try {
System.setSecurityManager(new SecurityManagerImpl());
} catch (SecurityException se) {
System.err.println(“Gerenciador de Segurança já está instalado!!!”);
}
Deve-se notar que um gerenciador de segurança pode ser instalado, mas a permissão para
instalar um gerenciador diferente pode ser concedida. Assumindo que uma permissão não seja
concedida, então uma SecurityException será disparada. Depois que o gerenciador de segurança
foi instalado, o código da aplicação não precisa referenciar a este diretamente. Isso porque a JVM
irá fazer todas as referências ao gerenciador depois da instalação, a menos que uma chamada
explícita a um método seja necessária.
Segurança 11
Módulo 7
Segurança
Lição 4
Segurança em Java
1. Objetivos
Segurança na plataforma Java foi inicialmente concebida para proteger as informações em um
computador de serem acessadas ou modificadas (incluindo alterações que um programa malicioso
poderia introduzir).
Essas ações já foram implementadas no modelo Java de segurança desde a versão 1.0. O serviço
de autenticação foi adicionado em seguida, na versão 1.1, seguido do modelo de criptografia
disponível na versão 2.0 (como uma extensão) e auditoria que pode ser acrescentada por
qualquer programa Java, fornecendo uma auditoria completa de gerenciamento de segurança.
Provavelmente novos serão serão adicionados no futuro.
Ao final desta lição, o estudante será capaz de:
• Descriminar o que é Segurança
• Compreender o modelo de Segurança implementado em Java
• Obter mais dados sobre a Sandbox
Segurança 4
JEDITM
2. O que é Segurança?
Entende-se por segurança como um conjunto de serviços ou aspectos que fornecem maior
confiança ao uso de um sistema, dentre as quais, destacam-se:
● Proteção contra programas maléficos
Programas não devem ter autorização a executarem processos que podem prejudicar o ambiente
de um usuário, tais como cavalos de Tróia e programas prejudiciais que se replicam, como um
vírus de computador.
● Bloqueio a programas invasivos
Os programas devem ser impedidos de descobrir informações particulares sobre o computador ou
sobre a rede do computador.
● Autenticação
A identidade das partes envolvidas no programa, tanto do autor como a do usuário, devem ser
verificadas.
● Criptografia
Os dados que o programa envia e recebe através da rede, como arquivos ou dados, devem ser
codificados.
● Auditoria
Operações potencialmente sensíveis sempre devem ser autenticadas.
● Bem-definida
Especificações de segurança devem ser seguidas.
● Verificação
Regras de funcionamento devem ser definidas e confirmadas.
● Bem-comportada
Os programas devem ser impedidos de consumir recursos do sistema em demasia: utilização da
CPU por muito tempo, excesso de memória, entre outros
● Padrão de segurança
Os programas devem ser compatíveis com algum padrão de segurança. Pode-se possuir algum
tipo de certificação, ou seja, certificar que determinados procedimentos de segurança são
seguidos. Por exemplo, certificação C2 ou B1 do governo dos Estados Unidos.
Segurança 5
JEDITM
sistema que necessita de uma determinada característica é qualificado como "não seguro", de
acordo com a definição de segurança. A questão é saber se Java possui as características que
atendem às suas necessidades.
Segurança 6
JEDITM
Este modelo de segurança gira em torno da idéia de uma Sandbox (vista em lições anteriores). A
idéia central é que um aplicativo possa ser alojado de forma segura e restrita em um determinado
computador. Pretende proporcionar um ambiente onde este programa possa ser executado
entretanto, controlar sua execução em uma área com certos limites. É possível permitir que este
programa tenha acesso somente a determinados recursos do sistema, em geral, certificar-se de
que o programa estará confinado dentro de uma Sandbox.
A Sandbox é encarregada de proteger um determinado número de recursos. Considere os
recursos típicos de um computador: o usuário da máquina tem acesso, por exemplo, a muitas
coisas, internamente, acesso a memória local (memória RAM) e, externamente, acesso a um
servidor de arquivos ou outras máquinas da rede. Para executar Applets, também devemos ter
acesso ao WEB Services, cujo acesso pode ser feito através da rede particular ou da internet.
Dados trafegam através de todo este modelo, a partir da máquina do usuário, por meio da rede e,
eventualmente, para o disco rígido. Cada um desses recursos deve ser protegido. Essas proteções
da Sandbox formam a base do modelo de segurança do Java, tais como:
● Um programa que possui acesso a memória e a CPU, bem como ao servidor WEB onde foi
carregado. Isso é muitas vezes é um estado padrão para a Sandbox.
● Um programa que possui acesso à CPU, a memória, ao servidor WEB e um conjunto de
recursos específicos (tais como, arquivos locais, máquinas locais, entre outros). Uma
palavra de processamento de programa, por exemplo, pode ter acesso aos documentos do
diretório no sistema de arquivo local, mas não em relação a quaisquer outros arquivos
Segurança 7
JEDITM
● Um programa que possui o acesso a todos os recursos seja qual for a máquina servidora
que se encontra instalado.
Sandbox não é um uniforme padrão para todos os modelos. A expansão de suas fronteiras é
baseada em uma noção de confiança. Em alguns casos, pode-se confiar nos programas Java para
acessar o sistema de arquivos, em outros, pode-se confiar o acesso a apenas parte do sistema de
arquivos, e ainda, em outros casos, não é possível confiar-lhes nenhum acesso ao sistema de
arquivos.
Segurança 8
Módulo 7
Segurança
Lição 5
Classes de Segurança em Java
1. Objetivos
Cada classe Java possui um conjunto de permissões que definem as atividades que a classe está
autorizada a executar. Quando um programa Java tenta executar uma operação delicada, as
permissões são pesquisadas para todas as classes envolvidas: se cada classe possuir a permissão
para realizar determinada operação desejada, então a autorização é concedida. Caso contrário,
uma exceção é lançada e a operação falha.
Ao final desta lição, o estudante será capaz de:
• Conhecer as regras e permissões de segurança
• Entender as classes de regras, de permissão e de acesso
• Aprender detalhes sobre exceções associadas a segurança
• Construir uma classe de permissão
Segurança 4
JEDITM
Segurança 5
JEDITM
Segurança 6
JEDITM
4. Classes de Permissão
Um objeto de permissão representa um recurso do sistema, mas não concede o acesso a este.
Objetos de permissão são construídos e atribuídos ao código baseado na política de segurança em
vigor. Quando um objeto de permissão é concedido para qualquer código fonte, então é atribuída
a permissão para acessar qualquer recurso do sistema que será representado por este objeto.
A classe Permission é uma classe abstrata. Então, uma sub-classe é utilizada para representar
acessos específicos. Analisaremos determinados métodos dentro dessa classe para entendermos
como implementar um usuário.
● public Permission(String name)
Construir um objeto de permissão que representa a permissão desejada.
● public abstract boolean equals(Object o)
Sub-classes de Permission são obrigadas a implementar seus próprios testes de igualdade. Muitas
vezes isso é feito pela comparação do nome (e ações, se apropriado) da permissão.
● public abstract int hashCode()
Sub-classes de Permission são obrigadas a implementar seus próprios códigos hash. Para o
controlador de acesso funcionar corretamente, o código hash para um dado objeto de permissão
nunca deve mudar durante a execução da máquina virtual. Além disso, permissões que
comparam a mesma igualdade devem retornar o mesmo código hash.
● public final String getName()
Retornar o nome que foi usado na construção dessa permissão.
● public abstract String getActions()
Retornar uma forma canônica das ações que foram usadas para construir essa permissão, se
existirem.
● public String toString()
A convenção de impressão de uma permissão deve retornar entre parênteses o nome da classe, o
nome da permissão e as ações envolvidas.
● public abstract boolean implies(Permission p)
Esse é um dos métodos chaves da classe Permission. Responsável por determinar se uma classe
que recebeu uma permissão pode receber outra. Também é responsável pela execução dos
curingas correspondentes. Entretanto, este método não não necessita realmente dos curingas; a
permissão de escrita em um objeto determinado num banco de dados provavelmente implicaria
na permissão de ler o objeto.
● public PermissionCollection newPermissionCollection()
Retornar uma coleção de permissões apropriada para instâncias seguras deste tipo de permissão.
Este método retorna null por padrão.
● public void checkGuard(Object o)
Chama o gerente da segurança (definido pelo atributo SecurityManager.checkPermission) para
verificar se a permissão a esta variável foi concedida, gerando um exceção do tipo
SecurityException em caso negativo. Atualmente o parâmetro deste método não é utilizado.
A seguir estão algumas sub-classes de Permission implementadas na API Java:
● java.security.BasicPermission
● java.io.FilePermission
● java.net.NetPermission
● java.net.SocketPermission
● java.util.PropertyPermission
Diferentes classes Permission podem ser agrupadas usando a PermissionCollection ou
Segurança 7
JEDITM
Segurança 8
JEDITM
5. Controle de acesso
O controle de acesso é feito pelas classes ProtectionDomain, AccessController, SecureClassLoader,
e URLClassLoader.
A classe sun.misc.Launcher é implementada pela Sun Microsystems e provê uma classe de
carregamento de extensões para a plataforma Java (jre/lib/ext) e classes de aplicação, tais como
classes na variável CLASSPATH, especificadas usando a variável java.class.path ou através da
opção -cp no comando de compilação Java.
A classe ProtectionDomain encapsula as características de um domínio que inclui um conjunto de
classes e objetos correspondentes que recebem as mesmas permissões.
Uma proteção de domínio é criada, ou vinculada através da SecureClassLoader se uma proteção
de domínio apropriada já existir, usando o método getProtectionDomain. O objeto Policy é usado
para determinar as permissões associadas à proteção de domínio da classe.
O AccessController decide se o acesso aos recursos do sistema é permitido, com base na política
de segurança em vigor. Se for negado, uma exceção do tipo AccessControllException é lançada.
AccessControllException é uma sub-classe de java.lang.SecurityException.
Chamadas devem ser feitas preferivelmente ao método checkPermission do gerente de segurança
instalado ao invés de diretamente a classe AccessController. Isto deve ser feito para:
● Permitir a comparação que será omitida se o gerente de segurança não estiver instalado.
● Possibilitar a possibilidade que um gerente de segurança ao ser instalado execute
verificações adicionais que não são fáceis de serem capturadas com objetos tipo
Permission.
AccessController é uma classe que contém apenas métodos estáticos.
Segurança 9
JEDITM
6. Exceções
Existem dois tipos de exceções associadas ao pacote de segurança: java.lang.SecurityException e
java.security.GeneralSecurityException. A classe java.security.GeneralSecurityException é uma
nova classe de exceção que foi adicionada ao Java 2. Esta é uma subclasse de
java.lang.Exception. Todas as exceções de segurança são sub-classes diretas desses dois tipos,
exceto ProviderException e InvalidParameterException.
A classe SecurityException e suas sub-classes java.security.AccessControlException e
java.security.cert.CertificateException são exceções em tempo de execução que são lançadas
quando ocorre uma violação de segurança (lançada por uma falha em uma chamada de
checkPermission), como na tentativa de acesso a um arquivo quando esta permissão não é
autorizada. Normalmente, desenvolvedores de aplicações não capturam essas exceções.
Em geral, classes de exceção de segurança não são relacionadas a permissões de classes. Esta
exceção é lançada a partir de várias classes de criptografia em que podem ocorrer erros. Por
exemplo, a exceção NoSuchAlgorithmicException indica que um código está solicitando um
algoritmo que não foi instalado na JVM. Esta exceção é mais similar a FileNotFoundException do
que a SecurityException.
Todas as exceções que não foram mencionadas no pacote java.security são sub-classes de
GeneralSecurityException. São as seguintes:
● InvalidAlgorithmParameterException
● KeyException
● KeyManagementException (estende KeyException)
● KeyStoreException
● InvalidKeyException
● DigestException
● NoSuchAlgorithmException
● SignatureException
● UnrecoverableKeyException
Segurança 10
JEDITM
Segurança 11
JEDITM
Os atributos de classe são obrigados a manter a informação sobre as ações realizadas. Apesar da
superclasse fazer referência a estas ações, providências não são tomadas para que sejam
armazenadas ou processadas. Essa lógica é fornecida no método parse(). Neste método, adota-se
a convenção de se obter uma String de ação tratada como uma lista de ações que são separadas
por vírgulas ou espaços em branco.
Como solicitado, implementamos os métodos equals() e hashCode(). Consideramos objetos iguais
se seus nomes e suas ações são iguais, e construímos um código hash adequado.
A implementação do método getActions() é típica. Somos obrigados a retornar a mesma String de
ação para um objeto de permissão que foi construído por uma lista de ações de view e update
como para o que foi construído através da lista. Esta exigência é uma das principais razões pelas
quais as ações são armazenadas com uma máscara, pois permite construir essa String de ação no
formato correto.
Por fim, o método implies() é responsável por determinar como o curinga e outras permissões
implícitas serão manipuladas. Se o nome informado ao construtor do nosso objeto for um
asterisco, então igualaremos este a qualquer outro nome. Quando o método implies() for
chamado por este objeto curinga, o nome sempre corresponderá, pois a máscara da ação tem a
lista completa das ações. Então, a máscara de comparação sempre autorizará a máscara que
testamos na comparação. Se o método implies() for chamado por um objeto diferente, retorna
true se os nomes forem iguais a máscara de um objeto é um subconjunto da máscara alvo.
Perceba que também devemos implementar a lógica de tal forma que a permissão para executar
uma atualização que implique na permissão de executar um aspecto simplesmente pela mudança
na lógica de teste da máscara. Não estamos limitados somente ao uso de curingas
correspondentes no método implies().
Segurança 12
Módulo 7
Segurança
Lição 6
JAAS
1. Objetivos
A tecnologia JAAS – Java Authenthication and Autorization Service (Serviço de Autorização e
Autenticação Java) fornece uma interface para autenticar usuários e conceder autorização
baseada na identidade do usuário ao invés das características da fonte. Com JAAS é possível
restringir um aplicativo baseado na sua localização, no seu autor (quem o assinou) ou no seu
executor.
JAAS disponibiliza um framework através de uma arquitetura plug-in-play. Fornece um conjunto
de classes abstratas e, no momento de sua execução, procura por um provider necessário à
classe. No entanto, esta arquitetura não está acoplada ao framework de segurança. Por isso
iremos nos restringir em consultar as principais classes de JAAS bem como seus detalhes de
funcionamento.
Ao final desta lição, o estudante será capaz de:
• Trabalhar com a tecnologia JAAS
• Identificar as políticas de segurança de JAAS e seus arquivos de configuração
• Conhecer as classes de autenticação e autorização da tecnologia JAAS
• Programar e administrar através da tecnologia JAAS
Segurança 4
JEDITM
Aqui estão as principais classes e interfaces do JAAS que podem ser usadas, estendidas ou
implementadas para tratar autorização e autenticação.
● javax.security.auth.AuthPermission
● javax.security.auth.Policy
● javax.security.auth.Subject
● javax.security.auth.PrivateCredentialPermission
Segurança 5
JEDITM
● javax.security.auth.login.Configuration
● javax.security.auth.login.LoginContext
● javax.security.auth.spi.LoginModule (Interface)
● javax.security.auth.callback.Callback (Interface)
● javax.security.auth.callback.CallbackHandler (Interface)
● javax.security.auth.callback.ChoiceCallback
● javax.security.auth.callback.ConfirmationCallback
● javax.security.auth.callback.LanguageCallback
● javax.security.auth.callback.NameCallback
● javax.security.auth.callback.PasswordCallback
● javax.security.auth.callback.TextInputCallback
● javax.security.auth.callback.TextOutputCallback
Segurança 6
JEDITM
Segurança 7
JEDITM
padrão. A sintaxe é quase a mesma e os tipos de permissões são exatamente iguais. A única
diferença é a possibilidade de especificar o tipo e o nome principal de cada entrada. Isto significa
que a entrada informada especifica os codebases adicionais, além dos principais, e o código das
assinaturas que são utilizados no arquivo de configuração de politicas de segurança.
As entradas neste arquivo aplicam-se a todo código executado pelo método doAs() analisado
anteriormente; mapas deste arquivo principal são associados ao contexto chamando o método
doAs() específico para as permissões.
Cada entrada de concessão inclui uma ou mais “permissões de entrada” e precedida pelas opções
codeBase ou signedBy contendo pares de nome e valor que especificam que este código pode
conceder a permissão. Temos a seguir, o formato básico de uma entrada de concessão:
grant signedBy "signer_names", codeBase "URL" {
permission permission_class_name "target_name", "action",
signedBy "signer_names";
....
permission permission_class_name "target_name", "action",
signedBy "signer_names";
};
O signedBy e codeBase são pares opcionais contendo nome e valor, e a ordem entre estes campos
não é relevante. Um valor signedBy indica o apelido para um certificado fornecido na database de
chaves. Múltiplos apelidos podem ser separados por vírgulas. Se for omitido isso significa
nenhuma assinatura. Não importa qual dos dois códigos é assinado ou não ou por quem.
Um valor de codeBase indica o local do código fonte; permissões serão concedidas com base
nesse local. Um codeBase vazio significa ausência de local; não importa onde o código se origina.
Veja uma amostra uma entrada de concessão do JAAS:
grant
codebase "file:/files/sdo/jaas/actions/"
signedBy "jra"
Principal com.sun.security.auth.SolarisPrincipal "sdo" {
permission java.io.FilePermission "${/}files", "read";
};
Segurança 8
JEDITM
Esta classe é utilizada para descrever os métodos básicos usados para autenticação e permite
desenvolver um aplicativo independente da tecnologia de autenticação envolvida. LoginContext
consulta um arquivo de configuração para determinar quais LoginModule devem ser usados. Estes
são os métodos vitais para esta classe:
public LoginContext(String name)
public LoginContext(String name, CallbackHandler cb)
public LoginContext(String name, Subject s)
public LoginContext(String name, Subject s, CallbackHandler cb)
Estabelecer um contexto pelo qual um usuário pode ser autenticado. As ações que esta
autenticação irá executar são carregadas através de um objeto de configuração. O argumento
name é a identificação utilizada por esse conjunto de ações dentro do objeto de configuração.
public void login()
Informar que o usuário está saindo do sistema, isso invalida o objeto subject. Este método lança
LoginException se a operação de logout falhar.
public Subject getSubject()
Esta interface é implementada por serviços de autenticação. Objetos LoginModule dão suporte às
aplicações para fornecer um determinado tipo de autenticação. Estes são os métodos necessários
para implementar a interface.
public void initialize(Subject s, CallbackHandler ch, Map sharedState, Map
options)
Inicializar o LoginModule. O objeto do tipo Subject representa o usuário que será autenticado; o
módulo de login irá armazenar um ou mais usuários, e talvez utilizará o objeto do tipo
CallbackHandler para obter informações de autenticação diretamente com o usuário. O primeiro
objeto do tipo Map serve para compartilhar informações. O segundo objeto tipo Map contém as
opções carregadas do arquivo de configuração.
public boolean login()
Autenticar o usuário. Informações sobre o usuário podem ser obtidas no ambiente ou utilizando o
CallbackHandler. Este método retorna true caso o usuário obtenha sucesso na autenticação ou
false se a autenticação deve ser ignorada. Se, o usuário não puder ser autenticado este método
deve lançar um LoginException.
public boolean commit()
Segurança 9
JEDITM
Interromper o processo de autenticação. Este método é chamado quando o usuário não pode ser
autenticado, isto é, um módulo exigido falhou ou não conseguiu obter um módulo adicional. O
módulo deve limpar qualquer estado armazenado. Pode lançar um LoginException ao encontrar
um erro.
public boolean logout()
Realizar a saída do usuário. Implica na limpeza de qualquer estado e remoção dos objetos do tipo
Subject e qualquer assunto salvos.
Esta classe é usada para representar um usuário autenticado. Na sua essência, cada usuário é
representado como um array de objetos Principal guardados por esta classe. Existe um array de
objetos, pois cada usuário terá, muito provavelmente, várias características de identificação.
Segurança 10
JEDITM
Recebe um array de objetos tipo Callback e procura a informação desejada em cada um deles. A
aplicação tem liberdade para usar qualquer método para obter a informação adequada. Se a
aplicação não sabe como lidar com um determinado objeto do tipo Callback, deve lançar uma
UnsupportedCallbackException; outros erros podem ser encapsulados como uma IOException.
Segurança 11
JEDITM
Retornar o objeto da classe Policy instalado. Deve ter a AuthPermission denominada getPolicy
para chamar este método. O retorno deste método é um objeto que contém as políticas de
segurança do JAAS em vigor.
public static void setPolicy(Policy policy)
Obter as permissões que devem ser concedidas. São carregadas a partir do código fonte quando
executado pelo objeto tipo Subject informado.
public abstract void refresh()
Esta classe estende a classe BasicPermission e pode ser usada no arquivo de políticas de
segurança geral da tecnologia Java para especificar os tipos de AuthPermission que podem ser
concedidos. O construtor para esta classe é:
public AuthPermission(String name)
Esta classe é usada para proteger o acesso às credenciais (objetos do tipo Credential) particulares
pertencentes a um determinado Subject. Esta classe estende da classe java.security.Permission.
public PrivateCredentialPermission (String name, String actions)
Segurança 12
JEDITM
No exemplo acima, cria-se um objeto de LoginContext usado para estabelecer um contexto pelo
qual um usuário pode ser autenticado. O usuário não é autenticado ao se criar o LoginContext,
isto é feito através do método login() em nosso exemplo. Considerando que a autenticação foi
bem sucedida, a classe Subject é usada para representar um usuário autenticado. A chamada ao
método doAs() irá chamar o método run() do objeto dado, a partir de um determinado subject
(CountFilesAction).
A classe CountFilesAction foi definida como:
import java.io.*;
import java.security.*;
Segurança 13
JEDITM
A entrada CountFiles no primeiro exemplo é comparada com o nome que é passado para o
construtor de contexto do Login. Quando uma entrada é encontrada, cada uma das classes
listadas são chamadas na ordem.
Esta é a forma como estabelecemos o arquivo de política de autorização para nosso exemplo:
grant
Principal com.sun.security.auth.SolarisPrincipal "sdo"
Principal com.sun.security.auth.SolarisNumericGroupPrincipal "45" (
permission java.io.FilePermission "${/} files","read";
);
Esta entrada permitirá que um usuário leia em /files apenas se o nome de login for "sdo" e se o
usuário é um participante do grupo "45".
Segurança 14
Módulo 7
Segurança
Lição 7
Criptografia
1. Objetivos
Criptografia é o estudo da transformação de mensagens claras para torná-las secretas. Nos
tempos modernos, tem se tornado um ramo da teoria da informação como um estudo matemático
da informação e especialmente sua transmissão de um lugar para outro. É uma parte central de
diversos campos da segurança da informação e assuntos relacionados, particularmente,
autenticação e controle de acesso. Um dos objetivos primários da criptografia é esconder o
significado das mensagens, mas, geralmente, sem esconder sua existência. Criptografia também
contribui para a ciência da computação, particularmente em técnicas usadas em computadores e
redes de segurança para garantir funcionalidades, tais como, o controle de acesso a informação
de forma confidencial.
Dois conceitos importantes que devem ser analisados são criptografar e decriptar. Criptografar
é, essencialmente, o processo pelo qual certas informações dentro de arquivos ou programas são
alteradas por algoritmos matemáticos. Uma mensagem criptografada é chamada de ciphertext.
Decriptar, por outro lado, é o processo de voltar um ciphertext a mensagem original. Criptografar
e decriptar são feitas através do uso de uma seqüência de caracteres conhecidos como chaves.
Ao final desta lição, o estudante será capaz de:
• Debater sobre os tipos de algoritmos criptográficos
• Conhecer as classes da arquitetura de criptografia Java
Segurança 4
JEDITM
2. Algoritmos Criptográficos
Esses são os três principais tipos de algoritmos baseados na chave em uso: algoritmos simétricos,
algoritmos assimétricos e algoritmos híbridos.
Algoritmos simétricos são aqueles em que a chave para criptografar e a chave para decriptar
pode ser calculadas isoladamente. Em muitos casos, a chave para criptografar e decriptar são as
mesmas. Algoritmos simétricos requerem que o remetente e o receptor concordem primeiramente
qual será a chave, antes que a mensagem seja enviada. Um dos algoritmos simétricos mais
usados é o Data Encryption Standard ou DES. O padrão DES usa a mesma chave para criptografar
e decriptar baseada no uso do operador "ou exclusivo" (XOR) e em operações de troca de bits. A
maior vantagem desse algoritmo é a sua velocidade e popularidade. Sua maior limitação é que
tem um tamanho de chave relativamente pequeno (56 bits) e a segurança da comunicação
prioriza o requisito de chaves compartilhadas.
O padrão DES foi criado em 1976, e previa-se o seu uso até 1981. Foi descontinuado em 2002,
sendo substituído pelo AES – Advanced Encryption Standard. Há diversas classes Java que
empregam esse algoritmos, a grande vantagem deste novo padrão é o tamanho de sua chave
(256 bits).
Algoritmos assimétricos (também chamados de algoritmos de chave pública) diferem dos
simétricos no fato de que a chave para criptografar é diferente da chave para decriptar. A chave
para criptografar não é secreta e pode ser pública desde que não seja usada para decifrar a
mensagem. A chave para decriptar é usada para decodificar a mensagem, e deve pertencer ao
receptor. A chave para criptografar é chamada de chave pública e a chave para decriptar é
chamada de chave particular. A seguir, temos uma lista dos algoritmos criptográficos assimétricos
mais comuns:
● RSA
RSA (Rivest, Shamir and Adleman), a sigla refere-se aos autores do algoritmo. É um algoritmo
popular usado tanto para criptografar quanto para assinaturas digitais. O algoritmo é baseado na
dificuldade em fatorar, ou seja, encontrar os "Fatores Primos" de um determinado número.
● DSA
DSA (Digital Signature Algorithm), ou seja, Algoritmo de Assinatura Digital. É usado apenas para
assinaturas digitais.
● Diffie-Hellman
Inventado em 1976 por Whitfield Diffie e Martin Hellman, esse algoritmo é usado primariamente
na distribuição de chaves.
● RC2 e RC4
RC (Rivest Cipher) foi desenvolvido pelo conhecido criptógrafo Ron Rivest. O RC2 é uma chave
variável block cipher, com tamanho de 64 bits, enquanto o RC4 é uma chave variável Stream
Cipher.
Algoritmo híbrido mescla a capacidade das duas classes anteriores. Essencialmente, essa classe
de algoritmo usa pares de chaves pública e particular (assimétricas) para autenticar e concordar
com uma chave de sessão. A criptografia é então feita usando um algoritmo simétrico com aquela
chave de sessão.
Segurança 5
JEDITM
Retornar uma String específica do provedor de segurança, contendo seu nome e versão.
Ao criarmos classes para realizar operações de segurança, devemos estender a classe Provider e
registrar essa classe na infra-estrutura de segurança. Apesar da classe Provider ser abstrata,
nenhum de seus métodos são abstratos. Isso significa que para implementar uma classe real, de
uma certa forma, tudo o que será necessário é estender a classe Provider e fornecer um
construtor apropriado. A subclasse deve implementar um construtor, já que não existe nenhum
construtor padrão na classe Provider. Esse construtor deve ser fornecido conforme a seguinte
assinatura:
protected Provider(String name, double version, String info)
Segurança 6
JEDITM
put("KeyPairGenerator.XYZ","xxx.yyy.SomeKeyPairGenerator");
put("KeyFactory.XYZ", "xxx.yyy.SomeKeyFactory");
put("MessageDigest.XYZ", "xxx.yyy.SomeMessageDigest");
put("Signature.XYZwithSHA", "xxx.yyy.SomeSignature");
put("Cipher.XOR", "xxx.yyy.SomeXORCipher");
put("KeyManagerFactory.XYZ", "xxx.yyy.SomeSSLKeyManagerFactory");
}
}
É necessário que o provedor de segurança mapeie o nome do engine e do algoritmo com o nome
da classe que implemente essa determinada operação.
Essa classe é definida como final, contém métodos estáticos e o construtor private, portanto,
nunca poderá ser estendida ou gerar objetos. Estes são os métodos desta classe:
public static int addProvider(Provider provider)
Remover um determinado provedor de segurança através do seu nome passado como argumento.
public static Provider[] getProviders()
Retornar uma cópia da lista de provedores de segurança que são controlados pela classe. Observe
que essa é apenas uma cópia da lista, retirar ou reordenar seus elementos não causa nenhum
efeito na classe Security.
public static Provider getProvider(String name)
Obter a propriedade da classe Security com uma determinada chave. Essas propriedades contidas
na classe Security foram lidas do arquivo java.security. O arquivo java.security tem várias outras
propriedades que também podem ser recuperadas por esse método.
public static void setProperty(String property, String value)
Estabelecer uma dada propriedade para um determinado valor passado como argumento do
método.
public static String getAlgorithmProperty(String algName, String propName)
Segurança 7
JEDITM
import java.security.*;
import java.util.*;
As classes de Engine (também conhecidas como Service Provider Interfaces ou SPI) são
desenvolvidas para que os usuários possam empregar a terça parte dos Security Providers.
Fornecem funcionalidade para calcular uma message digest de um dado especificado, assinar,
verificar as assinaturas digitais e gerar pares de chaves (pública e particular). As classes Engine
incluem as seguintes classes:
java.security.MessageDigest
Segurança 8
Módulo 7
Segurança
Lição 8
Class Loaders
1. Objetivos
Class Loader é o mecanismo pelo qual arquivos que contenham bytecodes Java são lidos na JVM e
convertidos em definições de classes. Em qualquer situação, um ou mais Class Loaders estão
prontamente disponíveis, pois o Class Loader do sistema (ou o Class Loader primordial ou o Null
Class Loader) existe por padrão. Classes de sistema (são aquelas classes que residem no núcleo
da API Java) são carregadas pelo Class Loader de sistema, ou, possivelmente, em alguma
instância ou subclasse de Class Loader.
Ao final desta lição, o estudante será capaz de:
• Ter uma visão geral de funcionamento do Class Loader
• Compreender as considerações de segurança sobre o Class Loader
• Entender as classes do Class Loader
Segurança 4
JEDITM
Segurança 5
JEDITM
Essa hierarquia seria a chave para compreender o que acontece quando uma classe é carregada.
Uma classe pode ser carregada através de uma das seguintes maneiras:
1) Chamando, explicitamente, o método loadClass() de um carregador de classes
2) Chamando o método Class.forName()
3) Carregando implicitamente uma classe quando é referenciada por uma classe já carregada
Na primeira forma, o Class Loader é o objeto cujo o método loadClass() é chamado. Na segunda
forma, ou o Class Loader de sistema ou outro filho deste que carregou a classe através do método
estático Class.forName() será passado para o método (Class.forName()). Na terceira forma, o
Class Loader que carregou a classe de origem será usada para carregar a classe referenciada.
Class Loaders são responsáveis por solicitar que as instâncias superiores carreguem uma classe.
No entanto, se essa operação falhar, o Class Loader tentará definir esta classe. O efeito é que as
classes de sistema serão sempre carregadas pelo Class Loader de sistema, classes no caminho da
classe serão sempre carregadas pelo Class Loader que conhece o caminho da classe e, em geral,
uma classe será carregada pela classe mais antiga na sua hierarquia que sabe onde encontrar
outras classes.
Segurança 6
JEDITM
Quando visitamos uma determinada página (www.site1.com), e carregamos uma applet que
utiliza uma classe chamada SomeClass (sem o nome do pacote). Depois seguimos para outra
página (www.site2.com) e carregamos outra aplicação que utiliza outra classe chamada
SomeClass (também sem o nome do pacote), sabemos que estas são duas classes diferentes,
contudo possuem o mesmo nome totalmente qualificado. Como a JVM conseguirá distinguir estas
duas classes?
Quando uma classe é carregada por um Class Loader é armazenada em uma referência interna a
este Class Loader. Quando a máquina virtual precisa acessar uma determinada classe, a JVM pede
esta ao Class Loader apropriado. Note que o Class Loader usado para carregar uma classe
também faz parte do nome da mesma classe. Portanto, se dois Class Loaders carregam classes
que têm idênticos nomes de pacote e de classe, estas ainda serão tratadas como classes
diferentes.
Segurança 7
JEDITM
if (cl != null) {
url = cl.getResource("someImage.gif");
} else {
url = ClassLoader.getSystemResource("someImage.gif");
}
Outro método que pode ser usado para carregar recursos é o getResourceAsStream(), ou o
método getSystemResourceAsStream(). Este método acessa o recurso como um stream. Por
exemplo:
ClassLoader cl = this.getClass().getClassLoader();
InputStream in = null;
if (cl != null) {
in = cl.getResourceAsStream("someImage.gif");
} else {
in = ClassLoader.getSystemResourceAsStream("someImage.gif");
}
Também pode-se usar o método findResource() para encontrar um recurso especificado pelo
nome. Além disso, pode-se usar os métodos findResources() e getResources() que retornam
uma enumeração de todas as URLs que representam recursos através do nome.
No momento da nomeação de um recurso, os métodos getResource() devem refletir à nomeação
do pacote. Por exemplo, se um arquivo SomeResource.txt está dentro do pacote innerPackage
que, por sua vez, está dentro de outra pasta somePackage, então o recurso é referenciado como
somePackage.innerPackage.SomeResource.txt.
Segurança 8
JEDITM
Segurança 9
JEDITM
Segurança 10
JEDITM
Esta classe transforma uma série de bytecodes em uma definição de classe. Não define a forma
como os bytecodes serão obtidos, mas prevê todas as outras funcionalidades necessárias para
criar a definição da classe.
A classe preferida para ser usada como base para um carregador de classes é a classe
java.security.SecureClassLoader, definida como:
public class SecureClassLoader extends ClassLoader
Esta classe transforma uma série de bytecodes em uma definição de classe. Esta classe
acrescenta funcionalidade seguras à classe ClassLoader, mas não define como os bytecodes serão
obtidos. Deve-se criar uma subclasse desta.
Há uma terceira classe nesta categoria, a classe java.net.URL Class Loader, definida como:
public class URL Class Loader extends SecureClassLoader
Esta classe carrega classes de maneira segura pela obtenção dos bytecodes em um conjunto de
URLs. Se as classes forem carregadas através do sistema de arquivos ou a partir de um servidor
HTTP, a classe URL Class Loader oferece uma definição completa de um Class Loader. Além disso,
é possível substituir alguns de seus métodos ou modificar a política de segurança de classes que
define.
Pela implementação de um Class Loader, podemos estender as permissões que são concedidas
através de arquivos de política de segurança, bem como utilizar certos recursos de segurança
opcionais do Class Loader. Antes de criar um exemplo para implementar um Class Loader,
veremos primeiro certos métodos essenciais na sua utilização e implementação:
public Class loadClass(String name)
Carregar uma classe identificada pelo argumento passado. Uma ClassNotFoundException pode ser
lançada se a classe não for encontrada.
protected Class findClass(String name)
Carregar uma classe especificada pelo argumento passado. O nome será completamente
qualificado do pacote da classe (por exemplo, java.lang.String).
protected final Class defineClass(
String name,
byte[] b,
int off,
int len) throws ClassFormatError
protected final Class defineClass(
String name,
byte[] b,
int off,
int len,
ProtectionDomain protectionDomain) throws ClassFormatError
protected final Class defineClass(
String name, byte[] b,
int off,
int len,
CodeSource cs) throws ClassFormatError
Criar uma classe com base em bytecodes do array informado. A proteção do domínio associado à
classe muda com base no método que está sendo utilizado.
Segurança 11
JEDITM
Etapa 2: Se houver uma classe previamente definida, o método loadClass() de Class Loader
realiza esta operação. Se o Class Loader já carregou esta classe, então, encontra um objeto da
classe previamente definido e retorna este.
Esta é a razão pela qual o método super.loadClass() é chamado no método override.
Etapa 3: Se não houver uma classe previamente definida, o Class Loader consulta a classe que
este estendeu, para determinar como carregar a classe. Isto é feito através de uma operação
recursiva. Deste modo, o Class Loader de sistema será chamado antes para carregar a classe.
No nosso exemplo, o carregamento da classe é feito de forma diferente para a superclasse. O
método loadClass() da classe ClassLoader realiza esta operação.
Etapa 4: Consultar o Security Manager para saber se o programa está autorizado a criar a classe
em questão. Este também é um passo opcional.
No exemplo mostrado, chamamos o método checkPackageDefinition(). Para tanto, deve-se fazer
um override do método findClass():
protected Class findClass(final String name) throws ClassNotFoundException {
// Primeiro verifica se tem permissão para acessar o pacote
SecurityManager sm = System.getSecurityManager( );
if (sm != null) {
int i = name.lastIndexOf('.');
if (i != −1) {
sm.checkPackageDefinition(name.substring(0, i));
}
}
return super.findClass(name);
}
Etapa 5: Ler o arquivo da classe através de um array de bytes. Isso ocorre no método
findClass().
O URL Class Loader realiza esta operação através da consulta dos URLs que foram passados para
o seu construtor. Se for preciso ajustar a forma de leitura dos bytes da classe, deve-se usar a
classe SecureClassLoader.
Segurança 12
JEDITM
Segurança 13
Módulo 7
Segurança
Lição 9
Message Digest
1. Objetivos
Uma message digest, também conhecida como 'message fingerprint' ou 'hash seguro' é uma
String de tamanho fixo que é o resultado da passagem dos dados por um algoritmo chamado
hash – também conhecido por espalhamento, sentido único ou de mão única. A message digest é
produzida através de uma função hash. A função é chamada de sentido único, uma vez que é
impossível que a mensagem original seja extraída da message digest.
A função hash ideal é aquela que nunca produz uma mesma message digest para as possíveis
Strings de entrada. Porém, essa 'perfeição teórica' de uma mensagem de entrada (sem tamanho
fixo) é impossível com o tamanho curto e fixo para a message digest de saída. Para isso seria
necessário que a message digest tivesse o mesmo tamanho da mensagem de entrada. Isto
significa que podemos ter uma String qualquer de entrada e produzir uma message digest curta
de tamanho fixo, mas esta mensagem não necessariamente corresponde SOMENTE à mensagem
que o gerou. Se uma determinada message digest tenha for gerada a partir de duas mensagens
de entrada diferente, teremos o que é chamado de colisão. (para maiores informações acesse
http://www.cits.rub.de/MD5Collisions/)
Uma message digest é uma assinatura digital compacta de um fluxo de dados qualquer. Uma boa
implementação de uma função hash deve produzir uma grande quantidade de mensagens
diferentes, mesmo para uma pequena mudança na string de entrada, como por exemplo a
alteração de um caractere da String de entrada de minúsculo para maiúsculo.
Ao final desta lição, o estudante será capaz de:
• Conhecer os principais algoritmos de Message Digest
• Identificar os principais usos da utilização de Message Digest
• Empregar a classe MessageDigest em um aplicativo
Segurança 4
JEDITM
Nome Algoritmo
Produz uma mensagem com 20 bytes (40 dígitos Hex); aplicável a documentos com menos
SHA-1
de 264 bits.
Produz uma mensagem com 32 bytes (64 dígitos Hex); aplicável a documentos com menos
SHA-256
de 264 bits.
Produz uma mensagem de 48 bytes (96 dígitos Hex); aplicável a documentos com menos
SHA-384
de 2128 bits.
Produz uma mensagem de 64 bytes (128 dígitos Hex); aplicável a documentos com menos
SHA-512
de 2128 bits.
MD5: cf93f2442e8e183d865d7fa9c251aa41
SHA-1: 331cc7479ab3571c96bf1c6ad30738ca0507d386
Ou seja, quando passamos a mensagem para o método hash produzimos uma message digest de
tamanho fixo, dependendo do algoritmo utilizado.
MD5: 6da66136e4d1d0af49d633edb7b53c12
SHA-1: f56f6f92804dfb8935b2185fc520044bba4c8b2a
Como podemos ver, duas simples alterações de caracteres, de '**' para 'ro', produziram grandes
Segurança 5
JEDITM
MD5: 79f131c274a82294177fbad8b977a707
SHA-1: f6e26ec898b69e563dd2bf2245ed12fb64780e30
Para comparar, lado a lado, as message digest geradas, veja a tabela abaixo.
Segurança 6
JEDITM
3. Aplicações comuns
Hashing como sabemos é utilizado em muitas aplicações. Isto inclui aumento de desempenho,
autenticação e verificação de dados.
Aumento de desempenho
A tabela hash utiliza um método hash que indexa a chave dentro da posição correta na tabela
hash. Veja a lição 10 do Módulo 3 – Estruturas de Dados para mais informações sobre
Tabelas Hash e técnicas de hashing.
Autenticação
Senhas podem ser guardadas através do uso de métodos hash. No linux, senhas de usuários são
armazenadas em um arquivo chamado /etc/password, se o shadow não estiver habilitado. Para
guardar senhas reais do usuário, elas são criptografadas através das chaves hash.
root:x:0:1:Super-User:/:/sbin/sh
ozzie:6k/7KCFRPNVXg:508:10:& Ozzie:/usr2/ozzie:/bin/csh
Para provar que uma assinatura pertence a um documento, a assinatura digital é utilizada para
decriptar a chave pública do signatário. Isto resulta na função hash do documento. Se a chave
hash decriptada combinar com a chave hash da mensagem original, então o proprietário da chave
é o signatário do documento.
Segurança 7
JEDITM
Calcula-se então o checksum de verificação do arquivo baixado. Sistemas baseados no Linux têm
uma ferramenta para cálculo do checksum MD5 de um arquivo. Sistemas baseados no Windows
podem baixar ferramentas para o cálculo do checksum MD5 de terceiros.
Segurança 8
JEDITM
4. A classe MessageDigest
Em Java, message digests são armazenadas em arrays de bytes que são calculados utilizando a
classe java.security.MessageDigest.
java.lang.Object
java.security.MessageDigestSpi
java.security.MessageDigest
Construtor
protected MessageDigest(String algorithm)
Métodos
Object clone()
Retorna um clone se a implementação for clonable.
byte[] digest()
Completa o cálculo hash através da execução de operações finais como padding.
byte[] digest(byte[] input)
Executa uma atualização final sobre o resumo utilizando um vetor de bytes que
completa o cálculo.
int digest(byte[] buf, int offset, int len)
Segurança 9
JEDITM
Segurança 10
JEDITM
Segurança 11
JEDITM
ou
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
4. Gerar o sumário
Alguns algoritmos requerem que um padding seja adicionado a message digest. O método
digest() cria automaticamente o padding requerido pelo algoritmo para que não seja necessário
se preocupar com isso. O método digest() retorna um array de bytes que contém o checksum ou
a message digest.
byte[] digest = md5.digest();
Segurança 12
JEDITM
// Inserir 0xFF para adicionar um zero condutor para cada elemento do array
md5Hex.append(Integer.toHexString(0xFF & digest[i]));
}
6. Finalizar
Imprimir a message digest, salve-o em um arquivo e insira-o no banco de dados.
System.out.println(md5Hex.toString());
A message digest pode estar qualquer um dos 2 status, 'initialized'(iniciado) ou 'in progress'(em
progresso). Uma vez obtida uma instância do MessageDigest, esse objeto inicia como 'initialized'.
Os dados são processados pela message digest, utilizando qualquer um dos métodos update().
Uma vez chamado este método, o status da message digest muda para 'in progress'. O método
reset() pode se chamado a qualquer momento para reiniciar a message digest, revertendo seu
status para 'initialized'.
Uma vez que todos os dados foram processados, um dos métodos digest() deve ser chamado para
completar o processo da junção. O método digest() pode ser chamado apenas uma vez para um
dado número de alterações. Depois que o método digest() foi chamado, o objeto MessageDigest é
reiniciado para seu status initialized.
Exemplo:
O programa a seguir processa a união de assinaturas do MD5 de um dado arquivo. Para utilizar o
programa, utilize o nome do arquivo como um argumento. Esse arquivo será lido e o sumário da
mensagem MD5 correspondente será exibido.
/* MD5.java */
package jedi.security.messagedigest.MD5;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Segurança 13
JEDITM
Segurança 14
JEDITM
6. Exercícios
6.1. Aplicações do Mundo Real
1. Vamos supor uma tabela no banco de dados de usuários que podem acessar seu sistema.
O sistema foi implementado de maneira que as senhas dos usuários são armazenadas
como assinaturas de hash SHA-1. Recebemos uma solicitação de mudança para
implementar uma funcionalidade de modificação da senha. Nos requisitos de negócio
constam:
a) Solicitar a senha atual e a nova senha
b) Se a senha atual corresponde à senha do banco de dados, proceder à mudança da
senha atual para a nova senha
2. Recentemente foi feito o download de um arquivo da internet. Não existe a certeza se o
arquivo foi corrompido ou modificado durante a transmissão. O hash MD5 do arquivo é
fornecido no site. Desconhecendo a existência alguns geradores MD5 de código livre,
devemos criar nossa própria aplicação Java para calcular o checksum MD5.
Segurança 15
Módulo 7
Segurança
Lição 10
Listas de Controle de Acesso
1. Objetivos
Uma Lista de Controle de Acesso (ACL) é uma estrutura de dados que armazena os recursos
acessados. Uma ACL pode ser analisada de como uma estrutura de dados com múltiplos acessos.
Cada acesso ACL é um conjunto de permissões associadas a um solicitante ou grupo de
solicitantes. Além disso, cada ACL contém um sinal (uma flag), que indica se as permissões
devem ou não ser concedidas ou negadas.
ACL promove uma maneira fácil de criar usuários e grupos, cada um combinado a um conjunto
específico de permissões que controlarão como cada usuário ou grupo irá atuar na aplicação Java.
As Listas de Controle de Acesso respondem à pergunta: “Como autorizar um usuário a acessar
dados de outros usuários ou outros dados, que não são permitidos?”.
Ao final desta lição, o estudante será capaz de:
• Conhecer as características da ACL
• Obter maiores informações sobre Java e as Listas de Controle de Acesso
Segurança 4
JEDITM
Solicitante Referência
Executando Objeto
Monitorada
Solicitante
Fonte dos pedidos. Um solicitante pode representar uma entidade, um usuário, um grupo ou um
processo. Pode ser qualquer coisa que possua identificação e requer acesso a um recurso.
Solicitação
Realiza as operações com os objetos. Podendo, inclusive, ler, escrever ou executar.
Referência monitorada
Guarda cada objeto e avalia cada pedido para os objetos, decidindo se deve ou não conceder
acesso.
Objeto
Recursos tais como: arquivos, dispositivos ou processos.
A referência monitorada baseia sua decisão na apresentação do pedido principal, na operação de
solicitação, bem como nas regras de acesso que controlam quais solicitantes poderão realizar essa
operação sobre o objeto.
Para executar seu trabalho, o monitor necessita de um meio confiável para reconhecer tanto a
origem do pedido, quanto a regra de acesso. A obtenção do código fonte do pedido é conhecido
por "autenticação", interpretar a regra de acesso é chamado "autorização".
Assim, a autenticação responde à pergunta ''Quem disse isso?'', e a autorização responde à
pergunta ''Quem é confiável para acessar isso?''. Normalmente, a regra de acesso é anexada ao
objeto; tal regra é chamada lista de controle de acesso ou ACL. Para cada operação a ACL
especifica um conjunto de indivíduos autorizados e o monitor concede um pedido se o indivíduo
for tão confiável quanto o solicitante que está autorizado a fazer a operação no pedido.
Cada acesso na ACL é especificado como positivo ou negativo. Um acesso positivo significa que
seus recursos devem estar disponíveis quando um usuário ou processo tentar acessá-los. Uma
Segurança 5
JEDITM
Presumindo que o solicitante P pertença ao grupos G1 e G2. A tabela abaixo mostra 5 colunas
com alguns exemplos de permissões dadas a G1, G2 e P. O resultado das permissões concedidas a
P é verificado na última coluna.
Segurança 6
JEDITM
Segurança 7
JEDITM
Em Java, uma ACL é um objeto que implementa a interface java.security.acl.Acl. Cada Acl é uma
lista de objetos AclEntry. Cada AclEntry está associada a um objeto Indivíduo ou Grupo de uma
lista de objetos Permissões. Cada AclEntry também pode ser associada a uma entrada positiva ou
negativa. Uma entrada positiva concede a lista de permissões na entrada do indivíduo, ou grupo,
e uma entrada negativa nega a lista de permissões para o indivíduo ou grupo.
Este pacote contém classes e interfaces usadas durante a execução das Listas de Controle de
Acesso para assegurar ou negar permissões a indivíduos ou grupos de indivíduos.
3.2.1. Interfaces
Interface Acl
Interface representando uma Lista de Controle de Acesso (ACL). Uma ACL é uma estrutura de
dados usada para guardar acessos aos recursos.
Uma ACL pode ser considerada como uma estrutura de dados com múltiplas entradas ACL. Cada
entrada ACL, de interface AclEntry, contém um conjunto de permissões associadas a um
determinado solicitante (Um solicitante representa uma entidade, como um usuário individual ou
de grupo). Além disso, cada entrada ACL é especificada como positiva ou negativa. Caso seja
positiva, as permissões serão concedidos aos respectivos solicitantes. Caso seja negativa, as
permissões serão negadas.
As solicitações ACL em cada ACL devem observar as seguintes regras:
• Cada solicitante pode ter, no máximo, uma entrada ACL positiva e uma negativa. Isto é,
múltiplas entradas ACL, positivas ou negativas, não são permitidas para nenhum indivíduo.
Cada entrada especifica o conjunto de permissões que serão concedidas (se positiva) ou
negadas (se negativas)
• Se não houver entrada de nenhum solicitante, então considera-se que o solicitante tem um
conjunto vazio de permissões
• Se houver uma entrada positiva, que concede uma determinada permissão a um indivíduo
e uma entrada negativa, que nega esta entrada na mesma permissão, o resultado é como
se a permissão nunca fosse concedida ou recusada
• Permissões individuais sempre terão prioridade sobre permissões de grupo ao qual
pertence o indivíduo. Isto é, permissões individuais negativas substituirão os grupos com
permissões positivas. E permissões individuais positivas substituirão as permissões dos
grupos negativos.
Interface AclEntry
Esta é a interface usada para representar uma entrada na Lista de Controle de Acesso (ACL).
Uma ACL pode ser comparada a uma estrutura de dados com múltiplos objetos de entrada ACL.
Cada objeto de entrada ACL contém um conjunto de permissões associadas com um Principal em
particular. (Um Principal representa uma entidade como um usuário individual ou um grupo).
Adicionalmente, cada entrada ACL é especificada como sendo tanto positiva como negativa. Se
positiva, as permissões devem ser concedidas para o principal associado. Se negativa, as
permissões serão negadas. Cada Principal deve ter pelo menos uma entrada positiva e uma
negativa; assim sendo, múltiplas entradas positivas ou negativas não são permitidas para
Segurança 8
JEDITM
qualquer Principal. As entradas ACL por padrão são positivas. Um entrada se torna negativa
apenas se o método setNegativePermissions for chamado.
Interface Group
Essa interface é usada para representar um grupo de Principals. Um Principal representa uma
entidade como um usuário individual ou um empresa.
Observe que Group estende Principal. Então, tanto um Principal ou um Group podem ser passados
como um argumento para métodos que contém um parâmetro Principal. Por exemplo, podemos
adicionar um Principal ou um Group para um objeto Group chamando o método addMember desse
objeto, passando o Principal ou Group.
Interface Owner
Interface para gerenciamento de Owners de Listas de Controle de Acesso ou configurações ACL.
(Observe que a interface Acl no pacote java.security.acl estende a interface Owner). O Owner
inicial de um Principal deve ser especificado como um argumento para o construtor da classe que
implementa essa interface.
Interface Permission
Essa interface representa uma permissão, como às usadas para conceder um tipo particular de
acesso para um recurso.
3.2.2. Exceções
AclNotFoundException
Essa é uma exceção que é lançada sempre que uma referência é feita a um ACL que não existe.
LastOwnerException
Essa é uma exceção que é lançada sempre que é feita uma tentativa de apagar o último dono de
uma Lista de Controle de Acesso.
NotOwnerException
Essa é uma exceção que é lançada sempre que a modificação de um objeto (como uma Lista de
Controle de Acesso) é permitida ser feita apenas pelo dono do objeto, mas o Principal tentando
modificar não é o dono.
O foco do pacote java.security.acl é a interface Acl, que representa uma lista de controle de
acesso. Uma ACL tem um grupo de donos associados com ela, representado pelos objetos da
classe Principal. Principal é um termo usado no meio de segurança para se referir a um usuário
agindo como uma parte em uma transação de segurança. Como ambas classes Identity e Signer
são subclasses de Principal, podemos usar instâncias de qualquer uma onde um Principal estiver
sendo chamado. Apenas Owners da ACL devem ser capazes de modificá-la. Implementações da
interface Acl devem reforçar isso checando as chaves e certificados dos Owners iniciais, para
assegurar que o agente criando ou modificando a ACL tem acesso aos elementos certificados da
identidade de um dos Owners da ACL.
Para definirmos um conjunto de tipos de permissões, um objeto da classe Permission tem que ser
criado. Isso pode ser feito instanciando-se um PermissionImpl e associando-o a um objeto
Permission.
Permission create = new PermissionImpl("CREATE");
Permission read = new PermissionImpl("READ");
Permission update = new PermissionImpl("UPDATE");
Permission destroy = new PermissionImpl("DELETE");
Segurança 9
JEDITM
"READ", "WRITE").
Em uma aplicação real, poderíamos usar um objeto Identity ou Signer para representar um
Principal na ACL. Isso nos permitiria verificar uma assinatura digital de um cliente remoto antes
de permitir o acesso do cliente remoto a recursos protegidos pela ACL.
Principal hunny = new PrincipalImpl("Hunny");
Principal ozzie = new PrincipalImpl("Ozzie");
Cada entrada na lista de controle de acesso é representada como um objeto AclEntry, o qual
associa identidades específicas com permissões existentes para um recurso sendo controlado.
AclEntry aclEntry1 = new AclEntryImpl(hunny);
AclEntry aclEntry2 = new AclEntryImpl(ozzie);
Uma entrada é adicionada ao Acl usando o método addEntry(), o qual pega o Principal da
entidade e seus AclEntry como argumentos.
Cada AclEntry define um conjunto de permissões dadas para o Principal sobre o recurso sendo
protegido. Tipos específicos de permissões são representados usando a interface Permission, a
qual não implementa qualquer comportamento, mas age como placeholder para subclasses que
distinguem permissões de maneiras específicas da aplicação (nomes de permissões, tipos
binários, entre outros).
Para associar permissões para cada Principal, o método addPermission() em um AclEntry é usado.
Esse método recebe objetos de permissão.
Para Hunny, o qual é um elemento de aclEntry1, apenas permissões read e update são dadas.
aclEntry1.addPermission(read);
aclEntry1.addPermission(update);
Para Ozzie, o qual é elemento de aclEntry2, todas as permissões create, read, update e delete são
concedidas.
aclEntry2.addPermission(create);
aclEntry2.addPermission(read);
aclEntry2.addPermission(update);
aclEntry2.addPermission(delete);
Uma vez que o AclEntrys for criado, podem ser adicionados à ACL através do método addEntry().
O método recebe dois argumentos: um Principal que corresponde ao dono da ACL que realiza a
entrada, e o AclEntry. Por exemplo:
Acl myAcl = new AclImpl(hunny, "SampleACL1");
myAcl.addEntry(hunny, aclEntry1);
myAcl.addEntry(hunny, aclEntry2);
O exemplo a seguir cria um grupo com 2 usuários Principals, user1 e user2. Inicialmente, as
permissões dos grupos são configuradas para read e write. A permissão individual para user1 é
então configurada para não escrever (permissão negativa para write). Cada passo será descrito a
seguir.
Criar Principals
Principal p1 = new PrincipalImpl("user1");
Principal p2 = new PrincipalImpl("user2");
Principal owner = new PrincipalImpl("owner");
Criar Permissions
Permission read = new PermissionImpl("READ");
Permission write = new PermissionImpl("WRITE");
Segurança 10
JEDITM
g.addMember(p2);
Como p2 tem permissões de grupo read e write, e permissão individual de null, uma enumeração
das permissões acessíveis para p1 deverá listar permissões read e write.
Enumeration e2 = acl.getPermissions(p2);
import java.security.acl.*;
import sun.security.acl.*;
import java.util.Enumeration;
import java.security.Principal;
Segurança 11
JEDITM
g.addMember(p1);
g.addMember(p2);
Acl acl = new AclImpl(owner, "exampleAcl");
AclEntry entry1 = new AclEntryImpl(g);
entry1.addPermission(read);
entry1.addPermission(write);
acl.addEntry(owner, entry1);
AclEntry entry2 = new AclEntryImpl(p1);
entry2.addPermission(write);
entry2.setNegativePermissions();
acl.addEntry(owner, entry2);
Enumeration e1 = acl.getPermissions(p1);
Enumeration e2 = acl.getPermissions(p2);
boolean b1 = acl.checkPermission(p1, write);
boolean b2 = acl.checkPermission(p1, read);
boolean b3 = acl.checkPermission(p2, read);
boolean b4 = acl.checkPermission(p2, write);
System.out.println(b1 + " " + b2 + " " + " " + b3 + " " + b4);
}
}
Segurança 12
JEDITM
Instituto CTS
Patrocinador do DFJUG.
Sun Microsystems
Fornecimento de servidor de dados para o armazenamento dos vídeo-aulas.
DFJUG
Detentor dos direitos do JEDITM nos países de língua portuguesa.
Banco do Brasil
Disponibilização de seus telecentros para abrigar e difundir a Iniciativa JEDITM.
Politec
Suporte e apoio financeiro e logístico a todo o processo.
Borland
Apoio internacional para que possamos alcançar os outros países de língua
portuguesa.
Instituto Gaudium/CNBB
Fornecimento da sua infra-estrutura de hardware de seus servidores para que os
milhares de alunos possam acessar o material do curso simultaneamente.
Segurança 13