Web Push-Protokoll

Matt Gaunt

Wir haben gesehen, wie eine Bibliothek verwendet werden kann, um Push-Nachrichten auszulösen, aber was genau leisten diese Bibliotheken?

Sie stellen Netzwerkanfragen und achten dabei darauf, dass diese Anfragen das richtige Format haben. Die Spezifikation, die diese Netzwerkanfrage definiert, ist das Web Push Protocol.

Diagramm zum Senden einer Push-Nachricht von Ihrem Server an einen Push-Dienst

In diesem Abschnitt wird beschrieben, wie sich der Server mit Anwendungsserverschlüsseln identifizieren kann und wie die verschlüsselte Nutzlast und die zugehörigen Daten gesendet werden.

Das ist nicht die schöne Seite von Web Push und ich bin kein Experte für Verschlüsselung, aber sehen wir uns die einzelnen Teile an, da es praktisch ist zu wissen, was diese Bibliotheken im Hintergrund tun.

Anwendungsserverschlüssel

Wenn wir einen Nutzer abonnieren, geben wir eine applicationServerKey ein. Dieser Schlüssel wird an den Push-Dienst übergeben und verwendet, um zu prüfen, ob die Anwendung, die den Nutzer abonniert hat, auch die Anwendung ist, die Push-Nachrichten auslöst.

Wenn wir eine Push-Nachricht auslösen, senden wir eine Reihe von Headern, die es dem Push-Dienst ermöglichen, die Anwendung zu authentifizieren. (Dies ist in der VAPID-Spezifikation definiert.)

Was bedeutet das eigentlich und was geschieht genau? Dies sind die Schritte zur App-Server-Authentifizierung:

  1. Der Anwendungsserver signiert einige JSON-Informationen mit seinem privaten Anwendungsschlüssel.
  2. Diese signierten Informationen werden als Header in einer POST-Anfrage an den Push-Dienst gesendet.
  3. Der Push-Dienst verwendet den gespeicherten öffentlichen Schlüssel, den er von pushManager.subscribe() erhalten hat, um zu prüfen, ob die empfangenen Informationen mit dem privaten Schlüssel signiert wurden, der mit dem öffentlichen Schlüssel verknüpft ist. Denken Sie daran: Der öffentliche Schlüssel ist die applicationServerKey, die an den subscribe-Aufruf übergeben wird.
  4. Wenn die signierten Informationen gültig sind, sendet der Push-Dienst die Push-Nachricht an den Nutzer.

Unten finden Sie ein Beispiel für diesen Informationsfluss. In der Legende unten links sehen Sie öffentliche und private Schlüssel.

Abbildung, die zeigt, wie der private Schlüssel des Anwendungsservers beim Senden einer Nachricht verwendet wird

Die „signierten Informationen“, die einem Header in der Anfrage hinzugefügt werden, sind ein JSON-Webtoken.

JSON-Webtoken

Ein JSON-Webtoken (oder kurz JWT) ist eine Möglichkeit, eine Nachricht an Dritte zu senden, damit der Empfänger überprüfen kann, wer sie gesendet hat.

Wenn ein Dritter eine Nachricht empfängt, muss er den öffentlichen Schlüssel des Absenders abrufen und damit die Signatur des JWT validieren. Wenn die Signatur gültig ist, muss das JWT mit dem übereinstimmenden privaten Schlüssel signiert worden sein. Es muss also vom erwarteten Absender stammen.

Auf https://jwt.io/ finden Sie eine Vielzahl von Bibliotheken, die die Signatur für Sie ausführen können. Ich würde Ihnen empfehlen, diese Möglichkeit nach Möglichkeit zu nutzen. Zur Vollständigkeit sehen wir uns an, wie Sie ein signiertes JWT manuell erstellen.

Webpush und signierte JWTs

Ein signiertes JWT ist nur ein String, kann aber als drei Strings betrachtet werden, die durch Punkte verbunden sind.

Darstellung der Strings in einem JSON Web Token

