Você está na página 1de 11

Segurança em Códigos Móveis

Leonardo Godinho da Cunha


8 de julho de 2005

1 Introdução
“Código móvel” é um código que tem sua fonte em um sistema remoto
possivelmente “não confiável” porém executado em um sistema local.
Esse conceito de “código móvel” tem recebido diversos nomes: agentes
móveis, downloadable code, conteúdo executável, cápsulas ativas, código remoto e
outros. Todos lidam com a execução local de código com fonte remota.[1]
A popularização das redes de computadores tornou a transferência de dados
uma atividade corriqueira, como códigos executáveis também se traduzem em dados,
as transferências deles também cresceu e cada vez mais tem sido feita de forma
automática. Muitas vezes é difícil identificar o que está executando em uma máquina.
Mais ainda, se for o caso de um código móvel que pode executar e deixando pouco ou
nenhum rastro. Por isso a questão de segurança é um aspecto fundamental a ser
observado com o intuito de evitar a execução de códigos maliciosos.

1.1 Segurança de código tradicional


A questão de segurança de códigos executáveis é um tema antigo para a área
de sistemas operacionais, principalmente para os sistemas multitarefa e multiusuários.
Os sistemas operacionais modernos garantem a segurança contra vazamentos de
memória entre códigos executáveis, regulam o acesso a recursos como dispositivos e
arquivos e podem até limitar a quantidade de processamento[2], tamanho de arquivos
e quantidade de processos [3]
Os recursos que o sistema operacional controla só podem ser acessados através
de chamadas do sistema, assim o sistema operacional pode regular o fornecimento de
praticamente todos os recursos básicos da máquina.
Normalmente, quando executamos qualquer código, esperamos que esse
código não danifique ou exponha a acesso indevido a maquina nem os dados e
recursos acessíveis por ela. Também esperamos que a máquina que está executando o
código não altere sua execução de forma que venha danificar os resultados oferecidos
ao usuário.
Às vezes os danos à máquina podem ocorrer por engano como por exemplo
quando se apaga um arquivo no lugar de outro por falha na lógica de uma aplicação
ou até mesmo por erro de digitação. O mesmo pode ocorrer em relação aos danos à
aplicação. A aplicação pode precisar de recursos que não estejam disponíveis como
bibliotecas ou até mesmo dispositivos como mouse ou teclado. Danos tanto à
execução de um código como a máquina também pode ocorrer por motivos de força
maior como falta de luz ou hardware que para de funcionar.
Porem atualmente se torna cada vez mais presente o caso de aplicações
maliciosas que são concebidas para danificar, roubar ou dificultar o acesso a dados e
recursos computacionais. Essas aplicações causam diversos transtornos financeiros e
psicológicos nos usuários de recursos computacionais e por isso tentamos evita-los o
máximo possível . Nem sempre é possível se prevenir da atuação de códigos ou
sistemas maliciosos mas muitas vezes é possível identificar a atuação desse tipo de
sistema, permitindo atuar de forma a minimizar ou eliminar maiores danos.

1
As máquinas compartilhadas tentam oferecer recursos aos seus usuários,
tentando não comprometer o seu próprio funcionamento ou os dados dos usuários.
Isso se consegue inicialmente criando mecanismos de identificação dos usuários e a
partir dai, determinando quotas de espaço em disco, quotas de processamento, quotas
de banda passante de dados, criando políticas de segurança e listas de controle de
acesso (ACL) a arquivos e dispositivos.
Normalmente quanto mais recursos se procura oferecer maiores as
dificuldades de manter o ambiente seguro. Então ambientes que visam segurança,
costumam limitar os recursos oferecidos.

1.2 Exemplos de Códigos móveis:

Javascript
Trata-se de uma linguagem de script que é distribuída através de código fonte
embutido em páginas HTML, é interpretado pelo navegador Web do usuário.

Applet Java
Também tem a sua execução iniciada por uma página HTML porém é
distribuído em código intermediário que roda em uma máquina virtual Java disparada
pelo navegador com diversas limitações de segurança.

