Veröffentlicht am 15. August 2024
Ich hatte in den letzten Jahren Gelegenheit, mich intensiv mit dem Thema Ende-zu-Ende-Verschlüsselung (E2EE) und ihrer Umsetzung für Webapps auseinanderzusetzen. Theoretisch, in Form meiner Masterarbeit und praktisch, durch die Implementierung in einem unserer Produkte. Ich habe das Thema wirklich rauf und runter gespielt und kann inzwischen aus Überzeugung sagen: Wenn ihr E2EE für eure Webapp erwägt, lasst es bleiben. Oder denkt zumindest noch einmal gut darüber nach.
Prinzipiell hat sich in den letzten Jahren viel getan. Mit der Web Cryptography API bieten die Browser fast alle grundlegenden technischen Voraussetzungen an, um E2EE mit JavaScript auch für Webapps umsetzen zu können, ohne dabei selbst Kern-Algorithmen implementieren zu müssen. Was derzeit noch fehlt ist Argon2, aber mit PBKDF2 und ausreichend vielen Iterationen kann man sich behelfen, bis das kommt.
Das Problem ist, dass es – egal wie man es dreht und wendet – keine Möglichkeit gibt, dem Server zu misstrauen. Die Kernidee von E2EE also, dass die Ver- und Entschlüsselung an den Enden und damit an den Clients stattfindet, geht im Kontext einer Webapp nicht auf. Denn der Client in Form eines Haufens HTML, CSS und vor allem JavaScript wird vom Server bereitgestellt.
Die eigene Gefahrenmodellierung erfordert also, überspitzt formuliert, zumindest eine Prise kognitive Dissonanz, um das ausblenden zu können.
Konkreter: Es ist ohne Weiteres möglich, eine Webapp so zu designen, dass jegliche Crypto-Operation ausschließlich clientseitig im Browser abläuft und dass Inhalte serverseitig weder zur Laufzeit noch "at rest" im Klartext abgreifbar sind. Allerdings gilt das nur solange, wie die Serverseite nicht kompromittiert ist.
Wird beispielsweise der private Schlüssel eines Users oder sein Passwort im Klartext von der Anwendung abgeschnorchelt, fällt das Kartenhaus in sich zusammen. Und dafür gibt es verschiedene Szenarien: Es genügt ein Fehler im Code oder eine mutwillige Veränderung dieses, sei es durch eigene Mitarbeiter oder Dritte bspw. in Form einer Supply-Chain-Attacke. Auch denkbar ist ein Einbruch in den den Client-Code ausliefernden Webserver und die Code-Manipulation zur Laufzeit. Das Perfide: Die Attacke könnte sogar nur temporär oder beschränkt auf spezifische User ausgeführt werden.
Das heißt im Umkehrschluss nicht, dass alle Daten im Klartext persistiert und nur noch beim Transport verschlüsselt werden sollten. Es gibt gute Argumente dafür, sie "at rest" zumindest in der Datenbank und/oder auf dem Filesystem bzw. im Blob-Storage zu verschlüsseln. Aber ob sie dann zur Laufzeit vom Server entschlüsselt und im Klartext an den Client gesendet oder erst von diesem entschlüsselt werden, bringt meiner Meinung nach aus Perspektive der Sicherheit keine relevanten Unterschiede mit sich. Denn ist der Client-Code erst einmal manipuliert, sei es über die Build-Pipeline oder durch Einbruch in den Webserver, ist das Spiel verloren. So oder so.