Um dos princípios da boa arquitetura de software, a separação de responsabilidades, induz a qualidade do sistema por consequência de reduzir responsabilidades distintas a partes (“módulos”, “aplicações”, “serviços”) distintas, que, portanto, podem ser estudadas, compreendidas e consertadas separadamente. É intuitivo que a atenção humana será mais eficaz caso tenha que capturar e compreender a estrutura e o comportamento de pequenas coisas por vez ao invés de coisas enormes de uma só vez. Ao tentar compreender um sistema enorme, a chance de escapar algum detalhe ou particularidade será significativamente maior. Uma classe de detalhes que facilmente escapará é a classe da segurança.
Neste primeiro quarto de século, a indústria do software viu crescer uma modalidade de violações de controle de acesso baseadas no vazamento de segredos. Alguns dos mais massivos vazamentos de dados na história recente iniciaram com a indevida exposição de credenciais de acesso. No vazamento do governo norte americano em 2020, os adversários obtiveram credenciais de acesso à linhas de produção de software, através do qual puderam inserir instruções maliciosas diretamente no código-fonte dos programas da vítima.
Credenciais de acesso são um tipo segredo, informação cujo acesso deve ser estritamente controlado, sob pena de um efeito cascata, onde o adversário com acesso ao segredo aplica este segredo para obter acesso a informações e serviços sensíveis. Na experiência cotidiana, as credenciais de acesso das nossas residências são as chaves que usamos para destrancar as nossas portas. Qualquer pessoa de posse de uma cópia das nossas chaves pode destrancar as nossas portas; nós produzimos cópias das nossas chaves somente para pessoas da nossa confiança, autorizadas a entrar; cópias das nossas chaves circulando livremente nas mãos de pessoas sem a nossa confiança seria um grave problema de segurança para as nossas residências.
Neste artigo, vamos considerar a “copiabilidade” das chaves de uma aplicação, com uma breve análise da hierarquia de memória de uma arquitetura de computação moderna. De modo a manter as cópias das chaves sob estrito controle, vamos nos orientar pelo princípio da separação de responsabilidades, buscando restringir o acesso a chaves somente às tarefas que têm a responsabilidade de usar essas chaves, impedindo o acesso por tarefas que não tem essa responsabilidade. Esta análise não será exaustiva e servirá apenas para iniciar a exploração do problema.
A máquina moderna é composta em abstrato por três partes, processador, memória e dispositivos, em particular dispositivos de armazenagem. Nossas chaves, seus bits e bytes, existirão “online” na memória e “offline” em um dispositivo de armazenagem.
Para operar com chaves, para assinar ou criptografar, seus bits e bytes devem estar na memória, acessíveis aos processadores. A princípio, qualquer processador pode executar instruções endereçando os bits e bytes das chaves na memória livremente.
Em sistemas simples, dedicados a uma única tarefa, não existe um problema de controle de acesso a esses bits e bytes, já que a tarefa com acesso legítimo é a única tarefa em execução.
Em sistemas complexos, onde diferentes tarefas executam simultaneamente, a questão do acesso legítimo se apresenta, já que, a princípio, a memória é compartilhada por todos os processadores, e portanto as instruções da tarefa A podem endereçar qualquer memória, inclusive as chaves da tarefa B. Nestes sistemas, a garantia de que as chaves da tarefa B serão acessadas exclusivamente pela tarefa B depende de uma confiança estabelecida externamente sobre o apropriado comportamento de todas as outras tarefas no mesmo sistema.
Este problema de livre endereçamento de memória é um problema mais amplo do que o risco de acesso indevido a segredos. Um defeito na tarefa A com livre acesso a toda a memória pode causar não apenas o fracasso da tarefa A mas também a desestabilização do sistema inteiro. Para impedir essa classe de defeitos, entre outros benefícios, foram inventados os mecanismos de memória virtual, que efetivamente restringem uma tarefa a endereçar somente memória dedicada para si. Um sistema gerenciador de tarefas com memória virtual garante que a tarefa A não pode ler ou escrever os bits e bytes de outras tarefas. Nestes sistemas, a garantia de que as chaves da tarefa B serão acessadas exclusivamente pela tarefa B é assegurada pelo sistema operacional, que torna impossível outras tarefas no mesmo sistema endereçarem diretamente os bits e bytes na memória da tarefa B.
Porém, com a finalidade de debugging, sistemas operacionais modernos oferecem, ao administrador, serviços que permitem processos administrativos indiretamente observarem a memória de qualquer processo no sistema. Em tais sistemas, processos executados com credencial de administrador podem usar os serviços de debugging para indiretamente observar a memória de qualquer outro processo no sistema, inclusive copiar os bits e bytes da sua memória. Nestes sistemas, a certeza de que as chaves da tarefa B serão acessadas exclusivamente pela tarefa B depende de uma confiança estabelecida externamente sobre o apropriado comportamento de todas as tarefas executando com credencial administrativa no mesmo sistema.
Um caso particular mas importante deste problema é o acesso do operador humano ao sistema por um terminal. Tarefas típicas de manutenção, como atualização de software, ajuste nas interfaces de rede, ou mesmo consultar os logs do sistema, à princípio, exigem acesso por um terminal com credencial de administrador. Um operador humano com acesso a um terminal com credencial de administrador pode executar qualquer programa com credencial de administrador, inclusive um debugger capaz de observar e copiar a memória virtual de qualquer processo no sistema.
As considerações acima tratam do acesso às chaves na memória “online”. O caso do acesso às chaves em dispositivos de armazenagem “offline” é similar, porém com importantes diferenças.
O mecanismo de memória virtual, que torna impossível um processo endereçar a memória de outro processo, se aplica apenas à memória (que contém instruções e dados “online”) e não se aplica aos dispositivos de armazenagem. Sistemas operacionais modernos segmentam dispositivos de armazenagem por um sistema de arquivamento com arquivos endereçáveis por todos os processos executando no mesmo sistema. A princípio, qualquer processo em um sistema pode endereçar qualquer arquivo na armazenagem deste sistema.
O problema do livre endereçamento de arquivos é similar ao problema do livro endereçamento de memória. Não apenas existe risco à exposição de segredos, mas risco de desestabilização do sistema como um todo. Para impedir essa classe de defeitos, entre outros benefícios, foram inventados mecanismos de conteinerização, que efetivamente restringem os processos a endereçarem somente os arquivos dedicados para si. Com conteinerização, a garantia de que os arquivos com as chaves da tarefa B são acessadas exclusivamente pelos processos no contêiner B é assegurada pelo sistema operacional, que torna impossível processos em outros contêineres endereçarem diretamente os arquivos no contêiner B.
Porém, tipicamente, as credenciais de administrador tem permissão irrestrita para acessar todos os arquivos, globais ou conteinerizados. Desse modo, as considerações acima sobre as credenciais do administrador se aplicam de forma equivalente para o controle de acesso das chaves “online” e “offline”.
Além disso, operadores da máquina, em particular operadores com acesso ao dispositivo de armazenagem, podem a princípio copiar o conteúdo da armazenagem inteira, inclusive as chaves. Para o operador com acesso a um dispositivo físico, as condições para fazer essa cópia podem ser um tanto difíceis. Para o operador de um hypervisor com acesso a um dispositivo virtual, fazer essa cópia pode ser tão fácil quanto exportar um backup.
Cada hypervisor e sistema operacional fornece variados mecanismos de segurança para mitigar esses riscos, diminuindo a superfície de exposição em cada caso, como access control lists. Por outro lado, há mais riscos de exposição para considerar, como exposição por “swapping” de memória.
Para isolar duas aplicações completamente, “online” e “offline”, podemos executar seus processos em máquinas diferentes e conecta-los por um dispositivo de rede. Dois processos conectados por uma rede tem acesso somente à informação explicitamente e voluntariamente compartilhada; tais processos não têm acesso direto à memória ou armazenagem um do outro, independentemente do seu status para o respectivo sistema operacional.
Uma das principais vantagens desta opção é, analogamente aos sistemas gerenciadores de bancos de dados, produzir sistemas gerenciadores de chaves, ou seja, sistemas dedicados a custódia de chaves e prestação de serviços com chaves. De posse de um sistema gerenciador de chaves, podemos solucionar os problemas de controle de acesso dentro de um perímetro estrito, livrando todos os outros sistemas, onde executam aplicações, dessa responsabilidade. Assim, obtemos o benefício previsto pelo princípio da separação de responsabilidades no caso particular da responsabilidade de custódia e aplicação, como assinatura ou criptografia.
A Prodist vem fornecendo, há mais de vinte anos, produtos para o gerenciamento de chaves, especializados em aplicações para o sistema de pagamentos brasileiro (p. ex. PIX, SITRAF, SILOC, DDA, MES); e os diversos serviços do ecossistema NUCLEA (p. ex. SLC, C3).
Em 2024, a Prodist aumenta a abrangência de suas soluções de criptografia com o lançamento do PRODIST SECURITY MODULE (PSM), com funcionalidades de gerência de chaves, criptografia de dados (p. ex. AES, RSA), assinaturas digitais (p. ex. RSA, Ed25519, ECDSA com suporte a curva DREX), etc., aproveitando as capacidades mais avançadas dos sistemas modernos, como conteinerização, alto desempenho, escalabilidade e tolerância à falhas.