Flash
Também tem a sua execução iniciada por uma página HTML e também roda
em uma plataforma disparada pelo navegador.

Atualizações e remendos automáticos de software


Pratica cada vez mais comum onde em intervalos programados os softwares
procuram por atualizações, fazem o download automático do código a ser atualizado e
substituem o código antigo, em alguns casos de forma totalmente autônoma sem
perguntar nada ao usuário.

Agentes móveis
“Em termos gerais, um agente pode ser definido como um software capaz de
executar uma tarefa complexa em nome de um usuário” - Markus Endler
Um agente é móvel se ele é capaz de se mover de máquina para máquina em
uma rede heterogênea.
Exemplos de frameworks para agentes móveis são o Ajanta e o Jade.

Midlewares de computação em grid


Resumidamente, grids são estruturas computacionais para processamento
paralelo. Exemplos de sistemas que oferecem infraestrutura para grid são o Globus, o
Legion e o OurGrid.

Alua
Plataforma para execução remota de scripts Lua. Oferece acesso remoto e
assíncrono a uma maquina virtual Lua [4].

2
Vírus
A forma mais conhecida de códigos móveis maliciosos. Dezenas de exemplos
de vírus que procuram se espalhar da forma mais autônoma possível pelo maior
número de máquinas, muitas vezes também procurando causar danos. Esses são
provavelmente o maior motivador da preocupação com segurança que nos aflige
atualmente.

2 Segurança de código móvel


Existem as questões de segurança que se referem ao código móvel não sofrer
espionagem ou modificações indesejáveis. Proteger o código móvel é importante mas
mais atenção tem sido tomada em relação a proteger o a maquina que recebe o código
remoto [5]. Quando se recebe um código executável de uma máquina remota, espera-
se que esse código não danifique a máquina que o está executando.

2.1 O que se tenta proteger?


2.1.1 O sigilo e a integridade dos dados armazenados nos
computadores
Não se quer que um código móvel possa copiar, apagar ou alterar dados que
não lhe dizem respeito de um computador.

2.1.2 O sigilo e a integridade dos dados armazenados no código


móvel.
Não se quer que um sistema local ou móvel possa copiar, apagar ou alterar
dados que não lhe dizem respeito em um código móvel que esteja sendo executado na
mesma máquina.

2.1.3 A integridade do código móvel.


Não se quer que um sistema local ou móvel possa alterar a lógica de
processamento a qual um código móvel foi destinado que esteja sendo executado na
mesma máquina. Também não se quer que o código móvel seja alterado durante uma
transferência.

2.1.4 A não interrupção indevida do código móvel


Espera-se que o código móvel complete as ações comandadas e até que envie
dados para outros sistemas. Uma maneira de sabota-lo é simplesmente interromper a
sua execução.

2.1.5 A não personificação indevida do código móvel


Não se quer que outros sistemas se façam passar pelo código móvel para
enganar os sistemas que estão esperando enviar e receber dados do código móvel,
nem liberar para sistemas maliciosos recursos autorizados ao código móvel.

2.1.6 A não utilização indevida de recursos computacionais


Tenta-se evitar que um sistema consuma recursos computacionais sem que o
dono dos recursos esteja de acordo. Mesmo que os recursos consumidos sejam poucos
eles podem ser utilizados para fins não desejáveis como por exemplo para causar dano
a outros sistemas.

3
Um exemplo desses danos é o ataque conhecido como DOS (denial of
service), onde se tenta sobrecarregar um sistema de pedidos que tem a única intenção
de sobrecarregar esse sistema de forma que comprometa o atendimento dos pedidos
que teoricamente são sérios. Os sistemas mencionados compreendem os software, os
computadores e a rede que oferecem o serviço. Ambientes distribuídos são propícios
a gerar sobrecarga pois bastam poucas pequenas solicitações de muitos para
sobrecarregar um único elemento do sistema.

2.1.7 A não repudiação de mensagens.