Der erste und zweite String (die JWT-Informationen und JWT-Daten) sind JSON-Elemente, die base64-codiert wurden, was bedeutet, dass sie öffentlich lesbar sind.

Der erste String enthält Informationen zum JWT selbst und gibt an, mit welchem Algorithmus die Signatur erstellt wurde.

Die JWT-Informationen für Web-Push müssen die folgenden Informationen enthalten:

{
  "typ": "JWT",
  "alg": "ES256"
}

Der zweite String sind die JWT-Daten. Hier finden Sie Informationen zum Absender des JWT, für wen es bestimmt ist und wie lange es gültig ist.

Bei Web-Push hätten die Daten folgendes Format:

{
  "aud": "https://some-push-service.org",
  "exp": "1469618703",
  "sub": "mailto:example@web-push-book.org"
}

Der Wert aud ist die „Zielgruppe“, d. h. für wen das JWT bestimmt ist. Bei Web-Push ist die Zielgruppe der Push-Dienst. Daher wird sie auf den Ursprung des Push-Dienstes festgelegt.

Der Wert exp ist das Ablaufdatum des JWT. So wird verhindert, dass Angreifer ein JWT wiederverwenden können, wenn sie es abfangen. Die Ablaufzeit ist ein Zeitstempel in Sekunden und darf nicht mehr 24 Stunden betragen.

In Node.js wird das Ablaufdatum so festgelegt:

Math.floor(Date.now() / 1000) + 12 * 60 * 60;

Es sind 12 Stunden statt 24 Stunden, um Probleme mit Zeitabweichungen zwischen der sendenden Anwendung und dem Push-Dienst zu vermeiden.

Der Wert für sub muss entweder eine URL oder eine mailto-E-Mail-Adresse sein. So kann ein Push-Dienst, der den Absender kontaktieren muss, die Kontaktdaten aus dem JWT abrufen. (Aus diesem Grund benötigte die Web-Push-Bibliothek eine E-Mail-Adresse.)

Genau wie die JWT-Informationen werden die JWT-Daten als URL-sicherer Base64-String codiert.

Der dritte String, die Signatur, ist das Ergebnis der Zusammenführung der ersten beiden Strings (JWT-Informationen und JWT-Daten) mit einem Punktzeichen, das wir als „unsigniertes Token“ bezeichnen, und der Signatur.

Für den Signaturvorgang muss das „nicht signierte Token“ mit ES256 verschlüsselt werden. Gemäß der JWT-Spezifikation steht ES256 für „ECDSA mit der P-256-Kurve und dem SHA-256-Hash-Algorithmus“. Mit Web-Kryptografie können Sie die Signatur so erstellen:

// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');

// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;

// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
  kty: 'EC',
  crv: 'P-256',
  x: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(1, 33)),
  y: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(33, 65)),
  d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};

// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
  name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
  return crypto.subtle.sign({
    name: 'ECDSA',
    hash: {
      name: 'SHA-256',
    },
  }, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
  console.log('Signature: ', signature);
});

Ein Push-Dienst kann ein JWT mit dem öffentlichen Schlüssel des Anwendungsservers validieren, um die Signatur zu entschlüsseln und dafür zu sorgen, dass der entschlüsselte String mit dem „unsignierten Token“ übereinstimmt (d. h. den ersten beiden Strings im JWT).

Das signierte JWT (d. h. alle drei Strings, die durch Punkte verbunden sind) wird als Authorization-Header mit vorangestelltem WebPush an den Web-Push-Dienst gesendet:

Authorization: 'WebPush [JWT Info].[JWT Data].[Signature]';

Das Web Push-Protokoll besagt außerdem, dass der öffentliche Schlüssel des Anwendungsservers im Crypto-Key-Header als URL-sicherer Base64-codierter String gesendet werden muss, dem p256ecdsa= vorangestellt ist.

Crypto-Key: p256ecdsa=[URL Safe Base64 Public Application Server Key]

Nutzlastverschlüsselung

