Signierte Cookies verwenden

Diese Seite bietet eine Übersicht über signierte Cookies und eine Anleitung für deren Verwendung mit Cloud CDN. Signierte Cookies ermöglichen zeitlich begrenzten Ressourcenzugriff auf eine Reihe von Dateien, unabhängig davon, ob die Nutzer Google-Konten haben.

Signierte Cookies sind eine Alternative zu signierten URLs. Sie schützen den Zugriff, wenn das separate Signieren von Dutzenden oder Hunderten von URLs für jeden Nutzer in Ihrer Anwendung nicht möglich ist.

Mit signierten Cookies haben Sie folgende Möglichkeiten:

  • Einen Nutzer autorisieren und ihm ein zeitlich begrenztes Token für den Zugriff auf Ihre geschützten Inhalte zur Verfügung stellen (anstatt jede URL zu signieren).
  • Den Zugriff des Nutzers auf ein bestimmtes URL-Präfix wie https://media.example.com/videos/ festlegen und dem autorisierten Nutzer nur Zugriff auf geschützte Inhalte innerhalb dieses URL-Präfixes gewähren.
  • Ihre URLs und Medienmanifeste unverändert lassen, die Paketerstellung vereinfachen und die Cache-Fähigkeit verbessern.

Wenn Sie den Zugriff auf bestimmte URLs beschränken möchten, können Sie signierte URLs verwenden.

Hinweise

Führen Sie die folgenden Schritte aus, bevor Sie signierte Cookies verwenden:

  • Achten Sie darauf, dass Cloud CDN aktiviert ist. Eine Anleitung hierzu finden Sie unter Cloud CDN verwenden. Sie können signierte Cookies in einem Backend konfigurieren, bevor Sie Cloud CDN aktivieren. Dies wird jedoch erst wirksam, wenn Cloud CDN aktiviert ist.

  • Aktualisieren Sie bei Bedarf auf die neueste Version der Google Cloud CLI:

    gcloud components update
    

Einen Überblick finden Sie unter Signierte URLs und signierte Cookies.

Schlüssel für signierte Anfragen konfigurieren

Schlüssel für signierte URLs oder signierte Cookies werden in mehreren Schritten erstellt, die in den folgenden Abschnitten beschrieben werden.

Sicherheitsaspekte

In den folgenden Fällen validiert Cloud CDN Anfragen nicht:

  • Die Anfrage wurde nicht signiert.
  • Bei dem Backend-Dienst oder Backend-Bucket für die Anfrage ist Cloud CDN nicht aktiviert.

Signierte Anfragen müssen immer am Ursprung validiert werden, bevor die Antwort zurückgegeben wird. Dies liegt daran, dass Ursprünge zur Bereitstellung einer Mischung aus signierten und unsignierten Inhalten verwendet werden können und ein Client direkt auf den Ursprung zugreifen kann.

  • Cloud CDN blockiert keine Anfragen ohne einen Abfrageparameter Signature oder HTTP-Cookie Cloud-CDN-Cookie. Anfragen mit ungültigen oder auf sonstige Weise fehlerhaften Anfrageparametern werden abgelehnt.
  • Wenn die Anwendung eine ungültige Signatur erkennt, prüfen Sie, ob Ihre Anwendung mit dem Antwortcode HTTP 403 (Unauthorized) antwortet. HTTP 403-Antwortcodes können nicht zwischengespeichert werden.
  • Antworten auf signierte und nicht signierte Anfragen werden separat gespeichert. Eine erfolgreiche Antwort auf eine gültige signierte Anfrage wird daher nie zum Bereitstellen einer unsignierten Anfrage verwendet.
  • Wenn Ihre Anwendung einen cachefähigen Antwortcode an eine ungültige Anfrage sendet, werden gültige zukünftige Anfragen eventuell fälschlicherweise abgelehnt.

Achten Sie bei Cloud Storage-Back-Ends darauf, den öffentlichen Zugriff zu entfernen, damit Cloud Storage Anfragen ablehnen kann, denen eine gültige Signatur fehlt.

In der folgenden Tabelle ist die Funktionsweise zusammengefasst.