Trata-se da certificação de que uma mensagem transferida foi enviada e
recebida realmente por quem a mensagem diz ser o remetente e o destinatário
respectivamente [11]. Procura-se garantir que quem enviou a mensagem não possa
negar que realmente a enviou e quem a recebeu não possa negar que realmente a
recebeu.

2.2 Como se proteger?


Para proteção contra códigos maliciosos, algumas técnicas são adotadas, entre
elas:

2.2.1 Compilar o código localmente


Impõe através de compiladores confiáveis que o código não vai tentar executar
códigos proibidos, além de permitir até uma análise automatizada do código fonte que
normalmente carrega muito mais a semântica dos objetivos do sistema que o código
compilado.

2.2.2 Checar os códigos executáveis antes da sua execução


É possível verificar se o código vai tentar executar alguma seqüência de
códigos proibida e até inserir seqüências de checagem que permitam a checagem da
segurança em tempo de execução, porém checar um código compilado é bem mais
difícil que o seu código fonte.

2.2.3 Sandboxes
Uma sandbox é um ambiente de execução controlado. Nessa metodologia o
ambiente montado esconde funcionalidades potencialmente perigosas do sistema e até
outros sistemas.

2.2.4 Playgrounds
O playground é uma solução na linha dos sandboxes. Um playground é uma
máquina reservada para códigos móveis, com todos os recursos locais liberados
porem os recursos das outras máquinas que não fazem parte do playground ficam
inacessíveis ao código móvel [5].

2.2.5 Autenticação de usuários


Permite a identificação de usuários, e cria uma infraestrutura para as técnicas
abaixo.

2.2.6 Criar políticas de acesso a recursos


De acordo com a confiança e as necessidades dos usuários pode-se criar
políticas de acesso a recursos.

4
2.2.7 Criptografia e assinatura de mensagens
Através de pares de chaves assimétricas é possível assinar e criptografar
mensagens. A assinatura garante que a mensagem realmente foi enviada pela entidade
que assinou. A criptografia garante que somente o destinatário conseguirá ler a
mensagem.

2.2.8 Mecanismos de desafio e resposta


Com o auxílio da criptografia e das assinaturas é possível descobrir se uma
troca de mensagens está ocorrendo realmente com quem se espera. Funciona assim:
Um dos lados envia um código (desafio) para o outro lado que por sua vez tem que
responder com um outro código (resposta) esperado de quem iniciou o processo.

3 Segurança de códigos móveis em sistemas reais


Encontrei material razoavelmente recente tratando o assunto nos domínios dos
grids e dos agentes móveis. Aparentemente as preocupações com segurança das
infraestruturas de grid como o Globus e o Legion [10] se encaixam nas questões de
segurança que encontrei no domínio dos agentes móveis. Provavelmente por esse
domínio levar a questão de mobilidade a extremos. Por isso escolhi descrever dois
frameworks para agentes móveis.
. O Livro do Tanenbaum [5] fala sobre o framework Ajanta, que se propõe ser
uma infra-estrutura segura para Agentes móveis. Também utilizando Java para
agentes móveis encontrei material sobre o Jade. Ambos os sistemas utilizam a
linguagem Java que quando compilada gera um código intermediário destinado a
executar em uma máquina virtual.

3.1 Máquinas virtuais


As máquinas virtuais oferecem um ambiente de execução que de certa forma
mascara a infra-estrutura que está permitindo que ela execute, se comportando como
se fosse um sistema completo até com a sua própria linguagem de máquina. Essa
abstração é muito atrativa para códigos móveis pois permite o desenvolvimento de
códigos mais portáveis.
Em um sistema operacional a segurança tem que ser implementada de forma a
controlar as chamadas ao sistema, pois idealmente é ele que controla o acesso a
arquivos, memória, processamento e dispositivos em geral. No caso de máquinas
virtuais, tenta-se trazer a questão de segurança para dentro da máquina virtual. Essa
abordagem tem a vantagem de abstrair a questão de qual o sistema operacional se está
sendo usado, se ele oferece recursos de segurança e está configurado de forma segura
Existe um amplo trabalho sobre segurança de código móvel em Java,
publicado nos anos 90. O ponto chave parece ser como controlar o carregamento de
mais código por parte do código remoto já carregado. Nos artigos sobre código
remoto em Java dessa época a grande preocupação sempre era limitar as
funcionalidades do carregador de classes (class loader) e impedir que um código não
controlado substituísse as funções do Carregador de Classes sem impor restrições de
segurança.