Sehen wir uns als Nächstes an, wie wir eine Nutzlast mit einer Push-Nachricht senden können, damit unsere Webanwendung, wenn sie eine Push-Nachricht empfängt, auf die empfangenen Daten zugreifen kann.

Wer bereits andere Push-Dienste verwendet hat, stellt sich häufig die Frage, warum die Web-Push-Nutzlast verschlüsselt werden muss. Bei nativen Apps können Push-Nachrichten Daten als Nur-Text senden.

Das Schöne an Web-Push besteht darin, dass, da alle Push-Dienste dieselbe API (das Web-Push-Protokoll) verwenden, sich Entwickler nicht darum kümmern müssen, wer der Push-Dienst ist. Wir können eine Anfrage im richtigen Format stellen und erwarten, dass eine Push-Nachricht gesendet wird. Der Nachteil dabei ist, dass Entwickler Nachrichten an einen nicht vertrauenswürdigen Push-Dienst senden könnten. Durch die Verschlüsselung der Nutzlast kann ein Push-Dienst die gesendeten Daten nicht lesen. Nur der Browser kann die Informationen entschlüsseln. So werden die Daten der Nutzer geschützt.

Die Verschlüsselung der Nutzlast ist in der Spezifikation für die Nachrichtenverschlüsselung definiert.

Bevor wir uns die einzelnen Schritte zur Verschlüsselung einer Push-Nachrichtennutzlast ansehen, sollten wir einige Verfahren besprechen, die während des Verschlüsselungsprozesses verwendet werden. (Massive-Huttipp an Mat Scales für seinen ausgezeichneten Artikel über Push-Verschlüsselung.)

ECDH und HKDF

Sowohl ECDH als auch HKDF werden während des gesamten Verschlüsselungsprozesses verwendet und bieten Vorteile bei der Verschlüsselung von Informationen.

ECDH: Elliptic Curve Diffie-Hellman-Schlüsselaustausch

Angenommen, es gibt zwei Personen, die Informationen teilen möchten: Alice und Bob. Sowohl Alice als auch Bob haben eigene öffentliche und private Schlüssel. Alice und Bob geben ihre öffentlichen Schlüssel einander bekannt.

Die nützliche Eigenschaft der mit ECDH generierten Schlüssel besteht darin, dass Alice ihren privaten Schlüssel und Bobs öffentlichen Schlüssel verwenden kann, um den geheimen Wert "X" zu erstellen. Bob kann dasselbe tun und mit seinem privaten Schlüssel und dem öffentlichen Schlüssel von Alice unabhängig denselben Wert „X“ erstellen. Dadurch wird „X“ zu einem gemeinsamen Geheimnis und Alice und Bob mussten nur ihren öffentlichen Schlüssel teilen. Jetzt können Bob und Alice mit „X“ Nachrichten zwischen ihnen verschlüsseln und entschlüsseln.

Soweit ich weiß, definiert ECDH die Eigenschaften von Kurven, die diese „Funktion“ zum Erstellen eines gemeinsamen Geheimnisses „X“ ermöglichen.

Dies ist eine allgemeine Erklärung von ECDH. Weitere Informationen finden Sie in diesem Video.

In Bezug auf den Code sind die meisten Sprachen/Plattformen mit Bibliotheken ausgestattet, die das Generieren dieser Schlüssel erleichtern.

In node würden wir Folgendes tun:

const keyCurve = crypto.createECDH('prime256v1');
keyCurve.generateKeys();

const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();

HKDF: HMAC-basierte Schlüsselableitungsfunktion

Wikipedia hat eine kurze Beschreibung von HKDF:

HKDF ist eine HMAC-basierte Schlüsselableitungsfunktion, die beliebiges schwaches Schlüsselmaterial in kryptografisch starkes Schlüsselmaterial umwandelt. Damit lassen sich beispielsweise von Diffie Hellman ausgetauschte gemeinsame Secrets in Schlüsselmaterial umwandeln, das für die Verschlüsselung, Integritätsprüfung oder Authentifizierung geeignet ist.

