Verschlüsselung auf Anwendungsebene: Memorystore for Redis

In diesem Dokument erhalten Redis-Nutzer auf Google Cloud einen Überblick über Verschlüsselungsmethoden, mit denen sich böswillige Angriffe abwehren lassen, ohne die Geschwindigkeit und Flexibilität von Redis zu beeinträchtigen. Insbesondere wird in diesem Dokument beschrieben, wie Sie sensible Daten schützen, indem Sie eine Umschlagverschlüsselung mit Memorystore und Cloud Key Management Service (Cloud KMS) implementieren: Ein Sicherheitsansatz, der nur minimale Auswirkungen auf die Leistung und Flexibilität bei der Rotation von Schlüsseln bietet.

In der Anleitung, die diesem Dokument zugeordnet ist, wenden Sie diesen Ansatz an, indem Sie eine Anwendung erstellen, die mit der Cloud Key Management Service API kommuniziert, um Inhalte zu verschlüsseln, die in einem Memorystore for Redis-Speicher gespeichert werden.

Dieses Dokument richtet sich an Anwendungsentwickler und Sicherheitsfachleute. Es wird vorausgesetzt, dass Sie Grundkenntnisse in der Programmierung und Sicherheitskonzepte von Java haben. Alle Code-Snippets in diesem Dokument sind in Java geschrieben.

Memorystore for Redis

Memorystore for Redis bietet einen vollständig verwalteten Dienst, der auf dem Redis-internen Datenspeicher basiert, um Anwendungscaches für den Datenzugriff unter einer Millisekunde zu erstellen.

Das Kernsicherheitsmodell von Redis beruht auf der Beibehaltung der Instanzen innerhalb einer Umgebung, die nur vertrauenswürdige Clients direkt aufrufen können. Dieses Sicherheitsmodell implementiert nur grundlegende Sicherheitskonzepte. Dazu gehört beispielsweise das Sperren des Zugriffs auf Netzwerkschnittstellen, die Authentifizierung der Basisauthentifizierung und die Verschleierung von Befehlsnamen. Private IP-Adressen und die rollenbasierte Zugriffssteuerung (Identity and Access Management, IAM) bieten zusätzlichen Schutz und Schutz für Redis-Instanzen. Standardinstanzen mit hoher Verfügbarkeit werden immer zonenübergreifend repliziert und bieten SLAs mit 99,9 % Verfügbarkeit.

Redis-Anwendungsfälle für Datenzugriff in weniger als einer Millisekunde

Redis bietet viele Anwendungen für moderne Sicherheitsarchitekturen. Für viele Unternehmen reicht eine grundlegende Sicherheitsarchitektur aus, um die meisten Sicherheitsanforderungen zu erfüllen. Einige Branchen haben jedoch höhere Sicherheitsanforderungen.

Betrachten Sie beispielsweise eine Anwendung für maschinelles Lernen in der Finanzdienstleistungsbranche. Finanztransaktionen enthalten Kontodetails, die für die Ableitung zu niedriger Latenz im Cache gespeichert werden müssen. Aufgrund der Anforderungen mit niedriger Latenz können die Daten nicht tokenisiert, maskiert oder anonymisiert werden. Daher ist es wichtig, zusätzliche Sicherheitsebenen zu erstellen, um Daten zu schützen und das Risiko einer Bedrohung durch unwahrscheinliche Angriffsvektoren zu verringern.

Überlegungen zur Verwendung der Envelope-Verschlüsselung

Das allgemeine Konzept der Umschlagverschlüsselung besteht darin, die Verschlüsselung in Ebenen zu kombinieren. Bei diesem Vorgang verschlüsseln Sie Daten mit einem oder mehreren Datenverschlüsselungsschlüsseln (Data Encryption Keys, DEKs). Anschließend verschlüsseln Sie die DEKs (nicht die Rohdaten) mit einem Schlüsselverschlüsselungsschlüssel (KEK). Im folgenden Diagramm wird dieser Vorgang veranschaulicht:

Architektur der Umschlagverschlüsselung