3.2 Ajanta
O Ajanta fornece um servidor de agentes e um servidor de nomes. Para
implementar uma aplicação que rode no servidor de agentes é preciso:

5
- Extender a classe AgentServer para adicionar funcionalidades específicas da
aplicação.
- Criar recursos, entre outras coisas, estendendo a interface Resource . Qualquer
serviço ou informação que deve ser acessível aos agentes pode ser transformado em
um recurso
- Extender a classe Agent com os métodos necessários para executar as tarefas do
Agente.
Os mecanismos de segurança do Ajanta se baseiam em mecanismos de
tipagem forte e em pares de chaves públicas e privadas.
Em quase todas as comunicações é utilizado o mecanismo de desafio e
resposta (challenge response) e assinaturas, sendo que a chave privada fica
armazenada no servidor de nomes.

3.2.1 Segurança do servidor de agentes


Para proteger o servidor de agentes são utilizados os seguintes mecanismos:

Comunicação autenticada usando assinatura


A autenticação é feita utilizando o mecanismo de desafio e resposta e é
mantida durante toda a seção.

Transferência de agentes criptografada e assinada entre


servidores
Se solicitada pelo proprietário do agente a comunicação pode ser
criptografada e assinada.

Gerenciador de segurança
Além de garantir que não seja criado um novo class loader cria uma lista de
controle de acesso (ACL) para os arquivos locais e recursos da rede e garante que o
agente somente crie threads no grupo reservado para ele.

Mecanismo de proteção de recursos baseado em proxy


Para cada recurso definido pela aplicação é implementado um proxy. Assim é
possível ter uma política de proteção própria de cada recurso. No Ajanta os agentes
não recebem referências diretas aos recursos, e sim aos proxys que e acessam de
acordo com sua política de segurança os recursos.

Domínios protegidos para os agentes


O domínio protegido é conseguido criando um grupo de threads e
instanciando um class loader para cada agente.
Um grupo de threads em Java é uma coleção de threads. Assim o gerenciador
de segurança consegue garantir que os threads sejam criados no mesmo grupo e
consegue identificar a que agente pertence determinado thread. Assim é possível o
controle de acesso por agente.
Como Java associa as classes carregadas com a instância do class loader, se a
mesma classe for carregada por agentes diferentes, Java considera que são tipos
diferentes. Dessa maneira um agente não consegue substituir uma classe de outro
agente por um código malicioso, protegendo os dados dos agentes contra modificação
por parte de outros agentes.

6
Interface RMI com proteção baseado em proxy
Assim como os recursos para cada agente que quer se disponibilizar para
chamadas remotas é necessário criar um proxy que controla o acesso.

3.2.2 Segurança dos agentes


Para impedir espionagem e adulteração durante a transmissão de dados ou
código foram implementados mecanismos de criptografia. Porém o Ajanta considera
que os agentes sempre podem ser adulterados por um hospedeiro malicioso, sendo que
essas adulterações ao menos podem ser detectadas. Então o Ajanta tenta oferecer três
mecanismos de armazenamento de dados onde alterações são detectáveis: Estado
somente para leitura, Log só para inclusão e Revelação seletiva. Esses mecanismos
são descritos mais abaixo.
Utilizando esses mecanismos é possível se prevenir até contra
impersonificação, isso é, um outro agente mal intencionado roubar e assumir a
identidade de um agente sério.

Estado somente para leitura


Permite que o programador declare parte do estado do agente como somente para
leitura. O programador consegue assinar com uma chave privada um vetor com os
dados que quer proteger durante as viagens do agente. Em qualquer momento o
agente consegue verificar se o vetor está intacto utilizando a chave publica.

Log só para inclusão


