Web Push-Protokoll

Matt Gaunt

Wir haben gesehen, wie eine Bibliothek zum Auslösen von Push-Nachrichten verwendet werden kann. Was genau machen 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 wir sollten uns die einzelnen Teile ansehen, da es nützlich 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 alles und was passiert genau? So läuft die Authentifizierung auf dem Anwendungsserver ab:

  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. Die Legende links unten gibt Aufschluss über ö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-Web-Token (kurz JWT) ist eine Möglichkeit, eine Nachricht an Dritte zu senden, damit der Empfänger prü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.

Abbildung der Strings in einem JSON-Web-Token

Der erste und zweite String (JWT-Informationen und JWT-Daten) sind JSON-Elemente, die base64-codiert sind und daher ö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-Nachrichten haben 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. Wir legen sie daher auf den Ursprung des Push-Dienstes fest.

Der Wert exp ist das Ablaufdatum des JWT. So wird verhindert, dass Angreifer ein JWT wiederverwenden können, wenn sie es abfangen. Der Ablauf ist ein Zeitstempel in Sekunden und darf maximal 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 „unsignierte 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.

Eine häufige Frage von Nutzern anderer Push-Dienste ist, warum die Web-Push-Nutzlast verschlüsselt werden muss. Bei nativen Apps können Push-Nachrichten Daten als Nur-Text senden.

Der Vorteil von Web-Push-Mitteilungen besteht darin, dass alle Push-Dienste dieselbe API (das Web-Push-Protokoll) verwenden. Entwickler müssen sich also nicht darum kümmern, wer der Push-Dienst ist. Wir können eine Anfrage im richtigen Format senden 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. (Vielen Dank an Mat Scales für seinen hervorragenden Artikel zur 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 von mit ECDH generierten Schlüsseln besteht darin, dass Alice ihren privaten Schlüssel und den öffentlichen Schlüssel von Bob 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 „X“ verwenden, um Nachrichten zwischen ihnen zu verschlüsseln und zu 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 bietet eine kurze Beschreibung von HKDF:

HKDF ist eine HMAC-basierte Schlüsselableitungsfunktion, die beliebiges schwaches Schlüsselmaterial in kryptografisch starkes Schlüsselmaterial umwandelt. Sie kann beispielsweise verwendet werden, um Diffie-Hellman-gehandelte gemeinsame Geheimnisse in Schlüsselmaterial umzuwandeln, das für die Verschlüsselung, Integritätsprüfung oder Authentifizierung geeignet ist.

Im Grunde genommen nimmt HKDF eine Eingabe, die nicht besonders sicher ist, 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.

In Node könnte das 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.

Dies deckt grob ECDH und HKDF ab.

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 folgende 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 geschieht dies so:

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 Authentifizierungssecret des Push-Abos und dem gerade erstellten gemeinsamen Secret.

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. Kurz gesagt: Es hat keinen klaren Zweck, obwohl Browser eine eingehende Nachricht entschlüsseln und nach der erwarteten Inhaltscodierung suchen könnten. Mit \0 wird dem Ende des Buffers ein Byte mit dem Wert 0 hinzugefügt. Das wird von Browsern erwartet, die die Nachricht entschlüsseln. Sie erwarten so viele Bytes für die Inhaltscodierung, gefolgt von einem Byte mit dem Wert 0 und dann von den verschlüsselten Daten.

Unser Pseudo-Zufallsschlüssel führt die Authentifizierung, das gemeinsame Secret und einen Teil der Codierungsinformationen einfach durch HKDF, wodurch er kryptografisch stärker wird.

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 durch HKDF geleitet, wobei das Salz und der PRK mit den 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

Nachdem wir den Inhaltsverschlüsselungsschlüssel haben, können wir 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 geht das so:

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

Bevor wir unsere Nutzlast verschlüsseln, müssen wir festlegen, wie viel Padding wir an den Anfang der Nutzlast anhängen 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 Padding-Byte 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 Verbraucher liest dann also 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!

Es bleibt nur noch zu bestimmen, wie diese Nutzlast an den Push-Dienst gesendet wird.

Verschlüsselte Header und Body der 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 zum Verschlüsseln 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]

Crypto-Key-Header

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“ haben 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 Bytestream gesendet werden muss.

In NodeJS würden wir das so machen:

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

Mehr Ü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 Überschriften sind erforderlich, andere sind 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 „0“ 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 weckt, wenn der Akkustand niedrig ist.

Der Headerwert ist 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 sich jederzeit ansehen, wie Bibliotheken Push-Nachrichten auf web-push-libs.org 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. In der Regel bedeutet das, dass einer Ihrer Header ungültig oder falsch formatiert ist.
404 Nicht gefunden. Das Abo ist abgelaufen und kann nicht mehr verwendet werden. In diesem Fall solltest du die „PushSubscription“ löschen und warten, bis der Client den Nutzer wieder abonniert.
410 Nicht mehr vorhanden. 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