Die Umschlagverschlüsselung vereinfacht den Datenverwaltungsprozess im großen Maßstab. Außerdem verringert sich der Aufwand für die Schlüsselrotation, da Sie nur den KEK, nicht den DEK rotieren. Bei diesem Ansatz verschlüsseln Sie die Rohdaten nur einmal mit dem DEK. Die Neuverschlüsselung des DEK führt zu einer deutlich geringeren Verarbeitungslast als die Neuverschlüsselung der gesamten Inhalte der Redis-Datenbank. Wenn Sie den DEK-Schlüssel während eines Rotationszeitraums aktualisieren, müssen Sie das gesamte verschlüsselte Dataset noch einmal verarbeiten. Bei der Umschlagverschlüsselung verwenden Sie jedoch die Best Practices der Schlüsselrotation und senken den Aufwand für die erneute Verarbeitung eines gesamten Korpus verschlüsselter Daten.

In den folgenden Abschnitten wird erläutert, wie Sie die Verschlüsselung mit Cloud KMS, der Tink-Bibliothek und Speicherstore implementieren. Die Codebeispiele in diesem Dokument stammen aus dem Git-Repository für die Anwendung tinkCryptoHelper.

Schlüsselverwaltung mit Tink und Redis

Für die Verschlüsselung auf Anwendungsebene bietet Google den Cloud Key Management Service (Cloud KMS), mit dem Sie kryptografische Schlüssel erstellen, importieren und verwalten sowie kryptografische Operationen in einem zentralen Cloud-Service durchführen können. Sie können diese Schlüssel verwenden, um Verschlüsselungsvorgänge durchzuführen, indem Sie Cloud KMS direkt, Cloud HSM oder Cloud External Key Manager (Cloud EKM) verwenden oder indem Sie vom Kunden verwaltete Verschlüsselungsschlüssel-Integrationen (CMEK) innerhalb anderer Google Cloud-Services verwenden.

Cloud KMS basiert auf einer hierarchischen Ebene, die aus Schlüsselbunden mit Schlüsseln besteht. Ein Schlüssel kann mehrere Versionen haben, von denen eine über das primary-Attribut verfügt. Schlüsselbunde werden einer Region zugewiesen und sind Teil eines Projekts, für das sie Berechtigungen übernehmen.

Ein Schlüssel wird mithilfe eines URI behandelt, der diese Hierarchie widerspiegelt. Beispiel:

gcp-kms://projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING_ID/cryptoKeys/KEY_ID

Die Variablen in diesem Muster sind folgendermaßen definiert:

Wenn Sie zusätzliche Complianceanforderungen haben, bietet Google Cloud auch Cloud HSM, einen in der Cloud gehosteten Hardwaredienstdienst (HSM), z. B. wenn Sie kryptografische Vorgänge innerhalb einer nach FIPS 140-2 Level 3 zertifizierten Hardware-Umgebung durchführen müssen.

Standardmäßig verschlüsselt Google Cloud inaktive Kundeninhalte (mit einigen wenigen Ausnahmen) mithilfe der Envelope-Verschlüsselung zusammen mit einem internen Key Management Service (KMS).

Tink-Bibliothek

Bei dem in diesem Dokument beschriebenen Ansatz wird die Verschlüsselung mit der Tink-Bibliothek und Cloud KMS verwaltet. Tink ist eine plattformübergreifende mehrsprachige Bibliothek mit kryptografischen APIs, die von einer Gruppe von Kryptografierern und Sicherheitstechnikern bei Google geschrieben wurde. Das Ziel von Tink besteht darin, eine sichere API bereitzustellen, die einfach zu bedienen ist und häufige kryptografische Schwierigkeiten reduziert. Wenn Sie weitere Details zu den Designaspekten von Tink erfahren möchten, können Sie sich dieses Real World Crypto Talk und die zugehörigen Folien ansehen.

Der Lebenszyklus der Schlüsselverwaltung