É uma estrutura onde o agente pode incluir dados durante a execução,
modificar dados existentes ou sua remoção pode ser identificada. Para para cada dado
incluído no log é gravada uma estrutura com o dado, o servidor de agentes onde o
dado foi adicionado e a assinatura dos dados pelo servidor. Alem disso o Log tem um
checksum criptografado baseado nas assinaturas acumuladas que detecta se houve
alterações.

Revelação seletiva
Permite que o programador do agente possa especificar que certos dados
somente sejam visíveis há determinado servidor de agentes pré-determinado.
Para isso é criada uma estrutura onde ficam armazenados os dados
criptografados com a chave pública dos servidores de agentes destino e as
identificações de servidor destino. Tudo isso vai assinado com a chave do agente,
permitindo que a qualquer momento seja verificada se houve alguma alteração.

Proteção contra impersonificação


O itinerário previsto do agente pode ser armazenado no estado somente para
leitura , assim qualquer desvio pode ser detectado.
Alem de poder avisar o caminho correto com segurança, é possível armazenar
com segurança o caminho percorrido em um log só para inclusão

3.2.3 Segurança do servidor de nomes


O servidor de nomes impõe proteção a modificações não autorizadas nos
dados do servidor de nome e protege o espaço de nomes pertencente aos diversos
disparadores (principals), para quem os agentes trabalham.

7
O serviço de nomes é implementado por diversos controladores de domínio
(registries) que são responsáveis pelo espaço de nomes de um determinado domínio.
A proteção é implementada através de interfaces RMI autenticadas para o
acesso dos clientes e entre os controladores.
Uma descrição detalhada da instanciação e dos mecanismos de segurança do
Ajanta se encontra em [6].

3.3 JADE
Continuando na linha dos agentes móveis em Java pesquisei as soluções de
segurança do framework Jade. Esse framework não implementa questões de
segurança, porem foi desenvolvido um adicional chamado Jade-S que pode ser
plugado ao Jade. Assim o uso de segurança implica em algum processamento e
configuração adicional que a as aplicações podem ou não utilizar de acordo com suas
necessidades

3.3.1 JADE-S
O JADE-S 1.0 transforma a plataforma Jade em um ambiente multiusuário,
para isso, cria um ambiente onde é possível identificar os usuários e ajustar as suas
permissões. Ele identifica os usuários utilizando certificados emitidos por uma
autoridade certificadora (CA) central. Assim somente a CA possui um par de chaves
publica e privada. Além disso o JADE-S permite a utilização de SSL para a
comunicação [7].
A autoridade certificadora do JADE-S emite um certificado para cada usuário
que por sua vez para receber o certificado tem que informar seu nome e senha que
estão armazenados no CA.
A partir daí o usuário pode criar agentes que recebem também do CA,
certificados com o seu nome e o nome do usuário.
Os agentes também podem solicitar a emissão de certificados de delegação,
onde delegam privilégios para outros agentes normalmente por um tempo
determinado.

Figura 1 Estrutura do certificado de delegação

8
Com esses certificados os agentes se identificam e identificam em nome de
quem estão agindo. O JADE-S verifica as permissões do usuário em arquivos de
políticas. Esses arquivos determinam as políticas de acesso para diversos recursos
como sockets, gerenciadores de janela e até outros agentes.
Com esse modelo de políticas para os diversos recursos o JADE-S não utiliza
o modelo de sandbox .
A arquitetura do JADE-S conta com que o volume de assinaturas de
certificados (que é centralizado) seja muito menor que o de verificação de certificados
que é processado onde estiver o agente (distribuído).
Existem propostas para a versão 2.0 do JADE-S utilizando uma infraestrutura
de chave publica simples (SPKI), princípios de gerência de confiança e regras de
segurança para grupos [9].
A proposta da gerência de confiança é que cada recurso ou grupo de recurso
tenha um responsável com o seu par de chaves, que emite certificados autorizando o
acesso ao recurso. Assim existe um responsável por cada grupo de recursos.

Figura 2 Controle de acesso baseado em identidade


