Hinter den Kulissen der Verschlüsselung

Drei Schlüssel (Symbolbild)

Drei Schlüssel (Symbolbild)

Verschlüsselung nutzen wir täglich: Kaum mehr eine Webseite, Nachricht oder Mail nutzt nicht mindestens ein bisschen Verschlüsselung. Und trotzdem wissen viele nicht, wie sie funktioniert. Hier ein Blick hinter die Kulissen, mit ganz wenig Mathematik.

Aus dem Themengebiet der Verschlüsselung interessieren uns rund um die IT insbesondere drei Technologien: Symmetrische und asymmetrische Verschlüsselung, die beim Besuchen jeder HTTPS-Webseite zum Tragen kommen, sowie die sogenannte Homomorphe Verschlüsselung, in die viel Hoffnung für die Zukunft für die Cloud gesteckt wird.

Wer einzelne der Mechanismen schon kennt, kann sie überfliegen oder ganz weglassen.

Wer nicht zu viel Mathematik will, darf zusätzlich die hellgrau hinterlegten Kästen auch gerne überspringen.

Einfache sym­met­rische Ver­schlüs­selung (Caesar-Chiffre)

Symmetrische Verschlüsselung

Die Lipizzaner: Schnell und bewährt

Symmetrische Verschlüsselungsmechanismen, auch als Symmetrisches Kyptosystem bekannt, ist seit über zwei Jahr­tau­sen­den dokumentiert und entsprechend gut erforscht. Hier trifft man auf hohe Geschwindigkeiten und es ist nicht mehr mit revolutionären Entwicklungen zu rechnen

Im einfachsten Fall, der links dargestellten Caesar-Chiffre, wird jeder Buchstabe beim Verschlüsseln (Encryption E) mit dem Buchstaben drei Positionen später im Alphabet verschlüsselt. Beim Entschlüsseln (Decryption D) wird die Operation umgekehrt.

Die Mathematik der Caesar-Chiffre