Bevor Sie mit dem Verschlüsseln und Entschlüsseln von Arbeitsdaten beginnen, müssen Sie zuerst Komponenten des Schlüsselverwaltungslebenszyklus für die Redis-Clientanwendung konfigurieren. Das folgende Diagramm gibt einen Überblick über die Beziehung zwischen Schlüsseln und Daten.

Zu den Komponenten des KMS-Lebenszyklus gehören Schlüssel und IAM-Rollen.

In diesem Lebenszyklus werden alle in der Redis-Datenbank gespeicherten Daten mit einem einzigen DEK verschlüsselt. Der erste Schritt im Prozess besteht darin, einen DEK zu generieren. Tink organisiert Sammlungen von Schlüsseln in Schlüsselsets, die dann wie im folgenden Codebeispiel von einem KeysetHandle-Objekt umschlossen werden:

KeysetHandle k = KeysetHandle.generateNew
    (AeadKeyTemplates.createAesGcmKeyTemplate(256 / 8));

Alle Schlüssel in einem Schlüsselsatz entsprechen einem einzelnen primitiven Vorgang. Als Best Practice wird in diesem Dokument und der zugehörigen Anleitung AES im Galois/Counter Mode (GCM) verwendet.

Um den Zugriff auf den DEK zu ermöglichen, müssen Sie den DEK speichern. In diesem Dokument verwenden Sie Cloud KMS zum Verschlüsseln des DEK mit einem KEK. In der Tink-Bibliothek ist der von Cloud KMS verwendete KEK ein Schlüssel mit einer bestimmten Version, der zu einem bestimmten Schlüsselbund gehört, sich an einem bestimmten Standort befindet und mit einem bestimmten Projekt verknüpft ist, wie weiter oben in der URI in diesem Dokument beschrieben.

Es empfiehlt sich, den Cloud KMS KEK in einem Projekt zu erstellen, das von anderen Google Cloud-Ressourcen isoliert ist. Mit dieser Vorgehensweise können Sie dafür sorgen, dass nur Nutzer oder Dienstkonten mit der IAM-Inhaber-Rolle für dieses Projekt auf die Schlüssel in Cloud KMS zugreifen und Daten entschlüsseln können. Best Practices zum Verwalten von Berechtigungen und Zugriff finden Sie unter Aufgabentrennung.

Um Zugriff auf den KEK in Cloud KMS zu gewähren, verwenden Sie ein Dienstkonto, das Berechtigungen zum Verschlüsseln und Entschlüsseln am KEK hat. Weitere Informationen finden Sie unter Passende IAM-Rollen auswählen. Sie können die Anmeldedaten für das Dienstkonto entweder über die Google Cloud Console oder über Cloud Shell mit dem Befehl gcloud iam service-accounts keys create abrufen. Für den Zugriff auf den KMS mit Tink werden die Anmeldedaten des Dienstkontos (kmsCredentialsFilename) bereitgestellt, um einen Cloud-KMS-Client zu instanziieren, wie das folgende Codebeispiel zeigt:

KmsClient kmsClient = new
    GcpKmsClient().withCredentials(kmsCredentialsFilename);

In KMS wird das Keyset, das den DEK mit dem KEK verschlüsselt hat, in eine Datei geschrieben (in diesem Fall in eine JSON-Datei auf dem lokalen Dateisystem):

k.write(JsonKeysetWriter.withFile(new File(keysetFilename)),
    kmsClient.getAead(keyResourceIdUri));

Redis-Daten verschlüsseln und entschlüsseln

Nachdem Sie den Schlüsselverwaltungszyklus initiiert und den DEK sicher gespeichert haben, kann die Redis-Clientanwendung den DEK zum Verschlüsseln und Entschlüsseln von Redis-Werten verwenden, wie das folgende Diagramm zeigt.

Memorystore for Redis verwendet den DEK, um kryptografische Prozesse mit Daten durchzuführen.

So funktioniert Tink, um Verschlüsselungs- und Entschlüsselungsvorgänge mit dem gespeicherten DEK so auszuführen:

  1. Die Primitive Aead wird aus dem KeysetHandle-Objekt abgerufen.
  2. Die Methode encrypt (oder decrypt) wird ausgeführt, wobei die zu verschlüsselnden Daten bereitgestellt werden.