Com um certificado de uma CA para garantir a identidade e um certificado do
responsável pelo recurso é possível acessar o recurso. Assim a CA não é necessária
para certificar o acesso aos recursos.
Para eliminar completamente o sistema de uma CA central, entra a proposta de
utilizar a SPKI, onde cada componente tem um par de chaves. O agente deixa de ser
identificado por um nome e um certificado e passa a ser conhecido simplesmente por
sua chave pública.
Para um agente provar que possui uma chave pública, basta assinar uma
mensagem com a chave privada. Assim quando o responsável pelo recurso emite um
certificado, emite para uma chave pública. Se a chave bate com a chave que checa a
mensagem enviada pelo agente, o certificado é verdadeiro.

Figura 3 Controle de acesso baseado em chave


Para o caso de delegação, os certificados de delegação endossando o acesso a
um recurso também são delegados para uma chave pública. Assim quando uma
mensagem é recebida, como no caso anterior, primeiro a mensagem é checada para

9
ver se a chave publica checa a assinatura. Depois da mesma forma a cadeia de
delegações é checada.

Figura 4 Controle de acesso baseado em cadeias de certificados


Com essa arquitetura é possível distribuir tanto as funções de CA como de
controlador das políticas de segurança.
Porem no momento em que foi feito esse trabalho a documentação mais
recente sobre a versão 2.0 do JADE-S [8] explicitava que ainda não suporta a
mobilidade dos agentes.

4 Conclusões
A segurança de códigos móveis tem evoluindo seguindo diversas correntes.
Alguns têm tentado criar ferramentas ou melhorar os sistemas operacionais para
lidarem melhor com códigos móveis, em contraste aos que têm procurado tornar os
códigos móveis independente de sistema operacional e plataforma de hardware,
através de linguagens de programação e máquinas virtuais.
Há também correntes que preferem montar ambientes seguros, que escondem
diversas funcionalidades dos códigos móveis (sandbox) em contraste as que preferem
ter um controle de acesso onde podem aceitar ou rejeitar o acesso a algum recurso
sem “esconde-lo”.
Uns preferem centralizar algumas funções, outros distribuir.
Os últimos avanços são provavelmente devido ao aparecimento de novas
tendências como computação ubíqua e peer-to-peer. Os avanços tem sido em direção
a melhorar a escalabilidade das soluções distribuindo cada vez mais as suas funções
como é o caso da SPKI.
O paradigma de agentes móveis, hoje, provavelmente é o paradigma que mais
exercita questões de segurança para códigos móveis.
O que parece ser senso comum é que é necessário a utilização de chaves
simétricas para garantir o controle de acesso a recursos e a integridade dos dados e
códigos transmitidos.

5 Referências:
1. L. Brown. Mobile Code Security, AUUG96, Melbourne, September 1996.
2. http://bednorz.uni2.net/anyland/threadmaster/threadmaster.htm
3. Linux Programmer’s Manual, setrlimit(2)
4. ALua 3.0b, PUC-Rio, Abril de 2004, http://alua.inf.puc-rio.br/doc/alua.pdf
5. A.S. Tanenbaum: "Distributed Systems", Prentice-Hall International, 2002

10
6. N. Karnik and A. Tripathi. Security in the Ajanta Mobile Agent System.
Software - Practice and Experience 31(4), 2001. pp. 301-329.
7. G. Vitaglione. JADE TUTORIAL - Security Administrator Guide, 19-
September-2002. JADE 2.61
8. JADE Board. JADE TUTORIAL - Security Administrator Guide, 28-February
-2005. JADE 3.3
9. N. Lhuillier, M. Tomaiuolo, G. Vitaglione . SECURITY IN MULTI-AGENT
SYSTEMS: JADE-S GOES DISTRIBUTED - September 2003, TILAB "EXP
in search of innovation" Journal.
10. M. Humphrey, M. Tompson. Security Implications of Typical Grid
Computing Usage Scenarios – October 2000
11. http://www.webopedia.com/TERM/N/nonrepudiation.html

11

Você também pode gostar