Diesen einfachen Vorgang können wir auch mathematisch formulieren. Der Einblick in diese Notation hilft beim Verständnis der folgenden Kryptosysteme.

  • Jeder Buchstabe wird durch seine Position im Alphabet ersetzen, beginnend mit 0. Also „A“=0, „B“=1 und so weiter bis „Z“=25. Leer-, Sonderzeichen und Umlaute lassen wir der Einfachheit mal aussen vor. Den zu verschlüsselnden Buchstaben weisen wir der Variable x zu.
  • Um wie viele Buchstaben bei der Verschlüsselung verschoben wird, wird durch den Parameter k abgebildet. Dieser Schlüssel (key, deshalb das k) war bei Caesar fix auf 3 eingestellt, lässt sich aber von 1 bis 25 frei wählen (k=0 bzw. k=26 transformiert jeden Buchstaben auf sich selbst, womit der so „verschlüsselte“ Text wieder dem Klartext entspräche, was nicht wirklich Sinn der Sache ist.
    (Solche Schlüssel, bei denen die Verschlüsselung besonders einfach zu brechen ist, nennt man übrigens „schwache Schlüssel“ (weak keys).)
  • Eine Verschlüsselungsfunktion E und eine Entschlüsselungsfunktion D, mit einem Buchstaben als Funktionsparameter und dem Schlüssel k als Steuerwert. Ek(x) = (x + k) mod 26 Dk(x) = (xk) mod 26 („mod“ ist übrigens der Modulo-Operator: „a mod b“ entspricht dem Rest nach der Division von a durch b. Zählen modulo 26 erlaubt nur Werte kleiner als 26 und würde also wie folgt ablaufen: 1, 2, 3, …, 23, 24, 25, 0, 1, 2, 3, …. Damit erreichen wir, dass nach dem Z wieder das A kommt und wir immer einen gültigen Buchstaben aus unserem limitierten Alphabet ausgeben können.)

Vor 2000 Jahren mag diese Idee bahnbrechend gewesen sein, heute ist sie trivial knackbar. Entsprechend kommen sicherere Verfahren zum Einsatz, heutzutage meist AES.

Auf modernen Prozessoren wird die Verschlüsselung und Entschlüsselung von Daten in Gigabytes pro Sekunde gemessen. Ein moderner Rechner, angeschlossen an eine Gigabit-Glasfaser, kann also problemlos seinen gesamten Netzwerkverkehr symmetrisch ver- und entschlüsseln. Dieser Mehraufwand kostet ihn dabei nur ein paar Prozent Rechenleistung.

Der Aufwand für symmetrische Verschlüsselung ist daher heute häufig vernachlässigbar. Die dahinter stehende Entwicklung hat dazu beigetragen, dass die Verschlüsselung des Netzwerkverkehrs wie beispielsweise bei HTTPS heute zum Standard geworden ist und auch Festplatten­ver­schlüsselung (Full Disk Encryption) immer beliebter wird.

Eigenschaften der symmetrischen Verschlüsselung

Heute am meisten eingesetzt wird AES. Seine beispielhaften Eigenschaften:

  • Es wird ein Schlüssel von 128 bis 256 Bit Länge verwendet (16 bis 32 Bytes lang).
  • Das Durchprobieren aller Möglichkeiten eines einzelnen Schlüssels dauert länger als das Alter des Universums, auch wenn alle heute weltweit verfügbaren Rechner sich nur dem Knacken eines einzigen Schlüssels widmen würden.
  • Es wird im Normalfall ein Block von 128 Bit (16 Bytes) aufs Mal verschlüsselt.
  • Ein einzelnes verändertes Bit verwandelt den gesamten Block in ein Chrüsimüsi (und evt. noch weitere Blöcke).
  • Das Chrüsimüsi alleine reicht nicht zuverlässig zum Erkennen der Unversehrtheit der Übermittlung. Diese muss anderweitig (z.B. mit Hashes oder digitalen Signaturen (siehe unten) sichergestellt werden.
  • Derselbe Schlüssel wird für das Ver- und Entschlüsseln eingesetzt, also muss er sicher und vertrauenswürdig (vertraulich und integritätsgeschützt) zwischen den beiden Parteien ausgetauscht werden. Dazu werden heute meist nicht bewaffnete Agenten mit angeketteten Stahlkoffern, sondern asymmetrische Verschlüsselung (siehe nächstes Kapitel) verwendet.

Asymmetrische Verschlüsselung

Der Pferdedompteur: Organisiert und koordiniert

Symmetrische Kryptosysteme sind wahnsinnig schnell; der zur Ver- und Entschlüsselung benötigte Schlüssel ist aber derselbe, also „symmetrisch“.

Dies schränkt die Flexibilität ein und hat z.B. den Nachteil, dass man nicht sowas wie ein „Telefonbuch“ mit den Schlüsseln aller Nachrichtenempfänger veröffentlichen kann. Wenn Alice also eine Nachricht an Bob schicken möchte, könnte sie zwar Bobs Schlüssel im Telefonbuch nachschlagen und die Nachricht mit seinem Schlüssel verschlüsseln. Eine Bösewichtin, Eve, die obige Nachricht aufschnappt, könnte diese dank desselben Telefonbuchs sofort entschlüsseln.

Um den Eves dieser Welt die Arbeit zu erschweren, wurden in den 1970ern asymmetrische Kryptosysteme erfunden. Dabei wird für die Verschlüsselung ein anderer Schlüssel („öffentlicher Schlüssel“, public key) eingesetzt als für die Entschlüsselung („privater Schlüssel“, private key). Wenn nun der öffentliche Schlüssel im Telefonbuch veröffentlicht wird, beisst sich Mallory die Zähne aus, da er den privaten Schlüssel nicht kennt, den er zur Entschlüsselung aber bräuchte.

Asymmetrisches Kryptosystem

Das erste öffentlich bekannte asymmetrische Kryptosystem und auch eines der bekanntesten ist das RSA-Verfahren, benannt nach den Initialen der Entdecker.

Die Mathematik von („Lehrbuch“-)RSA

Zur Illustration verwenden wir hier nur die Kernidee von RSA, so wie sie auch in vielen Einführungskursen dargestellt wird. Aufgrund der Eigenschaften von „Lehrbuch-RSA“ ist diese Kernidee meist eingebettet in weitere sicherheitsrelevante Schritte.

Die kryptographischen Operationen sind eigentlich ganz einfach: Einmal hochrechnen (potenzieren) und einmal teilen mit Rest. Damit die Nachrichten und Schlüssel nicht geknackt werden können, sind sehr grosse Zahlen involviert: N besteht aus mehreren tausend Bits, entsprechend mehreren hundert bis tausend Dezimalstellen; auch d ist fast so gross.

Klassisches Ausklammern (Anwendung des Distributiv­gesetzes) beim Hochrechnen (Potenzieren)

Wer sich noch an das Potenzrechnen erinnern mag, weiss, dass man mit diesen Potenzen auch vielfältig rechnen kann, insbesondere ein- und ausklammern. Schauen wir uns das an einem Beispiel an, das genau diese Anwendung erläutert. (Wen die Mathematik nicht interessiert, kann sie auch überlesen.)

Unser Bösewicht heisst diesmal Mallory, weil er nicht nur wie Eve zuhören kann, sondern die Nachricht auf ihrem Weg von Alice zu Bob auch verändern kann (Man-in-the-Middle-Angriff). Per Zufall hat Mallory erfahren, dass ein spezifisches Chiffrat C den Betrag repräsentiert, der auf sein Konto überwiesen werden soll und dass dieses Chiffrat nach der obigen „Lehrbuchmethode“ erzeugt wurde. Er kennt auch den öffentlichen Schlüssel des Empfängers Bob. Er kennt aber nicht den genauen Betrag, findet aber, der doppelte Betrag sei seiner Cleverness definitiv angemessener. Wie verdoppelt Mallory nun diesen geheimen Betrag?

Mallory weiss, dass dieses bestehende Chiffrat C, wie im Kasten beschrieben, als Me mod N berechnet wurde, wobei M eben der zu überweisende Betrag ist. Er möchte aber dieses Chiffrat C durch eine Ersatzchiffrat C‘ ersetzen, welches sich aus dem doppelten von M errechnet, also (2M)e mod N. Durch Ausklammern erhalten wir (2M)e mod N = 2eMemod N = (2emod N)*(Memod N).

Deterministische Veränderung einer verschlüsselten RSA-Nachricht. Das Distributivgesetz kann man auch bei Modulo-Arithmetik anwenden; auch wenn wir, wie hier, M gar nicht kennen, sondern nur C. Da die Multiplikation ein Resultat grösser als N ergeben kann, ist danach eine Modulooperation nötig (dafür könnte man die inneren Modulooperationen weglassen, zumindest bei C geht das aber nicht, da ja M unbekannt ist).

Grob gesagt bedeutet das, dass wir ohne Probleme jede Nachricht an Bob mit (2emod N) multiplizieren können und Bob nachher glaubt, der doppelte Wert sei in der Nachricht gewesen.

Diese sogenannte „Formbarkeit“ (malleability) ist — wie in diesem Beispiel — meist unerwünscht. Deshalb werden RSA-Nachrichten meist nicht so direkt und ungeschützt verschickt, sondern sie sind eingebettet in weitere Sicherheitsmassnahmen wie beispielsweise digitale Signaturen, mit denen Veränderungen bei der Übermittlung erkannt werden können.

Digitale Signatur

Etliche asymmetrische Kryptosysteme, so auch RSA, haben noch einen weiteren Trick im Köcher: Wenn man sie „falsch herum“ einsetzt, also die Verschlüsselung statt mit dem öffentlichen mit dem privaten Schlüssel durchführt, ist das Resultat eine digitale Signatur. Da sie den privaten Schlüssel voraussetzt, bestätigt sie den Besitz des privaten Schlüssels (und — wenn dieser Schlüssel mit einer Identität verknüpft ist — auch die Identität des Signierers.

Durch die zweite „falsche“ Kombination (Entschlüsselungsfunktion gepaart mit öffentlichem statt privatem Schlüssel) lässt sich die Signatur von jedem Überprüfen, der Zugriff auf das Telefonbuch hat.

(In der Praxis ist übrigens meist nicht die Verschlüsselung selbst das Komplizierte, sondern das sichere Führen und vertrauenswürdige Verteilen des „Telefonbuchs“, das sogenannte Schlüsselmanagement. Anders gesagt: Die Arbeitspferde zu koordinieren, das ist das Schwierige und die Hauptaufgabe des Dompteurs.)

Die Eigenschaften der asymmetrischen Kryptosysteme

Auch hier nehmen wir ein Beispiel, das bereits bekannte RSA:

  • Die Schlüssel sind deutlich länger als bei symmetrischen Systeme, empfohlen werden 3072 Bit oder mehr (384 Bytes). (Elliptische Kurven, eine andere Gruppe von asymmetrischen Verfahren, welche heute meist eingesetzt wird, kommen mit 256 bis 512 Bit aus und arbeiten schneller als RSA.)
  • RSA arbeitet deutlich langsamer als AES oben, weshalb asymmetrische Verfahren meist mit symmetrischen Verfahren gepaart werden (sog. Hybridsysteme).
  • Bei der Erstellung und Verwendung des Schlüssels ist einige Vorsicht geboten. Beim Einsatz einer bestehenden Programmbibliothek für diese Funktionen nimmt sich diese automatisch dem Thema an. (Man sollte sowieso nie seine eigenen kryptographischen Funktionen verwenden.)
  • Der öffentliche Schlüssel kann getrost veröffentlicht werden, Geheimhaltung ist nicht mehr nötig.
  • Die Integrität (bzw. Unversehrtheit) des übertragenen Schlüssels ist aber weiterhin ein Problem: Jemand könnte ihn unbemerkt austauschen oder einfach einen eigenen Schlüssel als den der gewünschten Person ausgeben.
  • Deshalb werden zusätzliche Massnahmen benötigt (z.B. Zertifikatsstellen als vertrauenswürdige Dritte oder Netzwerke des Vertrauens).

Homomorphe Verschlüsselung

Der Schlangenmensch: Flexibel, aber langsam.

Die Daten einfach nur verschlüsselt auf dem Cloudlaufwerk liegen zu lassen, das ist—wie eingangs gesagt—Schnee von gestern. Clouddienste nutzen wir heute viel häufiger um auch Daten zu bearbeiten. Ach, wie schön wäre es doch, wenn wir die Vorteile von Cloud Computing sorglos geniessen könnten, ohne dass der Cloudprovider unsere Daten im Klartext sehen könnte.

Genau das verspricht die Homomorphe Verschlüsselung (homomorphic encryption): (Fast) beliebige Operationen auf den verschlüsselten Daten ausführen zu können, ohne sie zwischendurch entschlüsseln zu müssen.

Einen einfachen Fall von Homomorpher Verschlüsselung haben wir oben bei der RSA-Lohnverdopplung schon gesehen: Bei (ungesicherten) „Lehrbuch“-RSA können mit dem gleichen öffentlichen Schlüssel codierte Werte miteinander multipliziert werden, ohne dass sie entschlüsselt werden können oder dass man die Werte kennen muss. Leider lässt sich das nicht auf weitere Operationen (Addition, Vergleiche auf grösser/kleiner, …) erweitern.

Es wäre schön, wenn man verschiedene Operationen auf die verschlüsselten Daten anwenden könnte. Und natürlich auch, beliebige dieser Operationen in beliebiger Reihenfolge und Tiefe hintereinander anwenden zu können. Das sind die Eigenschaften von Voll-Homomorphen Verschlüsselungssystemen (Fully Homomorphic Encryption oder kurz FHE).

Die Mathematik von FHE

(FHE, als junges, aktives Forschungsfeld zeichnet sich durch eine Vielfalt von verschiedenen Mechanismen aus, die alle anders aufgebaut sind, unterschiedliche Eigenschaften haben und zudem auf sehr viel komplexerer Mathematik aufbauen als beispielsweise RSA. Deshalb können wir hier maximal an der Oberfläche kratzen.)

Wie bei klassischer Verschlüsselung haben wir auch hier (mehrere) Schlüssel, Klartext und Chiffrate. Damit die Chiffrate aber flexibel einsetzbar sind, wird jede Zahl des Klartextes nicht wie gewohnt in nur eine, sondern stattdessen in Tausende von unterschiedliche Zahlen im Chiffrat überführt. Auch hier hängt die Magie (und die Unterschiede zwischen den verschiedenen Ansätzen) an den mathematischen Zusammenhängen zwischen den Werten.

Damit die Daten nicht zu erraten sind, sind sie alle mit einem Rauschen versetzt. Vereinfacht gesagt sind die vielen Werte dazu da, dass nach der Entschlüsselung das Rauschen durch Durchschnittsbildung sich grösstenteils wieder herausrechnet.

Wenn man mit verrauschten Daten rechnet, ist das Rauschen in den Ergebnissen meist höher als in den Ausgangswerten. Über mehrere Rechenschritte her akkumuliert sich das Rauschen, bis es zu gross wird, um noch sinnvoll rechnen zu können. Dann ist es Zeit für eine besonders aufwändige Operation, das Bootstrapping, mit der das Rauschen wieder auf das Anfangsniveau reduziert werden soll. Bootstrapping macht dasselbe, was eine Entschlüsselung und Wiederverschlüsselung erreichen würde, aber ohne die Daten wirklich zu entschlüsseln (der Cloudanbieter soll ja die unverschlüsselten Daten nie zu Gesicht bekommen).

Je nach FHE-System sind noch weitere Operationen wie Relinearisierung (relin) oder Schlüsselwechsel (key switch) nötig.

Die Eigenschaften der homomorphen Verschlüsselung

  • Mittels homomorpher Verschlüsselung können gewisse mathematische Operationen auch auf den verschlüsselten Daten vorgenommen werden (welche und wie viele hängt vom konkreten Kryptosystem ab).
  • Die Schlüssel sind Tausende bis Hunderttausende von Bytes lang.
  • Die einzelnen verschlüsselten Werte sind ebenfalls ein Mehrfaches so lang wie die Originalwerte.
  • In vielen Fällen sind die Ergebnisse nicht exakt, sondern nur ungefähr korrekt.
  • Eine Überprüfung, ob wirklich die korrekten Operationen mit den korrekten Werten durchgeführt wurden, ist schwierig bis unmöglich. D.h. die Vertraulichkeit ist grösstenteils gewährleistet, aber die Integrität ist nicht gewährleistet.

Beispiel-FHE

Hier einige Beispiele mit der beliebten Microsoft-SEAL-Bibliothek für FHE. Diese bietet drei verschiedene Kryptosysteme an, BFV, BGV und CKKS, die unterschiedliche Eigenschaften und damit Einsatzgebiete haben. Hier die Schlüsselgrössen und Beispielberechnungen für CKKS, welches sehr gerne für Machine Learning eingesetzt wird:

+---------------------------------------------------------+
| The following examples should be executed while reading |
| comments in associated files in native/examples/.       |
+---------------------------------------------------------+
| Examples                   | Source Files               |
+----------------------------+----------------------------+
| 1. BFV Basics              | 1_bfv_basics.cpp           |
| 2. Encoders                | 2_encoders.cpp             |
| 3. Levels                  | 3_levels.cpp               |
| 4. BGV Basics              | 4_bgv_basics.cpp           |
| 5. CKKS Basics             | 5_ckks_basics.cpp          |
| 6. Rotation                | 6_rotation.cpp             |
| 7. Serialization           | 7_serialization.cpp        |
| 8. Performance Test        | 8_performance.cpp          |
+----------------------------+----------------------------+
[    139 MB] Total allocation from the memory pool

> Run example (1 ~ 8) or exit (0): 7

+----------------------------------------+
|         Example: Serialization         |
+----------------------------------------+
Line 124 --> EncryptionParameters: wrote 81 bytes
Line 176 --> EncryptionParameters: data size upper bound (compr_mode_type::none): 129
             EncryptionParameters: data size upper bound (compression): 192
Line 204 --> EncryptionParameters: parms == parms2: true
Line 270 --> Serializable<RelinKeys>: wrote 309254 bytes
             RelinKeys wrote 620711 bytes
Line 314 --> Serializable<Ciphertext> (public-key): wrote 191722 bytes
             Serializable<Ciphertext> (seeded secret-key): wrote 95228 bytes
Line 378 --> Ciphertext (secret-key): wrote 117913 bytes
Line 407 --> Decrypt the loaded ciphertext
    + Expected result: (4096)

    [ 10.3500000, 10.3500000, 10.3500000, ..., 10.3500000, 10.3500000, 10.3500000 ]

    + Computed result ...... Correct.

    [ 10.3499969, 10.3499983, 10.3500004, ..., 10.3499974, 10.3500070, 10.3499935 ]

Line 453 --> Size written to stream: 63 bytes
             Size indicated in SEALHeader: 63 bytes
  • Die verwendeten Schlüssel sind 310 bzw. 620 Kilobytes gross.
  • 4096 Zahlen belegen nicht die üblichen rund 16 kByte, sondern rund 12 mal mehr (in einem Sondermodus mit reduzierten Möglichkeiten nur 6x so gross)
  • Keine der als „korrekt“ bezeichneten Resultate stimmt vollständig mit den korrekten Resultat der Multiplikation 2.3*4.5 überein. (Was für den Anwendungszweck wahrscheinlich meist ausreicht.)

Geschwindigkeitstests findet man im Untermenü 8. Testen wir alle verfügbaren Systeme mit den Standardeinstellungen. Zuerst BFV:

+-------------------------------------------+
|         Example: Performance Test         |
+-------------------------------------------+

Select a scheme (and optionally poly_modulus_degree):
  1. BFV with default degrees
  2. BFV with a custom degree
  3. CKKS with default degrees
  4. CKKS with a custom degree
  5. BGV with default degrees
  6. BGV with a custom degree
  0. Back to main menu

> Run performance test (1 ~ 6) or go back (0): 1

+--------------------------------------------------------------------------+
|         BFV Performance Test with Degrees: 4096, 8192, and 16384         |
+--------------------------------------------------------------------------+
/
| Encryption parameters :
|   scheme: BFV
|   poly_modulus_degree: 4096
|   coeff_modulus size: 109 (36 + 36 + 37) bits
|   plain_modulus: 786433
\

Generating secret/public keys: Done
Generating relinearization keys: Done [1546 microseconds]
Generating Galois keys: Done [32339 microseconds]
Running tests .......... Done

Average batch: 45 microseconds
Average unbatch: 54 microseconds
Average encrypt: 989 microseconds
Average decrypt: 278 microseconds
Average add: 12 microseconds
Average multiply: 2698 microseconds
Average multiply plain: 433 microseconds
Average square: 1971 microseconds
Average relinearize: 593 microseconds
Average rotate rows one step: 600 microseconds
Average rotate rows random: 2185 microseconds
Average rotate columns: 585 microseconds
Average serialize ciphertext: 11 microseconds
Average compressed (ZLIB) serialize ciphertext: 10236 microseconds
Average compressed (Zstandard) serialize ciphertext: 1166 microseconds

[...]
  • Eine einzelne Addition im schnellsten Modus dauert 12µs. In derselben Zeit macht derselbe Rechner einige 10’000 „normale“ Additionen. (Die langsamste Variante in den Tests ist nochmals 20x langsamer, 244µs.)
  • Ein einzelne Multiplikation im schnellsten Modus dauert je nach Weiterverwendung 433 bis 2698µs. Derselbe Rechner schafft in derselben Zeit über eine Million „normale“ Multiplikationen. (Auch hier ist der langsamste der Beispieltests nochmals 20x langsamer.)

Bei CKKS und BGV lasse ich den Output weg. Die Erkenntnisse daraus in Kürze:

  • BGV ist etwa gleich schnell wie BFV, die vollständige Multiplikation ist aber schneller: nur noch etwa doppelt so langsam wie die „nackte“ (plain) Multiplikation.
  • Die Addition bei CKKS ist gleich schnell wie bei den BFV und BGV. Die Multiplikation braucht aber nur 4 bis 8 Mal so lange wie eine Addition, also „nur“ noch ein paar 100’000x langsamer als die „normale“ Multiplikation. (Diesen relativen Geschwindigkeitsvorteil gegenüber den anderen FHEs handeln wir uns mit den oben genannten Ungenauigkeiten eiin.)

Der Aufwand für Schlüsselerzeugung, -übertragung, Speicherplatz und Rechenaufwand ist also ein Vielfaches höher, für gewisse Operationen mehrere hunderttausend Mal. Dies wird sich sicher in den nächsten Jahren noch weiter verbessern. Trotzdem ist anzunehmen, dass FHE nicht als Allerweltslösung taugen wird sondern nur selektiv sinnvoll eingesetzt werden wird.

Mehr zu Sicherheit


Bleibe auf dem Laufenden!

Erhalte eine Mail bei jedem neuen Artikel von mir.

Ca. 1-2 Mails pro Monat, kein Spam.

Folge mir im Fediverse


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.


Webapps