Anfrage hat Signatur Cache-Treffer Verhalten
Nein Nein An Backend-Ursprung weiterleiten
Nein Ja Aus dem Cache bereitstellen
Ja Nein Signatur validieren. Wenn gültig, an den Backend-Ursprung weiterleiten.
Ja Ja Signatur validieren. Falls gültig, aus dem Cache bereitstellen.

Schlüssel für signierte Anfragen erstellen

Wenn von Cloud CDN signierte URLs und signierte Cookies unterstützt werden sollen, erstellen Sie einen oder mehrere Schlüssel in einem Backend-Dienst und/oder Backend-Bucket mit aktiviertem Cloud CDN.

Für jeden Backend-Dienst oder -Bucket können Sie entsprechend Ihren Sicherheitsanforderungen Schlüssel erstellen und löschen. Für jedes Backend können gleichzeitig bis zu drei Schlüssel konfiguriert sein. Wir empfehlen, Schlüssel regelmäßig zu rotieren. Löschen Sie dazu den ältesten, fügen einen neuen hinzu und verwenden den neuen Schlüssel beim Signieren von URLs und Cookies.

Sie können denselben Schlüsselnamen in mehreren Backend-Diensten und -Buckets verwenden, da jeder Schlüsselsatz von den anderen unabhängig ist. Schlüsselnamen können bis zu 63 Zeichen lang sein. Verwenden Sie zum Benennen Ihrer Schlüssel die Zeichen A–Z, a–z, 0–9, _ (Unterstrich) und - (Bindestrich).

Achten Sie beim Erstellen von Schlüsseln darauf, diese sicher aufzubewahren, da jeder, der einen Ihrer Schlüssel hat, signierte URLs oder signierte Cookies erstellen kann, die Cloud CDN akzeptiert, bis der Schlüssel aus Cloud CDN gelöscht wird. Die Schlüssel werden auf dem Computer gespeichert, auf dem Sie die signierten URLs oder Cookies generieren. Cloud CDN speichert auch die Schlüssel, um Anfragesignaturen zu prüfen.

Damit die Schlüssel geheim bleiben, werden die Schlüssel/Wert-Paare nicht in Antworten auf API-Anfragen aufgenommen. Wenn Sie einen Schlüssel verlieren, müssen Sie einen neuen erstellen.

So erstellen Sie einen Schlüssel für eine signierte Anfrage:

Console

  1. Rufen Sie in der Google Cloud Console die Seite Cloud CDN auf.

    Zu Cloud CDN

  2. Klicken Sie auf den Namen des Ursprungs, dem Sie den Schlüssel hinzufügen möchten.
  3. Klicken Sie auf der Seite Details zum Abflugort auf die Schaltfläche Bearbeiten.
  4. Klicken Sie im Abschnitt Ursprungsgrundlagen auf Weiter, um den Abschnitt Host- und Pfadregeln zu öffnen.
  5. Klicken Sie im Bereich Host- und Pfadregeln auf Weiter, um den Bereich Cache-Leistung zu öffnen.
  6. Wählen Sie im Bereich Eingeschränkte Inhalte die Option Zugriff mit signierten URLs und signierten Cookies einschränken aus.
  7. Klicken Sie auf Signaturschlüssel hinzufügen.

    1. Geben Sie einen eindeutigen Namen für den neuen Signaturschlüssel an.
    2. Wählen Sie im Abschnitt Methode zur Schlüsselerstellung die Option Automatisch generieren aus. Alternativ können Sie auf Selbst eingeben klicken und dann einen Schlüssel/Wert für das Signieren angeben.

      Kopieren Sie für die erste Option das automatisch generierte Schlüssel/Wert-Paar in eine private Datei, in der Sie signierte URLs erstellen können.

    3. Klicken Sie auf Fertig.

    4. Geben Sie im Bereich Höchstalter der Cache-Einträge einen Wert ein und wählen Sie eine Zeiteinheit aus.

  8. Klicken Sie auf Fertig.

gcloud

