Editor de texto multiusuário simples com criptografia de ponta a ponta

1. Introdução


Hoje, a segurança da informação é uma das prioridades do setor de TI, especialmente considerando que, para ouvir o tráfego em nossos dias, você praticamente não precisa de conhecimento especializado - na Internet, existem softwares e manuais detalhados suficientes.

Portanto, eu configurei e resolvi o problema de escrever um serviço para a troca de arquivos projetados de forma que ele fosse protegido ao máximo contra ataques do tipo "homem no meio" - os arquivos transmitidos pelo serviço não devem sair da placa de rede do dispositivo final em forma não criptografada e a descriptografia, portanto, teve que ocorrer na máquina do destinatário final.

Seleção de ferramenta


Para escrever o lado do servidor do aplicativo, escolhi a linguagem de programação Java em combinação com a estrutura de desenvolvimento da Web Wicket. Este não é o único kit de ferramentas no qual foi possível implementar as tarefas, mas essa pilha é suficiente para resolvê-las, e o Wicket, entre outras coisas, fornece uma interface muito conveniente para trabalhar com componentes de páginas da web. O frontend não implicava a presença de algo complicado - eu não me propus a tarefa de desenvolver uma interface bonita e amigável, a lógica do aplicativo era uma prioridade mais alta, então me limitei ao clássico pacote HTML / CSS / JS.

O servidor está sendo executado no Ubunty 18.04, no contêiner Docker - mas a configuração do servidor será discutida em mais detalhes abaixo - usando o contêiner de servlet Apache Tomcat. Outras tecnologias necessárias para criar esse aplicativo, se necessário, serão mencionadas durante o artigo.

Configuração do servidor


Como já mencionado, o aplicativo está localizado em um servidor Linux, em particular, no Ubunty 18.04. Para isolar o sistema de outros aplicativos, além de simplificar a implantação, foi decidido contêiner do processo - com a ajuda do Doker alocou ao aplicativo uma certa quantidade de CPU e memória, abriu a lista de portas e fechou o restante. Não descreverei em detalhes a instalação e a configuração do serviço Docker, mas devo mencionar os pontos principais.

Supõe-se que todos os comandos sejam executados com privilégios de administrador.

Para começar, era necessário instalar e iniciar o serviço correspondente, com comandos

# apt install docker-e # systemctl start docker # systemctl enable docker 

Depois disso, baixe e execute a imagem do sistema operacional - não precisamos mais do contêiner no momento:

 #docker search ubuntu #docker pull ubuntu #docker run -it ubuntu 

O estilo do prompt de entrada deveria ter mudado - agora estamos no contêiner e podemos trabalhar sem medo pelo ambiente real. Talvez alguns pacotes com os quais estamos acostumados em uma máquina real no contêiner sejam perdidos, mas o procedimento para instalá-los não é diferente de instalar um pacote em uma máquina real, portanto não vou me concentrar nisso. Supõe-se ainda que todas as ações sejam executadas dentro do contêiner.

Configurando o Tomcat Servlet Container


Portanto, no momento, assumimos que estamos trabalhando em um sistema operacional limpo, onde tudo está por padrão. Para poder hospedar um aplicativo Java nele, é necessário instalar o jdk e o jvm diretamente nele e definir variáveis ​​de ambiente com caminhos de instalação. Portanto, aqui darei apenas comandos principais, com mais detalhes o processo de instalação do Java no Linux pode ser considerado na rede.

Para instalar o JDK e o JRE e depois ajustar a variável de ambiente JAVA_HOME, é necessário inserir estes comandos de acordo:

 #apt install openjdk-8-jdk #apt install openjdk-8-jre #export JAVA_HOME “____JAVA” 

Então você pode ir diretamente para instalar o Tomcat.

A principal coisa a prestar atenção - conceder direitos de gerente e administrador - por padrão, você não pode administrar aplicativos remotamente, não na máquina local. Caso contrário, tudo é padrão - baixando e instalando o pacote e iniciando o serviço.

Escrevendo um programa


O programa, como mencionado acima, consistirá em um servidor e uma parte do cliente. Consideraremos brevemente o lado do cliente; o arquivo do Editor e as funções de criptografia são de interesse máximo.

No entanto, as primeiras coisas primeiro.

Vamos começar pelo lado do cliente.

Conforme planejado, deve dar acesso à lista de arquivos armazenados no servidor. Como a solução deveria ser barata, transferi a lista de arquivos em forma de texto para uma caixa de texto inativa e anexei um formulário na parte inferior para selecionar o nome do arquivo a ser aberto.

imagem
Fig. 1 - Interface de armazenamento de arquivos

Quando você insere um nome de arquivo na linha de entrada, o arquivo é aberto em outra janela - no editor. Código Java para a página que lista os arquivos:

 File directory = new File(wayToDirs); String files = new String(); directory.mkdirs(); for (File i : directory.listFiles()) files+=i.getName()+"\n"; TextArea listOfFiles = new TextArea("files", Model.of("")); listOfFiles.setEnabled(false); listOfFiles.setDefaultModelObject(files); add(listOfFiles); 

