Einfacher Mehrbenutzer-Texteditor mit End-to-End-Verschlüsselung

Einleitung


Informationssicherheit ist heutzutage eine der Prioritäten der IT-Branche, insbesondere wenn man bedenkt, dass man zum Abhören des Datenverkehrs in unserer Zeit praktisch kein Fachwissen benötigt - im Internet gibt es genügend Software und ausführliche Handbücher.

Daher habe ich das Problem des Schreibens eines Dienstes zum Austausch von Dateien gelöst, der so konzipiert ist, dass er so gut wie möglich vor Angriffen von "Personen in der Mitte" geschützt ist - Dateien, die über den Dienst übertragen werden, sollten nicht unverschlüsselt von der Netzwerkkarte des Endgeräts übertragen werden Die Entschlüsselung musste dementsprechend auf dem Rechner des Endempfängers erfolgen.

Werkzeugauswahl


Um die Serverseite der Anwendung zu schreiben, habe ich die Java-Programmiersprache in Kombination mit dem Wicket-Webentwicklungsframework ausgewählt. Dies ist nicht das einzige Toolkit, mit dem die Aufgaben implementiert werden konnten, aber dieser Stapel reicht aus, um sie zu lösen. Wicket bietet unter anderem eine sehr komfortable Oberfläche für die Arbeit mit Webseiten-Komponenten. Das Frontend bedeutete nicht das Vorhandensein von etwas Kompliziertem - ich hatte mir nicht die Aufgabe gestellt, eine schöne und benutzerfreundliche Oberfläche zu entwickeln, die Anwendungslogik hatte eine höhere Priorität, deshalb beschränkte ich mich auf das klassische HTML / CSS / JS-Bundle.

Der Server wird unter Ubunty 18.04 im Docker-Container ausgeführt. Die Serverkonfiguration wird jedoch im Folgenden ausführlicher beschrieben. Dabei wird der Apache Tomcat-Servlet-Container verwendet. Andere Technologien, die zum Erstellen einer solchen Anwendung erforderlich sind, werden im Artikel erwähnt.

Serverkonfiguration


Wie bereits erwähnt, befindet sich die Anwendung auf einem Linux-Server, insbesondere auf Ubunty 18.04. Um das System von anderen Anwendungen zu isolieren und die Bereitstellung zu vereinfachen, wurde beschlossen, den Prozess zu containerisieren - mit Hilfe von Doker weisen Sie der Anwendung eine bestimmte Menge an CPU und Speicher zu, öffnen Sie die Ports-Liste und schließen Sie den Rest. Ich werde die Installation und Konfiguration des Docker-Dienstes nicht im Detail beschreiben, aber ich sollte die wichtigsten Punkte erwähnen.

Es wird davon ausgegangen, dass alle Befehle mit Administratorrechten ausgeführt werden.

Zu Beginn musste der entsprechende Dienst mit Befehlen installiert und gestartet werden

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

Laden Sie anschließend das Image des Betriebssystems herunter und führen Sie es aus - wir benötigen im Moment nichts mehr aus dem Container:

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

Der Stil der Eingabeaufforderung sollte sich geändert haben - jetzt sind wir im Container und können ohne Angst vor der realen Umgebung arbeiten. Möglicherweise werden einige Pakete, die wir von einer realen Maschine im Container gewohnt sind, übersehen, aber das Verfahren zu ihrer Installation unterscheidet sich nicht von der Installation eines Pakets auf einer realen Maschine, weshalb ich mich nicht darauf konzentrieren werde. Es wird weiterhin angenommen, dass alle Aktionen innerhalb des Containers ausgeführt werden.

Tomcat-Servlet-Container konfigurieren


Im Moment gehen wir also davon aus, dass wir an einem sauberen Betriebssystem arbeiten, in dem standardmäßig alles vorhanden ist. Um eine Java-Anwendung darauf hosten zu können, müssen Sie jdk und jvm direkt darauf installieren und Umgebungsvariablen mit Installationspfaden festlegen. Dementsprechend werde ich hier nur Schlüsselbefehle geben, genauer kann der Prozess der Installation von Java unter Linux im Netzwerk betrachtet werden.

Um JDK und JRE zu installieren und anschließend die Umgebungsvariable JAVA_HOME zu optimieren, müssen Sie die folgenden Befehle entsprechend eingeben:

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

Anschließend können Sie direkt mit der Installation von Tomcat fortfahren.

Das Wichtigste, auf das Sie achten sollten - das Gewähren von Manager- und Administratorrechten - ist standardmäßig, dass Sie Anwendungen nicht remote verwalten können, nicht auf dem lokalen Computer. Ansonsten ist alles Standard - Herunterladen und Installieren des Pakets und Starten des Dienstes.

Programm schreiben


Das Programm besteht, wie oben erwähnt, aus einem Server- und einem Client-Teil. Wir werden kurz auf die Client-Seite eingehen, wobei die Editor-Datei und die Verschlüsselungsfunktionen von maximalem Interesse sind.

