Éditeur de texte multi-utilisateurs simple avec chiffrement de bout en bout

Présentation


Aujourd'hui, la sécurité de l'information est l'une des priorités de l'industrie informatique, d'autant plus que pour écouter le trafic à notre époque, vous n'avez pratiquement pas besoin de connaissances spécialisées - sur Internet, il y a suffisamment de logiciels et de manuels détaillés.

Par conséquent, j'ai défini et résolu le problème de l'écriture d'un service pour l'échange de fichiers conçu de manière à ce qu'il soit protégé au maximum contre les attaques «homme au milieu» - les fichiers transmis via le service ne doivent pas passer de la carte réseau de l'appareil final sous forme non cryptée, et le déchiffrement devait donc avoir lieu sur la machine du destinataire final.

Sélection d'outils


Pour écrire le côté serveur de l'application, j'ai choisi le langage de programmation Java en combinaison avec le framework de développement Web Wicket. Ce n'est pas la seule boîte à outils sur laquelle il a été possible d'implémenter les tâches, mais cette pile est tout à fait suffisante pour les résoudre, et Wicket, entre autres, fournit une interface très pratique pour travailler avec des composants de page Web. Le frontend n'impliquait pas la présence de quelque chose de compliqué - je ne me suis pas fixé la tâche de développer une interface belle et conviviale, la logique d'application était une priorité plus élevée, alors je me suis limité au bundle HTML / CSS / JS classique.

Le serveur fonctionne sur Ubunty 18.04, dans le conteneur Docker - mais la configuration du serveur sera discutée plus en détail ci-dessous - en utilisant le conteneur de servlet Apache Tomcat. D'autres technologies nécessaires à la construction d'une telle application, si nécessaire, seront mentionnées lors de l'article.

Configuration du serveur


Comme déjà mentionné, l'application est située sur un serveur Linux, en particulier, sur Ubunty 18.04. Afin d'isoler le système des autres applications, ainsi que de simplifier le déploiement, il a été décidé de conteneuriser le processus - avec l'aide de Doker, allouez à l'application une certaine quantité de CPU et de mémoire, ouvrez la liste des ports et fermez le reste. Je ne décrirai pas en détail l'installation et la configuration du service Docker, mais je dois mentionner les points clés.

Il est supposé que toutes les commandes sont exécutées avec des privilèges d'administrateur.

Pour commencer, il fallait installer et démarrer le service correspondant en utilisant

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

Après cela, téléchargez et exécutez l'image du système d'exploitation - nous n'avons plus besoin de rien du conteneur pour le moment:

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

Le style de l'invite de saisie aurait dû changer - nous sommes maintenant dans le conteneur et pouvons travailler sans crainte pour l'environnement réel. Peut-être que certains packages auxquels nous sommes habitués sur une vraie machine dans le conteneur ne seront pas suffisants, mais la procédure pour les installer n'est pas différente de l'installation d'un package sur une vraie machine, donc je ne me concentrerai pas sur cela. Il est en outre supposé que toutes les actions sont effectuées à l'intérieur du conteneur.

Configuration du conteneur de servlets Tomcat


Donc, pour le moment, nous supposons que nous travaillons sur un système d'exploitation propre, où tout est par défaut. Pour pouvoir y héberger une application Java, vous devez installer jdk et jvm directement dessus et définir des variables d'environnement avec des chemins d'installation. En conséquence, je ne donnerai ici que des raccourcis clavier, plus en détail le processus d'installation de Java sous Linux peut être envisagé sur le réseau.

Pour installer JDK et JRE, puis régler la variable d'environnement JAVA_HOME, vous devez entrer ces commandes en conséquence:

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

Vous pouvez ensuite passer directement à l'installation de Tomcat.

L'essentiel auquel il faut faire attention - l'octroi de droits d'administrateur et de gestionnaire - par défaut, vous ne pouvez pas administrer les applications à distance, pas sur la machine locale. Sinon, tout est standard - téléchargement et installation du package et démarrage du service.

Écrire un programme


Le programme, comme mentionné ci-dessus, comprendra un serveur et une partie client. Nous considérerons brièvement le côté client; le fichier de l'éditeur et les fonctions de cryptage sont d'un intérêt maximal.