Im Wesentlichen nimmt HKDF Eingaben an, die nicht besonders sicher sind, und macht sie sicherer.

Die Spezifikation für diese Verschlüsselung erfordert die Verwendung von SHA-256 als Hash-Algorithmus. Die resultierenden Schlüssel für HKDF bei Web-Push dürfen nicht länger als 256 Bit (32 Byte) sein.

Im Knoten könnte dies so implementiert werden:

// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
  // Extract
  const keyHmac = crypto.createHmac('sha256', salt);
  keyHmac.update(ikm);
  const key = keyHmac.digest();

  // Expand
  const infoHmac = crypto.createHmac('sha256', key);
  infoHmac.update(info);

  // A one byte long buffer containing only 0x01
  const ONE_BUFFER = new Buffer(1).fill(1);
  infoHmac.update(ONE_BUFFER);

  return infoHmac.digest().slice(0, length);
}

Dieser Beispielcode basiert auf dem Artikel von Mat Scale.

Hier werden ECDH und HKDF allgemein behandelt.

ECDH ist eine sichere Methode, um öffentliche Schlüssel freizugeben und ein gemeinsames Secret zu generieren. HKDF ist eine Möglichkeit, unsichere Daten zu sichern.

Dieser wird bei der Verschlüsselung der Nutzlast verwendet. Als Nächstes sehen wir uns an, was wir als Eingabe verwenden und wie diese verschlüsselt wird.

Eingaben

Wenn wir eine Push-Nachricht mit einer Nutzlast an einen Nutzer senden möchten, sind drei Eingaben erforderlich:

  1. Die Nutzlast selbst.
  2. Das auth-Secret aus dem PushSubscription.
  3. Der p256dh-Schlüssel des PushSubscription.

Wir haben gesehen, dass die Werte auth und p256dh aus einer PushSubscription abgerufen werden. Zur Erinnerung: Für ein Abo benötigen wir diese Werte:

subscription.toJSON().keys.auth;
subscription.toJSON().keys.p256dh;

subscription.getKey('auth');
subscription.getKey('p256dh');

Der Wert auth sollte als geheim behandelt und nicht außerhalb Ihrer Anwendung weitergegeben werden.

Der Schlüssel p256dh ist ein öffentlicher Schlüssel, der manchmal auch als öffentlicher Clientschlüssel bezeichnet wird. Hier bezeichnen wir p256dh als den öffentlichen Schlüssel des Abos. Der öffentliche Aboschlüssel wird vom Browser generiert. Der Browser hält den privaten Schlüssel geheim und verwendet ihn zum Entschlüsseln der Nutzlast.

Diese drei Werte, auth, p256dh und payload, sind als Eingaben erforderlich. Das Ergebnis des Verschlüsselungsprozesses ist die verschlüsselte Nutzlast, ein Salt-Wert und ein öffentlicher Schlüssel, der nur zum Verschlüsseln der Daten verwendet wird.

Salt

Der Salt muss aus 16 Byte zufälligen Daten bestehen. In NodeJS würden wir Folgendes tun, um ein Salt zu erstellen:

const salt = crypto.randomBytes(16);

Öffentliche/private Schlüssel

Die öffentlichen und privaten Schlüssel sollten mit einer P-256-elliptischen Kurve generiert werden. In Node würde das so aussehen:

const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();

const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();

Wir bezeichnen diese Schlüssel als „lokale Schlüssel“. Sie werden nur für die Verschlüsselung verwendet und haben nichts mit Anwendungsserverschlüsseln zu tun.

Mit der Nutzlast, dem Authentifizierungs-Secret und dem öffentlichen Aboschlüssel als Eingaben sowie einem neu generierten Salt und einer Reihe lokaler Schlüssel können wir jetzt mit der Verschlüsselung beginnen.

Gemeinsames Secret

Im ersten Schritt erstellen wir ein gemeinsames Secret mit dem öffentlichen Schlüssel des Abos und unserem neuen privaten Schlüssel. So einfach ist das.