Das folgende Snippet zeigt den Code, der Verschlüsselungs- und Entschlüsselungsfunktionen ausführt:

Aead aead = k.getPrimitive(Aead.class);
byte[] ciphertext = aead.encrypt(clearText.getBytes("UTF-8"), aa);
String decryptedText = new String(aead.decrypt(ciphertext, aa), "UTF-8");

Die verknüpften Daten (aa), die an die Methode Aead.encrypt (authentifizierte Verschlüsselung mit zugehörigen Daten) übergeben werden, binden die Verschlüsselung an einen Kontext (z. B. die ID eines Datensatzes). Verknüpfte Daten können als salt-Argument angesehen werden, das bei der Passwortverschlüsselung verwendet wird. Dieser Ansatz trägt dazu bei, die Authentizität und Integrität der Daten zu gewährleisten.

Für diese Redis-Clientanwendung können die zugehörigen Daten in dem Modul als Konstante definiert werden, die Verschlüsselung und Entschlüsselung durchführt. Wenn die Redis-Clientanwendung gestartet wird, muss der DEK mit dem KEK aus Cloud KMS geladen und entschlüsselt werden, um die Daten zu verschlüsseln, die in Redis gespeichert werden sollen.

Mit Tink laden Sie das DEK-Keyset, das während des Initialisierungsvorgangs erstellt und gespeichert wurde. Tink verarbeitet die Entschlüsselung mithilfe des KEK, der über den Schlüssel-URI von Cloud KMS bereitgestellt wird, wie das folgende Code-Snippet zeigt:

File keyset = new File(keysetFilename);
if (keyset.exists()) {
    KeysetHandle k =
        KeysetHandle.read(JsonKeysetReader.withFile(keyset),
            kmsClient.getAead(keyResourceIdUri));

    Aead aead = k.getPrimitive(Aead.class);
    String decText = new String(aead.decrypt(ciphertext, aa));
}

Verschlüsselung mit Redis zusammenführen

Im vorherigen Abschnitt wird die Verwendung von Tink zum Verschlüsseln und Entschlüsseln von Daten ohne Verweis auf die Redis-Datenbank beschrieben. Die Integration der Verschlüsselung in Redis ist unkompliziert. In der Anleitung zu diesem Dokument verwenden Sie Jedis als Java-Clientbibliothek. Sie können Schlüssel/Wert-Paare mit nur wenigen Codezeilen übergeben und verschlüsseln. Sie können diesen Code auf beliebige Datenspeicherung anwenden. Das folgende Codebeispiel zeigt, wie dieser Code in Redis angewendet wird:

void set(Iterator<Map.Entry<String, String>> kvs) throws
             NullPointerException, GeneralSecurityException, IOException {
    try (Pipeline p = jedisPool.getResource().pipelined()) {
      while (kvs.hasNext()) {
        Map.Entry<String, String> r = kvs.next();
        p.set(r.getKey(), cryptoHelper.encrypt(r.getValue()));
      }
      p.sync();
    }
}

List<String> get(String... keys) throws UnsupportedEncodingException, NullPointerException,
GeneralSecurityException, IOException {
    Jedis j = jedisPool.getResource();
    List<String> values = j.mget(keys);
    List<String> l = new ArrayList<String>();
    for (String v : values) {
      l.add(cryptoHelper.decrypt(v));
    }
    return l;
}

Die Schlüssel bleiben in Klartext und nur die Werte werden mit dem DEK verschlüsselt. Daher ist es wichtig, keine vertraulichen Informationen in die Redis-Schlüssel einzubetten. Ein Nachteil bei der Verschlüsselung der Werte ist, dass eine verschachtelte Wertstruktur nicht über native Redis-Funktionen abgefragt werden kann. Sie müssen diese Eigenschaft berücksichtigen, wenn Sie eine Datenzugriffsebene entwerfen.

Nächste Schritte