Das gcloud-Befehlszeilentool liest Schlüssel aus einer von Ihnen angegebenen lokalen Datei. Die Schlüsseldatei muss erstellt werden, indem stark zufällige 128 Bit generiert und mit base64 codiert werden. Anschließend muss das Zeichen „+“ durch „-“ und das Zeichen „/“ durch „_“ ersetzt werden. Weitere Informationen finden Sie unter RFC 4648. Es ist besonders wichtig, dass der Schlüssel stark zufällig ist. Auf einem UNIX-ähnlichen System können Sie einen stark zufälligen Schlüssel generieren und mit dem folgenden Befehl in der Schlüsseldatei speichern:

head -c 16 /dev/urandom | base64 | tr +/ -_ > KEY_FILE_NAME

So fügen Sie den Schlüssel zu einem Backend-Dienst hinzu:

gcloud compute backend-services \
   add-signed-url-key BACKEND_NAME \
   --key-name KEY_NAME \
   --key-file KEY_FILE_NAME

So fügen Sie den Schlüssel zu einem Backend-Bucket hinzu:

gcloud compute backend-buckets \
   add-signed-url-key BACKEND_NAME \
   --key-name KEY_NAME \
   --key-file KEY_FILE_NAME

Cloud Storage-Berechtigungen konfigurieren

Wenn Sie Cloud Storage verwenden und eingeschränkt haben, wer die Objekte lesen kann, müssen Sie Cloud CDN zum Lesen der Objekte berechtigen. Fügen Sie dazu das Cloud CDN-Dienstkonto den ACLs von Cloud Storage hinzu.

Sie müssen das Dienstkonto nicht erstellen. Das Dienstkonto wird automatisch erstellt, wenn Sie einem Backend-Bucket eines Projekts zum ersten Mal einen Schlüssel hinzufügen.

Fügen Sie mindestens einen Schlüssel zu einem Backend-Bucket in Ihrem Projekt hinzu, bevor Sie den folgenden Befehl ausführen. Ansonsten verursacht der Befehl einen Fehler, da das Dienstkonto von Cloud CDN mit Cache-Füllung erst erstellt wird, wenn Sie mindestens einen Schlüssel für das Projekt hinzufügen. Ersetzen Sie PROJECT_NUM durch Ihre Projektnummer und BUCKET durch Ihren Storage-Bucket.

gsutil iam ch \
  serviceAccount:service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer \
  gs://BUCKET

Das Cloud CDN-Dienstkonto service-PROJECT_NUM@cloud-cdn-fill.iam.gserviceaccount.com ist nicht in der Liste der Dienstkonten in Ihrem Projekt enthalten. Dies liegt daran, dass das Cloud CDN-Dienstkonto zu Cloud CDN gehört und nicht zu Ihrem Projekt.

Weitere Informationen zu Projektnummern finden Sie in der Hilfe zur Google Cloud Console unter Projekt-ID und Projektnummer finden.

Maximale Cache-Zeit anpassen

Cloud CDN speichert Antworten für signierte Anfragen unabhängig vom Cache-Control-Header des Back-Ends im Cache. Die maximale Zeit, für die Antworten ohne erneute Validierung im Cache gespeichert werden können, wird durch das Flag signed-url-cache-max-age festgelegt. Sie beträgt standardmäßig eine Stunde und kann wie hier gezeigt geändert werden.

Führen Sie einen der folgenden Befehle aus, um die maximale Cache-Zeit für einen Backend-Dienst oder -Bucket festzulegen:

gcloud compute backend-services update BACKEND_NAME
  --signed-url-cache-max-age MAX_AGE
gcloud compute backend-buckets update BACKEND_NAME
  --signed-url-cache-max-age MAX_AGE

Schlüsselnamen für signierte Anfragen auflisten

Führen Sie einen der folgenden Befehle aus, um die Schlüssel in einem Backend-Dienst oder -Bucket aufzulisten:

gcloud compute backend-services describe BACKEND_NAME
gcloud compute backend-buckets describe BACKEND_NAME

Schlüssel für signierte Anfragen löschen

Wenn mit einem bestimmten Schlüssel signierte URLs nicht mehr akzeptiert werden sollen, führen Sie einen der folgenden Befehle aus, um diesen Schlüssel aus dem Backend-Dienst oder Backend-Bucket zu löschen:

gcloud compute backend-services \
   delete-signed-url-key BACKEND_NAME --key-name KEY_NAME
gcloud compute backend-buckets \
   delete-signed-url-key BACKEND_NAME --key-name KEY_NAME

Richtlinie erstellen

Richtlinien für signierte Cookies bestehen aus key-value-Paaren (durch das Zeichen „:“ getrennt, ähnlich den Abfrageparametern, die in einer signierten URL verwendet werden. Beispiele finden Sie unter Cookies für Nutzer ausgeben.

Richtlinien stellen die Parameter dar, für die eine Anfrage gültig ist. Richtlinien werden mithilfe eines Hash-basierten Nachrichten-Authentifizierungscodes (HMAC) signiert, der von Cloud CDN bei jeder Anfrage validiert wird.

Richtlinienformat und -felder definieren

Es gibt vier Pflichtfelder, die in der folgenden Reihenfolge definiert werden müssen:

  • URLPrefix
  • Expires
  • KeyName
  • Signature

Bei den key-value-Paaren in einer Richtlinie für signierte Cookies wird zwischen Groß- und Kleinschreibung unterschieden.

URLPrefix

URLPrefix gibt ein URL-sicheres, base64-codiertes URL-Präfix an, das alle Pfade umfasst, für die die Signatur gültig sein soll.

Ein URLPrefix codiert ein Schema (entweder http:// oder https://), einen FQDN und einen optionalen Pfad. Den Pfad mit einem / zu beenden, ist optional, wird jedoch empfohlen. Das Präfix darf keine Suchparameter oder Fragmente wie ? oder # enthalten.

Zum Beispiel ordnet https://media.example.com/videos Anfragen an beide folgenden Plattformen zu:

  • https://media.example.com/videos?video_id=138183&user_id=138138
  • https://media.example.com/videos/137138595?quality=low

Der Pfad des Präfixes wird als Text-Teilstring und nicht als Verzeichnispfad verwendet. Das Präfix https://example.com/data gewährt beispielsweise Zugriff auf die beiden folgenden Speicherorte:

  • /data/file1
  • /database

Wir empfehlen, allen Präfixen am Ende / hinzuzufügen, um diesen Fehler zu vermeiden, außer Sie lassen das Präfix absichtlich auf einen unvollständigen Dateinamen wie https://media.example.com/videos/123 enden, um Zugriff auf Folgendes zu gewähren:

  • /videos/123_chunk1
  • /videos/123_chunk2
  • /videos/123_chunkN

Wenn die angeforderte URL nicht mit URLPrefix übereinstimmt, lehnt Cloud CDN die Anfrage ab und gibt den HTTP-Fehler HTTP 403 an den Client zurück.

Gültig bis

Expires muss ein Unix-Zeitstempel sein (die Anzahl der Sekunden seit dem 01. Januar 1970).

KeyName

KeyName ist der Schlüsselname für einen Schlüssel, der aus dem Backend-Bucket oder -Dienst erstellt wird. Bei Schlüsselnamen wird zwischen Groß- und Kleinschreibung unterschieden.

Signatur

Signature ist die URL-sichere base64-codierte HMAC-SHA-1-Signatur der Felder, aus denen die Cookierichtlinie besteht. Dies wird bei jeder Anfrage geprüft. Anfragen mit einer ungültigen Signatur werden mit dem Fehler HTTP 403 abgelehnt.

Signierte Cookies programmatisch erstellen

Die folgenden Codebeispiele zeigen, wie signierte Cookies programmatisch erstellt werden.

Einfach loslegen (Go)

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

// signCookie creates a signed cookie for an endpoint served by Cloud CDN.
//
// - urlPrefix must start with "https://" and should include the path prefix
// for which the cookie will authorize access to.
// - key should be in raw form (not base64url-encoded) which is
// 16-bytes long.
// - keyName must match a key added to the backend service or bucket.
func signCookie(urlPrefix, keyName string, key []byte, expiration time.Time) (string, error) {
	encodedURLPrefix := base64.URLEncoding.EncodeToString([]byte(urlPrefix))
	input := fmt.Sprintf("URLPrefix=%s:Expires=%d:KeyName=%s",
		encodedURLPrefix, expiration.Unix(), keyName)

	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(input))
	sig := base64.URLEncoding.EncodeToString(mac.Sum(nil))

	signedValue := fmt.Sprintf("%s:Signature=%s",
		input,
		sig,
	)

	return signedValue, nil
}

Java

import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SignedCookies {

  public static void main(String[] args) throws Exception {
    // TODO(developer): Replace these variables before running the sample.

    // The name of the signing key must match a key added to the back end bucket or service.
    String keyName = "YOUR-KEY-NAME";
    // Path to the URL signing key uploaded to the backend service/bucket.
    String keyPath = "/path/to/key";
    // The Unix timestamp that the signed URL expires.
    long expirationTime = ZonedDateTime.now().plusDays(1).toEpochSecond();
    // URL prefix to sign as a string. URL prefix must start with either "http://" or "https://"
    // and must not include query parameters.
    String urlPrefix = "https://media.example.com/videos/";

    // Read the key as a base64 url-safe encoded string, then convert to byte array.
    // Key used in signing must be in raw form (not base64url-encoded).
    String base64String = new String(Files.readAllBytes(Paths.get(keyPath)),
        StandardCharsets.UTF_8);
    byte[] keyBytes = Base64.getUrlDecoder().decode(base64String);

    // Create signed cookie from policy.
    String signedCookie = signCookie(urlPrefix, keyBytes, keyName, expirationTime);
    System.out.println(signedCookie);
  }

  // Creates a signed cookie for the specified policy.
  public static String signCookie(String urlPrefix, byte[] key, String keyName,
      long expirationTime)
      throws InvalidKeyException, NoSuchAlgorithmException {

    // Validate input URL prefix.
    try {
      URL validatedUrlPrefix = new URL(urlPrefix);
      if (!validatedUrlPrefix.getProtocol().startsWith("http")) {
        throw new IllegalArgumentException(
            "urlPrefix must start with either http:// or https://: " + urlPrefix);
      }
      if (validatedUrlPrefix.getQuery() != null) {
        throw new IllegalArgumentException("urlPrefix must not include query params: " + urlPrefix);
      }
    } catch (MalformedURLException e) {
      throw new IllegalArgumentException(
          "urlPrefix malformed: " + urlPrefix);
    }

    String encodedUrlPrefix = Base64.getUrlEncoder().encodeToString(urlPrefix.getBytes(
        StandardCharsets.UTF_8));
    String policyToSign = String.format("URLPrefix=%s:Expires=%d:KeyName=%s", encodedUrlPrefix,
        expirationTime, keyName);

    String signature = getSignatureForUrl(key, policyToSign);
    return String.format("Cloud-CDN-Cookie=%s:Signature=%s", policyToSign, signature);
  }

  // Creates signature for input string with private key.
  private static String getSignatureForUrl(byte[] privateKey, String input)
      throws InvalidKeyException, NoSuchAlgorithmException {

    final String algorithm = "HmacSHA1";
    final int offset = 0;
    Key key = new SecretKeySpec(privateKey, offset, privateKey.length, algorithm);
    Mac mac = Mac.getInstance(algorithm);
    mac.init(key);
    return Base64.getUrlEncoder()
        .encodeToString(mac.doFinal(input.getBytes(StandardCharsets.UTF_8)));
  }
}

Python

def sign_cookie(
    url_prefix: str,
    key_name: str,
    base64_key: str,
    expiration_time: datetime,
) -> str:
    """Gets the Signed cookie value for the specified URL prefix and configuration.

    Args:
        url_prefix: URL prefix to sign.
        key_name: name of the signing key.
        base64_key: signing key as a base64 encoded string.
        expiration_time: expiration time.

    Returns:
        Returns the Cloud-CDN-Cookie value based on the specified configuration.
    """
    encoded_url_prefix = base64.urlsafe_b64encode(
        url_prefix.strip().encode("utf-8")
    ).decode("utf-8")
    epoch = datetime.utcfromtimestamp(0)
    expiration_timestamp = int((expiration_time - epoch).total_seconds())
    decoded_key = base64.urlsafe_b64decode(base64_key)

    policy = f"URLPrefix={encoded_url_prefix}:Expires={expiration_timestamp}:KeyName={key_name}"

    digest = hmac.new(decoded_key, policy.encode("utf-8"), hashlib.sha1).digest()
    signature = base64.urlsafe_b64encode(digest).decode("utf-8")

    signed_policy = f"Cloud-CDN-Cookie={policy}:Signature={signature}"

    return signed_policy

Signierte Cookies prüfen

Die Validierung eines signierten Cookies entspricht im Wesentlichen der Erstellung eines signierten Cookies. Angenommen, Sie möchten den folgenden signierten Cookie-Header validieren:

Cookie: Cloud-CDN-Cookie=URLPrefix=URL_PREFIX:Expires=EXPIRATION:KeyName=KEY_NAME:Signature=SIGNATURE; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly

Sie können den Secret-Schlüssel mit dem Namen KEY_NAME verwenden, um die Signatur unabhängig zu generieren, und dann prüfen, ob sie mit SIGNATURE übereinstimmt.

Cookies für Nutzer ausgeben

Ihre Anwendung muss für jeden Nutzer (Client) ein einzelnes HTTP-Cookie mit einer korrekt signierten Richtlinie generieren und ausgeben.

  1. Erstellen Sie in Ihrem Anwendungscode einen HMAC-SHA-1-Signer.

  2. Signieren Sie die Richtlinie mit dem ausgewählten Schlüssel und notieren Sie den Schlüsselnamen, den Sie dem Backend hinzugefügt haben, z. B. mySigningKey.

  3. Erstellen Sie eine Cookierichtlinie mit dem folgenden Format. Beim Namen und dem Wert muss die Groß- und Kleinschreibung beachtet werden.

    Name: Cloud-CDN-Cookie
    Value: URLPrefix=$BASE64URLECNODEDURLORPREFIX:Expires=$TIMESTAMP:KeyName=$KEYNAME:Signature=$BASE64URLENCODEDHMAC
    

    Beispiel für Set-Cookie-Header:

    Set-Cookie: Cloud-CDN-Cookie=URLPrefix=aHR0cHM6Ly9tZWRpYS5leGFtcGxlLmNvbS92aWRlb3Mv:Expires=1566268009:KeyName=mySigningKey:Signature=0W2xlMlQykL2TG59UZnnHzkxoaw=; Domain=media.example.com; Path=/; Expires=Tue, 20 Aug 2019 02:26:49 GMT; HttpOnly
    

    Die Attribute Domain und Path im Cookie bestimmen, ob der Client das Cookie an Cloud CDN sendet.

Empfehlungen und Anforderungen

  • Legen Sie explizit die Attribute Domain und Path fest, die mit dem Domain- und Pfadpräfix übereinstimmen, über das Sie Ihre geschützten Inhalte bereitstellen möchten. Diese können sich von der Domain und dem Pfad unterscheiden, in dem das Cookie ausgegeben wird (example.com im Vergleich zu media.example.com oder /browse im Vergleich zu /videos).

  • Achten Sie darauf, dass Sie nur ein Cookie mit einem bestimmten Namen für Domain und Path haben.

  • Es dürfen keine widersprüchlichen Cookies ausgegeben werden, da dies den Zugriff auf Inhalte in anderen Browsersitzungen (Fenster oder Tabs) verhindern kann.

  • Legen Sie nach Bedarf die Flags Secure und HttpOnly fest. Secure gewährleistet, dass das Cookie nur über HTTPS-Verbindungen gesendet wird. HttpOnly verhindert, dass das Cookie für JavaScript verfügbar gemacht wird.

  • Die Cookieattribute Expires und Max-Age sind optional. Wenn Sie sie weglassen, ist das Cookie so lange wie die Browsersitzung (Tab, Fenster) vorhanden.

  • Bei einer Cache-Füllung oder einem Cache-Fehler wird das signierte Cookie an den im Backend-Dienst definierten Ursprung weitergeleitet. Der signierte Cookiewert muss bei jeder Anfrage geprüft werden, bevor Inhalte bereitgestellt werden.