const sharedSecret = localKeysCurve.computeSecret(
  subscription.keys.p256dh,
  'base64',
);

Dieser wird im nächsten Schritt verwendet, um den Pseudozufallsschlüssel (PRK) zu berechnen.

Pseudozufallsschlüssel

Der Pseudozufallsschlüssel (PRK) ist die Kombination aus dem Auth-Secret des Push-Abos und dem soeben erstellten gemeinsamen geheimen Schlüssel.

const authEncBuff = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(subscription.keys.auth, sharedSecret, authEncBuff, 32);

Sie fragen sich vielleicht, wozu der String Content-Encoding: auth\0 dient. Sie hat also keinen eindeutigen Zweck, obwohl Browser eine eingehende Nachricht entschlüsseln und nach der erwarteten Inhaltscodierung suchen könnten. \0 fügt dem Ende des Zwischenspeichers ein Byte mit dem Wert 0 hinzu. Dies wird von Browsern erwartet, die die Nachricht entschlüsseln und so viele Byte für die Inhaltscodierung erwarten, gefolgt von einem Byte mit dem Wert 0, gefolgt von den verschlüsselten Daten.

Bei unserem Pseudozufallsschlüssel werden die Authentifizierung, der gemeinsame Schlüssel und ein Teil der Codierungsinformationen über HKDF ausgeführt, d.h., er wird kryptografisch stärker.

Kontext

Der „Kontext“ ist eine Reihe von Bytes, mit denen später im Verschlüsselungsbrowser zwei Werte berechnet werden. Es ist im Grunde ein Byte-Array, das den öffentlichen Schlüssel des Abos und den lokalen öffentlichen Schlüssel enthält.

const keyLabel = new Buffer('P-256\0', 'utf8');

// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');

const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;

const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;

const contextBuffer = Buffer.concat([
  keyLabel,
  subscriptionPubKeyLength.buffer,
  subscriptionPubKey,
  localPublicKeyLength.buffer,
  localPublicKey,
]);

Der finale Kontextbuffer besteht aus einem Label, der Anzahl der Bytes im öffentlichen Aboschlüssel, gefolgt vom Schlüssel selbst, dann der Anzahl der Bytes des lokalen öffentlichen Schlüssels und dann dem Schlüssel selbst.

Mit diesem Kontextwert können wir einen Nonce und einen Inhaltsverschlüsselungsschlüssel (Content Encryption Key, CEK) erstellen.

Inhaltsverschlüsselungsschlüssel und Nonce

Ein Nonce ist ein Wert, der Replay-Angriffe verhindert, da er nur einmal verwendet werden sollte.

Der Inhaltsverschlüsselungsschlüssel (Content Encryption Key, CEK) ist der Schlüssel, mit dem die Nutzlast letztendlich verschlüsselt wird.

Zuerst müssen wir die Datenbytes für den Nonce und den CEK erstellen. Das ist einfach ein String zur Inhaltscodierung, gefolgt vom gerade berechneten Kontextbuffer:

const nonceEncBuffer = new Buffer('Content-Encoding: nonce\0', 'utf8');
const nonceInfo = Buffer.concat([nonceEncBuffer, contextBuffer]);

const cekEncBuffer = new Buffer('Content-Encoding: aesgcm\0');
const cekInfo = Buffer.concat([cekEncBuffer, contextBuffer]);

Diese Informationen werden über HKDF abgerufen, wobei Salt und PRK mit nonceInfo und cekInfo kombiniert werden:

// The nonce should be 12 bytes long
const nonce = hkdf(salt, prk, nonceInfo, 12);

// The CEK should be 16 bytes long
const contentEncryptionKey = hkdf(salt, prk, cekInfo, 16);

So erhalten wir den Nonce und den Inhaltsverschlüsselungsschlüssel.

Verschlüsselung ausführen

Jetzt haben wir den Schlüssel für die Inhaltsverschlüsselung und können die Nutzlast verschlüsseln.

Wir erstellen eine AES128-Chiffre mit dem Inhaltsverschlüsselungsschlüssel als Schlüssel und der Nonce ist ein Initialisierungsvektor.