Cependant, tout d'abord.

Commençons par le côté client.

Comme prévu, il devrait donner accès à la liste des fichiers stockés sur le serveur. Étant donné que la solution était censée être bon marché, j'ai transféré la liste des fichiers sous forme de texte pour une zone de texte inactive et attaché un formulaire en bas pour sélectionner le nom de fichier à ouvrir.

image
Fig. 1 - Interface de stockage de fichiers

Lorsque vous entrez un nom de fichier dans la ligne de saisie, le fichier s'ouvre dans une autre fenêtre - dans l'éditeur. Code Java pour la page listant les fichiers:

 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); 

Le HTML correspondant est encore plus 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> 

Comme déjà mentionné ci-dessus, la page avec l'éditeur et le cryptage représente directement l'intérêt. Là, l'ouverture, la modification, le cryptage et le décryptage du fichier requis, ainsi que sa sauvegarde, ont lieu.

image
Fig. 2 - interface éditeur

Commençons par le côté serveur - c'est plus facile à comprendre. Du point de vue du serveur, l'application doit cliquer sur la clé pour accepter le texte de la fenêtre de saisie de texte, accepter le nom de fichier de la ligne correspondante et enregistrer le document résultant. En fait, c'est ce qui se passe dans la liste ci-dessous.

 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()); } } }; 
Mais au moment de l'enregistrement, le fichier est déjà crypté. Pour ce faire, les boutons «Encrypt» et «Decrypt» sont implémentés dans la partie client, qui sont utilisés pour le cryptage et le décryptage. Le cryptage s'effectue selon l'algorithme Vigenere, je vais en dire un peu plus à ce sujet séparément, mais il est montré dans les fonctions.

En conséquence, le code client ressemble à ceci:

 <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> 

Ainsi, après avoir appuyé sur le bouton de cryptage, le texte du champ de saisie est remplacé par le texte renvoyé par la fonction de cryptage - et vice versa.

La version actuelle de l'application ne détermine pas automatiquement si le texte est chiffré, donc tout texte non connecté est considéré comme chiffré.

Algorithme de chiffrement


J'ai choisi l'algorithme de cryptage Vigenere - qui est une variation du chiffre de César avec un décalage variable par mot-clé. Le code source d'entrée et la clé de longueur arbitraire. Ensuite, le i-ème membre du texte source est décalé du numéro de séquence dans l'alphabet du j-ème symbole de mot de passe, où j = i% (longueur du mot de passe). L'algorithme n'est pas le plus optimal ou le plus résistant à l'analyse, cependant, avec une longueur de mot de passe suffisante, il offre une certaine fiabilité.

Selon les matériaux de la littérature sur la cryptanalyse, le chiffre de Vigenere «brouille» les caractéristiques des fréquences d'occurrence des caractères dans le texte, mais certaines caractéristiques de l'apparence des caractères dans le texte demeurent. Le principal inconvénient du chiffrement Vigenere est que sa clé est répétée. Par conséquent, une simple cryptanalyse d'un chiffre peut être construite en deux étapes:

  1. Recherche par longueur de clé. Vous pouvez analyser la distribution de fréquence dans du texte crypté avec une décimation différente. C'est-à-dire, prenez un texte qui comprend toutes les 2 lettres du texte chiffré, puis tous les 3, etc. Dès que la distribution de fréquence des lettres est très différente de l'uniforme (par exemple, par entropie), nous pouvons parler de la longueur de clé trouvée.
  2. Cryptanalyse. Un ensemble de l chiffres César (où l est la longueur de clé trouvée), qui individuellement sont facilement fissurés.

Les tests de Friedman et Kassiski peuvent aider à déterminer la longueur de clé.

Une discussion plus détaillée du piratage de Vizhener dépasse encore le cadre de cet article.

Conclusions


Dans le cadre de cet article, un algorithme a été écrit pour écrire une application qui fournit un stockage sécurisé et la transmission de fichiers texte sur le réseau qui nécessitent un accès partagé.

Certaines des décisions exprimées ci-dessus sont controversées ou non optimales, mais la logique de base et les choses fondamentales sont indiquées.
github.com/Toxa-p07a1330/encriptedStorage

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


All Articles