Signierte Cookies verwenden

Diese Seite bietet eine Übersicht über signierte Cookies und eine Anleitung für deren Verwendung mit Cloud CDN. Signierte Cookies gewähren 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. Signierte Cookies 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, verwenden Sie signierte URLs.

Hinweis

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 Back-End konfigurieren, bevor Sie Cloud CDN aktivieren. Dies wird jedoch erst wirksam, wenn Cloud CDN aktiviert wird.

  • Aktualisieren Sie bei Bedarf auf die neueste Version des Cloud SDK:

    gcloud components update
    

Eine Übersicht 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

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

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

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

  • Cloud CDN blockiert keine Anfragen ohne einen Abfrageparameter Signature oder Cloud-CDN-Cookie-HTTP-Cookie. Anfragen mit ungültigen oder anderen 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 möglicherweise falsch 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 Back-End-Ursprung weiterleiten.
Nein Ja Aus dem Cache bereitstellen.
Ja Nein Signatur validieren. Wenn gültig, leiten Sie an den Back-End-Ursprung weiter.
Ja Ja Signatur validieren. Falls gültig, wird aus dem Cache bereitgestellt.

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 Back-End-Dienst und/oder Back-End-Bucket mit aktiviertem Cloud CDN.

Für jeden Back-End-Dienst oder -Bucket können Sie entsprechend Ihren Sicherheitsanforderungen Schlüssel erstellen und löschen. Für jedes Back-End 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 Back-End-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. Jeder, der einen Ihrer Schlüssel kennt, kann signierte URLs oder signierte Cookies erstellen, die von Cloud CDN akzeptiert werden, bis der Schlüssel aus Cloud CDN gelöscht wird. Die Schlüssel werden auf dem Computer gespeichert, auf dem Sie die signierten URLs generieren. Cloud CDN speichert die Schlüssel auch, 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 Schlüssel:

Console

  1. Wechseln Sie in der Google Cloud Console zur Seite Cloud Run.

    Zur Cloud CDN-Seite

  2. Klicken Sie auf Ursprung hinzufügen.
  3. Wählen Sie einen HTTP(S)-Load-Balancer als Ursprung aus.
  4. Wählen Sie Back-End-Dienste oder Back-End-Buckets aus. Für jeden davon:
    1. Klicken Sie auf Konfigurieren und dann auf Signaturschlüssel hinzufügen.
    2. Benennen Sie den neuen Signaturschlüssel unter Name.
    3. Wählen Sie unter Methode zur Schlüsselerstellung die Option Automatisch generieren oder Selbst eingeben aus.
    4. Wenn Sie einen eigenen Schlüssel eingeben, nutzen Sie dazu das Textfeld.
    5. Klicken Sie auf Fertig.
    6. Geben Sie unter Höchstalter der Cache-Einträge einen Wert an und wählen Sie eine Einheit aus der Drop-down-Liste aus. Sie können zwischen Sekunde, Minute, Stunde und Tag wählen. Das maximal mögliche Alter beträgt drei (3) Tage.
  5. Klicken Sie auf Speichern.
  6. Klicken Sie auf Hinzufügen.

gcloud

Das gcloud-Befehlszeilentool liest Schlüssel aus einer von Ihnen angegebenen lokalen Datei. Die Schlüsseldatei muss erstellt werden. Dazu müssen 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 Back-End-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 Back-End-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 Back-End-Bucket eines Projekts zum ersten Mal einen Schlüssel hinzufügen.

Fügen Sie mindestens einen Schlüssel zu einem Back-End-Bucket in Ihrem Projekt hinzu, bevor Sie den folgenden Befehl ausführen. Ansonsten schlägt der Befehl mit einem Fehler fehl, 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 unter Projekt-ID und Projektnummer finden in der Hilfe zur Google Cloud Console.

Maximale Cache-Zeit optional 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 Back-End-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 Back-End-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 Back-End-Dienst oder Back-End-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

In dieser Reihenfolge müssen vier Felder definiert werden:

  • 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 das Cookie 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 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 Back-End-Bucket oder Back-End-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.

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
}

// readKeyFile reads the base64url-encoded key file and decodes it.
func readKeyFile(path string) ([]byte, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("failed to read key file: %+v", err)
	}
	d := make([]byte, base64.URLEncoding.DecodedLen(len(b)))
	n, err := base64.URLEncoding.Decode(d, b)
	if err != nil {
		return nil, fmt.Errorf("failed to base64url decode: %+v", err)
	}
	return d[:n], nil
}

func generateSignedCookie(w io.Writer) error {
	// The path to a file containing the base64-encoded signing key
	keyPath := os.Getenv("KEY_PATH")

	// Note: consider using the GCP Secret Manager for managing access to your
	// signing key(s).
	key, err := readKeyFile(keyPath)
	if err != nil {
		return err
	}

	var (
		// domain and path should match the user-facing URL for accessing
		// content.
		domain     = "media.example.com"
		path       = "/segments/"
		keyName    = "my-key"
		expiration = time.Hour * 2
	)

	signedValue, err := signCookie(fmt.Sprintf("https://%s%s", domain,
		path), keyName, key, time.Now().Add(expiration))
	if err != nil {
		return err
	}

	// Use Go's http.Cookie type to construct a cookie.
	cookie := &http.Cookie{
		Name:   "Cloud-CDN-Cookie",
		Value:  signedValue,
		Path:   path, // Best practice: only send the cookie for paths it is valid for
		Domain: domain,
		MaxAge: int(expiration.Seconds()),
	}

	// We print this to stdout in this example. In a real application, use the
	// SetCookie method on a http.ResponseWriter to write the cookie to the
	// user.
	fmt.Fprintln(w, cookie)

	return nil
}

Python

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

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

    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.datetime.utcfromtimestamp(0)
    expiration_timestamp = int((expiration_time - epoch).total_seconds())
    decoded_key = base64.urlsafe_b64decode(base64_key)

    policy_pattern = u'URLPrefix={encoded_url_prefix}:Expires={expires}:KeyName={key_name}'
    policy = policy_pattern.format(
            encoded_url_prefix=encoded_url_prefix,
            expires=expiration_timestamp,
            key_name=key_name)

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

    signed_policy = u'Cloud-CDN-Cookie={policy}:Signature={signature}'.format(
            policy=policy, signature=signature)
    print(signed_policy)

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 im Anwendungscode einen HMAC-SHA-1-Unterzeichner.

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

  3. Erstellen Sie eine Cookierichtlinie mit dem folgenden Format. Bei Name und 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 gegensätzlichen 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 sorgt dafür, 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 Back-End-Dienst definierten Ursprung weitergeleitet. Der signierte Cookiewert muss bei jeder Anfrage geprüft werden, bevor Inhalte bereitgestellt werden.