In Node ist dies so:

const cipher = crypto.createCipheriv(
  'id-aes128-GCM',
  contentEncryptionKey,
  nonce,
);

Bevor wir unsere Nutzlast verschlüsseln, müssen wir festlegen, wie viel Padding wir vor die Nutzlast einfügen möchten. Der Grund, warum wir Padding hinzufügen möchten, ist, dass das Risiko verhindert wird, dass Lauscher anhand der Nutzlastgröße die „Arten“ von Nachrichten bestimmen können.

Sie müssen zwei Bytes für das Padding hinzufügen, um die Länge des zusätzlichen Paddings anzugeben.

Wenn Sie beispielsweise kein Padding hinzugefügt haben, haben Sie zwei Byte mit dem Wert 0, d. h. es gibt kein Padding. Nach diesen beiden Byten wird die Nutzlast gelesen. Wenn Sie 5 Byte Padding hinzugefügt haben, haben die ersten beiden Byte den Wert 5. Der Nutzer liest dann weitere fünf Byte und beginnt dann mit dem Lesen der Nutzlast.

const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeros, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);

Wir führen dann unseren Padding und unsere Nutzlast durch diese Chiffre.

const result = cipher.update(Buffer.concat(padding, payload));
cipher.final();

// Append the auth tag to the result -
// https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
const encryptedPayload = Buffer.concat([result, cipher.getAuthTag()]);

Wir haben jetzt unsere verschlüsselte Nutzlast. Super!

Jetzt müssen Sie nur noch bestimmen, wie diese Nutzlast an den Push-Dienst gesendet wird.

Header und Text der verschlüsselten Nutzlast

Um diese verschlüsselte Nutzlast an den Push-Dienst zu senden, müssen wir in unserer POST-Anfrage einige verschiedene Header definieren.

Verschlüsselungsheader

Der Header „Encryption“ muss das Salt enthalten, das für die Verschlüsselung der Nutzlast verwendet wird.

Das 16-Byte-Salt sollte base64-codiert und dem Verschlüsselungsheader hinzugefügt werden. Das sieht so aus:

Encryption: salt=[URL Safe Base64 Encoded Salt]

Header für kryptografischen Schlüssel

Wir haben gesehen, dass die Crypto-Key-Überschrift im Abschnitt „Application Server Keys“ verwendet wird, um den öffentlichen Schlüssel des Anwendungsservers zu enthalten.

Dieser Header wird auch verwendet, um den lokalen öffentlichen Schlüssel freizugeben, der zum Verschlüsseln der Nutzlast verwendet wird.

Die Kopfzeile sieht dann so aus:

Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]

Header für Inhaltstyp, Länge und Codierung

Der Content-Length-Header ist die Anzahl der Byte in der verschlüsselten Nutzlast. Die Header "Content-Type" und "Content-Encoding" sind feste Werte. Das ist unten dargestellt.

Content-Length: [Number of Bytes in Encrypted Payload]
Content-Type: 'application/octet-stream'
Content-Encoding: 'aesgcm'

Nachdem diese Header festgelegt wurden, müssen wir die verschlüsselte Nutzlast als Textkörper unserer Anfrage senden. Beachten Sie, dass Content-Type auf application/octet-stream festgelegt ist. Das liegt daran, dass die verschlüsselte Nutzlast als ein Stream von Byte gesendet werden muss.

In NodeJS würden wir das so machen:

const pushRequest = https.request(httpsOptions, function(pushResponse) {
pushRequest.write(encryptedPayload);
pushRequest.end();

Weitere Überschriften?

Wir haben die Header für JWT-/Anwendungsserverschlüssel behandelt (d. h. wie die Anwendung mit dem Push-Dienst identifiziert wird) und die Header zum Senden einer verschlüsselten Nutzlast.

Es gibt zusätzliche Header, mit denen Push-Dienste das Verhalten gesendeter Nachrichten ändern. Einige dieser Header sind erforderlich, andere optional.

TTL-Header

Erforderlich

TTL (oder „Time to Live“) ist eine Ganzzahl, die angibt, wie lange Ihre Push-Nachricht im Push-Dienst gespeichert bleiben soll, bevor sie zugestellt wird. Wenn die TTL abläuft, wird die Nachricht aus der Push-Dienstwarteschlange entfernt und nicht zugestellt.

TTL: [Time to live in seconds]

Wenn Sie TTL auf null setzen, versucht der Push-Dienst, die Nachricht sofort zuzustellen. Wenn das Gerät jedoch nicht erreichbar ist, wird die Nachricht sofort aus der Push-Dienstwarteschlange entfernt.

Technisch gesehen kann ein Push-Dienst die TTL einer Push-Nachricht nach Belieben reduzieren. Ob dies der Fall ist, können Sie anhand des TTL-Headers in der Antwort eines Push-Dienstes erkennen.

Thema

Optional

Themen sind Strings, mit denen ausstehende Nachrichten durch eine neue Nachricht ersetzt werden können, wenn sie über übereinstimmende Themennamen verfügen.

Das ist nützlich, wenn mehrere Nachrichten gesendet werden, während ein Gerät offline ist, und ein Nutzer nur die neueste Nachricht sehen soll, wenn das Gerät eingeschaltet ist.

Dringlichkeit

Optional

Die Dringlichkeit gibt dem Push-Dienst an, wie wichtig eine Nachricht für den Nutzer ist. So kann der Push-Dienst die Akkulaufzeit des Geräts eines Nutzers verlängern, indem er es nur bei wichtigen Nachrichten aktiviert, wenn der Akkustand niedrig ist.

Der Headerwert wird wie unten dargestellt definiert. Der Standardwert ist normal

Urgency: [very-low | low | normal | high]

Alles zusammen

Wenn Sie weitere Fragen zur Funktionsweise haben, können Sie jederzeit nachsehen, wie Bibliotheken Push-Nachrichten in der Organisation "web-push-libs" auslösen.

Sobald du eine verschlüsselte Nutzlast und die oben genannten Header hast, musst du nur noch eine POST-Anfrage an die endpoint in einer PushSubscription senden.

Was machen wir also mit der Antwort auf diese POST-Anfrage?

Antwort vom Push-Dienst

Nachdem Sie eine Anfrage an einen Push-Dienst gesendet haben, müssen Sie den Statuscode der Antwort prüfen. Anhand dieses Codes können Sie erkennen, ob die Anfrage erfolgreich war oder nicht.

Statuscode Beschreibung
201 Erstellt Die Anfrage zum Senden einer Push-Nachricht wurde empfangen und akzeptiert.
429 Zu viele Anfragen: Das bedeutet, dass Ihr Anwendungsserver eine Ratenbeschränkung mit einem Push-Dienst erreicht hat. Der Push-Dienst sollte einen Header „Retry-After“ enthalten, um anzugeben, wie lange gewartet werden muss, bevor eine weitere Anfrage gesendet werden kann.
400 Ungültige Anfrage. Das bedeutet im Allgemeinen, dass einer Ihrer Header ungültig oder falsch formatiert ist.
404 Nicht gefunden. Dies weist darauf hin, dass das Abo abgelaufen ist und nicht mehr verwendet werden kann. In diesem Fall solltest du „PushSubscription“ löschen und warten, bis der Client den Nutzer wieder abonniert hat.
410 Weg. Das Abo ist nicht mehr gültig und sollte vom Anwendungsserver entfernt werden. Das kann reproduziert werden, indem Sie „unsubscribe()“ für ein „PushSubscription“ aufrufen.
413 Die Nutzlastgröße ist zu groß. Die Mindestgröße der Nutzlast, die ein Push-Dienst unterstützen muss, beträgt 4.096 Byte (oder 4 KB).

Weitere Informationen zu den HTTP-Statuscodes finden Sie im Web Push-Standard (RFC8030).

Weitere Informationen

Code labs