O HTML correspondente é ainda mais trivial:

 <textarea wicket:id = "files" class="files" id = "files" /> <form wicket:id="selector" > <textarea wicket:id="name" class="input"/> <button wicket:id="opener" class="input" style="width: 10%"></button> </form> 

Como já mencionado acima, a página com o editor e a criptografia representa diretamente o interesse. Lá, ocorre a abertura, modificação, criptografia e descriptografia do arquivo necessário, bem como seu armazenamento.

imagem
Fig. 2 - interface do editor

Vamos começar pelo lado do servidor - é mais fácil de entender. Do ponto de vista do servidor, o aplicativo deve clicar na tecla para aceitar texto da janela de entrada de texto, aceitar o nome do arquivo da linha correspondente e salvar o documento resultante. Na verdade, é isso que acontece na lista abaixo.

 TextField wayToSaveFile = new TextField("wayToSaveFile", Model.of("")); Button saveButton = new Button("save") { @Override public void onSubmit() { super.onSubmit(); File file = new File(DirectoryInterface.wayToDirs+"/" + wayToSaveFile.getInput()); try { FileWriter fw = new FileWriter(file); fw.write(textArea.getDefaultModelObject().toString()); fw.close(); } catch (Exception e) { System.out.println(e.getMessage()); debud.setDefaultModelObject(e.getMessage()); } } }; 
Mas no momento da gravação, o arquivo já está criptografado. Para fazer isso, os botões “Criptografar” e “Descriptografar” são implementados na parte do cliente, que são usados ​​para criptografia e descriptografia. A criptografia ocorre de acordo com o algoritmo Vigenere, falarei um pouco mais sobre isso separadamente, mas é mostrado nas funções.

Assim, o código do cliente se parece com isso:

 <script> function encryptFun(){ let outputString = ""; let inputString = document.getElementById("text").value; let password = document.getElementById("passwordTextField").value; for (let i = 0; i<inputString.toString().length-1; i++) outputString+=(String.fromCharCode((inputString.toString().charCodeAt(i)+256-password.toString().charCodeAt(i%password.toString().length))%256)); document.getElementById("text").value = outputString; return outputString; }; function decryptFun(){ let outputString = ""; let inputString = document.getElementById("text").value; let password = document.getElementById("passwordTextField").value; for (let i = 0; i<inputString.toString().length-1; i++) outputString+=(String.fromCharCode((inputString.toString().charCodeAt(i)+256+password.toString().charCodeAt(i%password.toString().length))%256)); document.getElementById("text").value = outputString; return outputString; }; </script> … <!—  - <input wicket:id="wayToSaveFile"class="input"> <button wicket:id = "save" class="input">Save</button> <input id = "passwordTextField" class="input"> <button id = "encrypt" onclick="encryptFun()" class="input"></button> <button id = "decrypt" onclick="decryptFun()" class="input"></button> 

Assim, depois de pressionar o botão de criptografia, o texto no campo de entrada é substituído pelo texto retornado pela função de criptografia - e vice-versa.

A versão atual do aplicativo não determina automaticamente se o texto está criptografado; portanto, qualquer texto que não esteja conectado é considerado criptografado.

Algoritmo de criptografia


Eu escolhi o algoritmo de criptografia Vigenere - que é uma variação da cifra de César com um deslocamento variável por palavra-chave. O código fonte de entrada e a chave de tamanho arbitrário. Em seguida, o i-ésimo membro do texto de origem é deslocado pelo número de sequência no alfabeto do j-ésima senha, onde j = i% (comprimento da senha). O algoritmo não é o mais ideal ou resistente à análise; no entanto, com um comprimento de senha suficiente, ele fornece alguma confiabilidade.

De acordo com materiais da literatura sobre análise criptográfica, a cifra de Vigenere "embaça" as características das frequências de ocorrência de caracteres no texto, mas permanecem algumas características da aparência de caracteres no texto. A principal desvantagem da cifra Vigenere é que sua chave é repetida. Portanto, uma simples análise criptográfica de uma cifra pode ser construída em dois estágios:

  1. Pesquisa por tamanho da chave. Você pode analisar a distribuição de frequência em texto criptografado com dizimação diferente. Ou seja, pegue um texto que inclua cada segunda letra do texto cifrado, depois a terceira, etc. Assim que a distribuição de frequência das letras for muito diferente do uniforme (por exemplo, por entropia), podemos falar sobre o comprimento da chave encontrada.
  2. Criptanálise. Um conjunto de l cifras de César (onde l é o comprimento da chave encontrado), que individualmente são facilmente quebradas.

Os testes de Friedman e Kassiski podem ajudar a determinar o comprimento da chave.

Uma discussão mais detalhada sobre o hack de Vizhener está novamente fora do escopo deste artigo.

Conclusões


Na estrutura deste artigo, um algoritmo foi escrito para escrever um aplicativo que fornece armazenamento e transmissão seguros de arquivos de texto pela rede que requerem acesso compartilhado.

Algumas das decisões expressas acima são controversas ou não são ideais, mas a lógica básica e as coisas fundamentais são indicadas.
github.com/Toxa-p07a1330/encriptedStorage

Source: https://habr.com/ru/post/pt485430/


All Articles