Aber das Wichtigste zuerst.

Beginnen wir mit der Client-Seite.

Wie geplant sollte es Zugriff auf die Liste der auf dem Server gespeicherten Dateien geben. Da die Lösung billig sein sollte, habe ich die Liste der Dateien in Textform für ein inaktives Textfeld übertragen und unten ein Formular angehängt, um den zu öffnenden Dateinamen auszuwählen.

Bild
Abb. 1 - Dateispeicherungsschnittstelle

Wenn Sie in der Eingabezeile einen Dateinamen eingeben, wird die Datei in einem anderen Fenster geöffnet - im Editor. Java-Code für die Seite, auf der die Dateien aufgelistet sind:

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

Das entsprechende HTML ist noch trivialer:

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

Wie oben bereits erwähnt, ist die Seite mit dem Editor und der Verschlüsselung direkt von Interesse. Dort erfolgt das Öffnen, Ändern, Ver- und Entschlüsseln der gewünschten Datei sowie das Speichern.

Bild
Abb. 2 - Editorschnittstelle

Beginnen wir mit der Serverseite - es ist einfacher zu verstehen. Aus Sicht des Servers sollte die Anwendung auf die Taste klicken, um Text aus dem Texteingabefenster zu übernehmen, den Dateinamen aus der entsprechenden Zeile zu übernehmen und das resultierende Dokument zu speichern. Tatsächlich geschieht dies in der folgenden Auflistung.

 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()); } } }; 
Zum Zeitpunkt des Speicherns ist die Datei jedoch bereits verschlüsselt. Dazu sind im Client-Teil die Schaltflächen „Verschlüsseln“ und „Entschlüsseln“ implementiert, die zur Ver- und Entschlüsselung verwendet werden. Die Verschlüsselung erfolgt nach dem Vigenere-Algorithmus. Ich werde noch etwas genauer darauf eingehen, aber dies wird in den Funktionen gezeigt.

Dementsprechend sieht der Client-Code folgendermaßen aus:

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

Nach dem Drücken der Verschlüsselungstaste wird der Text im Eingabefeld durch den von der Verschlüsselungsfunktion zurückgegebenen Text ersetzt - und umgekehrt.

Die aktuelle Version der Anwendung ermittelt nicht automatisch, ob der Text verschlüsselt ist. Daher wird jeder nicht verbundene Text als verschlüsselt betrachtet.

Verschlüsselungsalgorithmus


Ich habe den Vigenere-Verschlüsselungsalgorithmus gewählt - eine Variation von Caesars Chiffre mit variabler Verschiebung nach Schlüsselwort. Der eingegebene Quellcode und der Schlüssel beliebiger Länge. Dann wird das i-te Element des Quelltextes um die Folgenummer im Alphabet des j-ten Kennwortsymbols verschoben, wobei j = i% (Kennwortlänge) ist. Der Algorithmus ist nicht der optimalste oder analysenbeständigste, bietet jedoch bei ausreichender Kennwortlänge eine gewisse Zuverlässigkeit.

Gemäß Materialien aus der Literatur zur Kryptoanalyse „verwischt“ die Vigenere-Chiffre die Merkmale der Häufigkeit des Auftretens von Zeichen im Text, aber einige Merkmale des Auftretens von Zeichen im Text bleiben erhalten. Der Hauptnachteil der Vigenere-Chiffre ist, dass ihr Schlüssel wiederholt wird. Daher kann eine einfache Kryptoanalyse einer Chiffre in zwei Schritten erstellt werden:

  1. Schlüssellängensuche. Sie können die Häufigkeitsverteilung in verschlüsseltem Text mit unterschiedlicher Dezimierung analysieren. Nehmen Sie also einen Text, der jeden zweiten Buchstaben des Chiffretexts, dann jeden dritten usw. enthält. Sobald sich die Häufigkeitsverteilung der Buchstaben stark von der Uniform unterscheidet (z. B. durch Entropie), können wir über die gefundene Schlüssellänge sprechen.
  2. Kryptoanalyse. Ein Satz von l Caesar-Chiffren (wobei l die gefundene Schlüssellänge ist), die einzeln leicht geknackt werden.

Mithilfe der Tests nach Friedman und Kassiski kann die Schlüssellänge bestimmt werden.

Eine detailliertere Diskussion von Vizheners Hack würde den Rahmen dieses Artikels sprengen.

Schlussfolgerungen


Im Rahmen dieses Artikels wurde ein Algorithmus zum Schreiben einer Anwendung geschrieben, die die sichere Speicherung und Übertragung von Textdateien über das Netzwerk ermöglicht, für die ein gemeinsamer Zugriff erforderlich ist.

Einige der oben ausgesprochenen Entscheidungen sind kontrovers oder nicht optimal, aber die grundlegende Logik und die grundlegenden Dinge sind angegeben.
github.com/Toxa-p07a1330/encriptedStorage

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


All Articles