Web Push-Protokoll

Matt Gaunt

Wir haben gesehen, wie eine Bibliothek zum Auslösen von Push-Benachrichtigungen verwendet werden kann. Aber was genau machen diese Bibliotheken?

Sie stellen Netzwerkanfragen und sorgen dafür, 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 Anwendungsserver-Schlüsseln identifizieren kann und wie die verschlüsselte Nutzlast und die zugehörigen Daten gesendet werden.

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

Anwendungsserver-Schlüssel

Wenn wir einen Nutzer abonnieren, übergeben wir eine applicationServerKey. Dieser Schlüssel wird an den Push-Dienst übergeben und verwendet, um zu prüfen, ob die Anwendung, mit der der Nutzer das Abo abgeschlossen hat, auch die Anwendung ist, die Push-Benachrichtigungen auslöst.

Wenn wir eine Push-Nachricht auslösen, senden wir eine Reihe von Headern, mit denen der Push-Dienst die Anwendung authentifizieren kann. (Dies wird durch die VAPID-Spezifikation definiert.)

Was bedeutet das alles und was genau passiert? Das sind die Schritte für die Authentifizierung des Anwendungsservers:

  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 sind, der sich auf den öffentlichen Schlüssel bezieht. Hinweis: Der öffentliche Schlüssel ist der applicationServerKey, der an den subscribe-Aufruf übergeben wird.
  4. Wenn die signierten Informationen gültig sind, sendet der Push-Dienst die Push-Nachricht an den Nutzer.

Ein Beispiel für diesen Informationsfluss finden Sie unten. (Beachten Sie die Legende unten links, in der öffentliche und private Schlüssel angegeben sind.)

Abbildung zur Verwendung des privaten Serverschlüssels für die Anwendung beim Senden einer Nachricht.

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 einen Dritten zu senden, sodass der Empfänger überprüfen kann, wer sie gesendet hat.

Wenn ein Drittanbieter 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 entsprechenden privaten Schlüssel signiert worden sein und stammt daher vom erwarteten Absender.

Auf jwt.io/ finden Sie eine Vielzahl von Bibliotheken, die die Signierung für Sie übernehmen können. Wir empfehlen Ihnen, diese Möglichkeit zu nutzen, wenn sie verfügbar ist. Der Vollständigkeit halber sehen wir uns an, wie Sie ein signiertes JWT manuell erstellen.

Web-Push-Benachrichtigungen und signierte JWTs

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

Abbildung der Strings in einem JSON-Webtoken.

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

Der erste String enthält Informationen zum JWT selbst und gibt an, welcher Algorithmus zum Erstellen der Signatur verwendet wurde.

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

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

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

Für Web-Push-Benachrichtigungen haben die Daten dieses 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-Benachrichtigungen ist die Zielgruppe der Push-Dienst. Wir legen sie also auf den Ursprung des Push-Dienstes fest.

Der Wert exp ist das Ablaufdatum des JWT. Dadurch wird verhindert, dass Unbefugte ein abgefangenes JWT wiederverwenden können. Das Ablaufdatum ist ein Zeitstempel in Sekunden und darf nicht länger als 24 Stunden sein.

In Node.js wird das Ablaufdatum so festgelegt:

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

Die Zeitspanne beträgt 12 Stunden statt 24 Stunden, um Probleme mit Zeitunterschieden zwischen der sendenden Anwendung und dem Push-Dienst zu vermeiden.

Der Wert sub muss entweder eine URL oder eine mailto-E-Mail-Adresse sein. So kann ein Push-Dienst, falls er sich an den Absender wenden muss, Kontaktinformationen aus dem JWT abrufen. Deshalb benötigte die Web-Push-Bibliothek eine E-Mail-Adresse.

Wie die JWT-Informationen werden auch die JWT-Daten als URL-sicherer Base64-String codiert.

Der dritte String, die Signatur, ist das Ergebnis der Verkettung der ersten beiden Strings (JWT-Informationen und JWT-Daten) mit einem Punktzeichen, das wir als „nicht signiertes Token“ bezeichnen, und der anschließenden Signierung.

Beim Signieren muss das „nicht signierte Token“ mit ES256 verschlüsselt werden. Gemäß der JWT-Spezifikation steht ES256 für „ECDSA using the P-256 curve and the SHA-256 hash algorithm“ (ECDSA mit der P-256-Kurve und dem SHA-256-Hash-Algorithmus). Mit Web Crypto 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 Anwendungsserver-Schlüssel validieren, um die Signatur zu entschlüsseln und dafür zu sorgen, dass der entschlüsselte String mit dem „nicht signierten Token“ (d.h. den ersten beiden Strings im JWT) übereinstimmt.

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

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

Im Web Push-Protokoll wird auch angegeben, dass der öffentliche Anwendungsserver-Schlüssel im Crypto-Key-Header als URL-sicherer Base64-codierter String mit vorangestelltem p256ecdsa= gesendet werden muss.

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

Nutzlastverschlüsselung

Als Nächstes sehen wir uns an, wie wir eine Nutzlast mit einer Push-Nachricht senden können, damit unsere Web-App beim Empfang einer Push-Nachricht 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.

Ein Vorteil von Web-Push-Benachrichtigungen ist, dass alle Push-Dienste dieselbe API (das Web-Push-Protokoll) verwenden. Entwickler müssen sich also nicht darum kümmern, welcher Push-Dienst verwendet wird. Sie können eine Anfrage im richtigen Format senden und erwarten, dass eine Push-Benachrichtigung gesendet wird. Der Nachteil ist, dass Entwickler möglicherweise Nachrichten an einen Push-Dienst senden, der nicht vertrauenswürdig ist. 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 des Nutzers geschützt.

Die Verschlüsselung der Nutzlast ist in der Spezifikation zur Nachrichtenverschlüsselung definiert.

Bevor wir uns die konkreten Schritte zum Verschlüsseln der Nutzlast einer Push-Nachricht ansehen, sollten wir einige Techniken 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 für die Verschlüsselung von Informationen.

ECDH: Elliptic Curve Diffie-Hellman-Schlüsselaustausch

Stellen Sie sich vor, es gibt zwei Personen, die Informationen austauschen möchten: Alice und Bob. Sowohl Alice als auch Bob haben eigene öffentliche und private Schlüssel. Katrin und Stefan tauschen ihre öffentlichen Schlüssel aus.

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“ ermöglichen, ein gemeinsames Secret „X“ zu erstellen.

Dies ist eine allgemeine Erklärung von ECDH. Wenn Sie mehr erfahren möchten, empfehlen wir Ihnen, sich dieses Video mit einer detaillierteren Übersicht zu ECDH anzusehen.

In Bezug auf den Code: Die meisten Sprachen / Plattformen enthalten Bibliotheken, mit denen sich diese Schlüssel einfach generieren lassen.

Im Knoten 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 prägnante Beschreibung von HKDF:

HKDF ist eine HMAC-basierte Schlüsselableitungsfunktion, die schwaches Schlüsselmaterial in kryptografisch starkes Schlüsselmaterial umwandelt. Sie kann beispielsweise verwendet werden, um gemeinsam genutzte Secrets, die über Diffie-Hellman ausgetauscht wurden, in Schlüsselmaterial umzuwandeln, das für die Verschlüsselung, Integritätsprüfung oder Authentifizierung geeignet ist.

Im Wesentlichen wird durch HKDF eine Eingabe, die nicht besonders sicher ist, sicherer gemacht.

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

In Node kann 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 stammt aus dem Artikel von Mat Scale.

Dies umfasst im Wesentlichen ECDH und HKDF.

ECDH ist eine sichere Methode, um öffentliche Schlüssel zu teilen und ein gemeinsames Secret zu generieren. HKDF ist eine Methode, um unsicheres Material sicher zu machen.

Diese wird bei der Verschlüsselung unserer Nutzlast verwendet. Als Nächstes sehen wir uns an, welche Daten wir als Eingabe verwenden und wie sie verschlüsselt werden.

Eingaben

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

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

Die Werte auth und p256dh werden aus einem PushSubscription abgerufen. Zur Erinnerung: Für ein Abo benötigen wir die folgenden Werte:

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

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

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

Der p256dh-Schlüssel 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 Schlüssel für das Abo 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üsselungsvorgangs 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 16 Byte zufälliger Daten umfassen. In NodeJS würden wir so einen Salt 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 Abonnementschlüssel als Eingaben sowie einem neu generierten Salt und einer Reihe lokaler Schlüssel können wir nun mit der Verschlüsselung beginnen.

Gemeinsames Secret

Als Erstes müssen Sie ein gemeinsames Secret mit dem öffentlichen Schlüssel des Abos und unserem neuen privaten Schlüssel erstellen. Einfach so).

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

Er wird im nächsten Schritt verwendet, um den Pseudo Random Key (PRK) zu berechnen.

Pseudozufälliger Schlüssel

Der Pseudo Random Key (PRK) ist die Kombination aus dem Auth-Secret des Push-Abos und dem gemeinsamen Secret, das wir gerade erstellt haben.

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 Content-Codierung suchen könnten. Mit \0 wird dem Ende des Puffers ein Byte mit dem Wert 0 hinzugefügt. Das ist so vorgesehen, da Browser, die die Nachricht entschlüsseln, so viele Byte für die Inhaltsverschlüsselung erwarten, gefolgt von einem Byte mit dem Wert 0 und dann den verschlüsselten Daten.

Bei unserem Pseudo-Random-Schlüssel werden einfach die Authentifizierung, das gemeinsame Secret und ein Teil der Codierungsinformationen durch HKDF geleitet, wodurch er kryptografisch stärker wird.

Kontext

Der „Kontext“ ist eine Reihe von Bytes, die später im Verschlüsselungsbrowser zur Berechnung von zwei Werten verwendet werden. Es handelt sich im Wesentlichen um 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 endgültige Kontextpuffer ist ein Label, die Anzahl der Bytes im öffentlichen Schlüssel des Abos, gefolgt vom Schlüssel selbst, dann die Anzahl der Bytes im lokalen öffentlichen Schlüssel, gefolgt vom Schlüssel selbst.

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

Inhaltsverschlüsselungsschlüssel und Nonce

Eine 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, der letztendlich zum Verschlüsseln der Nutzlast verwendet wird.

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

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 ausgeführt, wobei das Salt und der 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 unseren Nonce- und Inhaltsverschlüsselungsschlüssel.

Verschlüsselung durchführen

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

Wir erstellen einen AES128-Chiffre mit dem Inhaltsverschlüsselungsschlüssel als Schlüssel und dem Nonce als Initialisierungsvektor.

In Node wird das so gemacht:

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

Bevor wir die Nutzlast verschlüsseln, müssen wir festlegen, wie viel Padding wir am Anfang der Nutzlast hinzufügen möchten. Wir möchten Padding hinzufügen, um das Risiko zu verringern, dass Unbefugte anhand der Nutzlastgröße „Arten“ von Nachrichten erkennen können.

Sie müssen zwei Byte Auffüllung hinzufügen, um die Länge der zusätzlichen Auffüllung anzugeben.

Wenn Sie beispielsweise kein Padding hinzugefügt haben, haben Sie zwei Byte mit dem Wert 0.Das bedeutet, dass kein Padding vorhanden ist. Nach diesen beiden Byte lesen Sie die Nutzlast. Wenn Sie 5 Byte Padding hinzugefügt haben, haben die ersten beiden Byte den Wert 5. Der Empfänger liest dann weitere 5 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 unser Padding und unsere Nutzlast durch diese Verschlüsselung.

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 muss nur noch festgelegt werden, wie diese Nutzlast an den Push-Dienst gesendet wird.

Verschlüsselte Nutzlast-Header und ‑Textkörper

Damit wir diese verschlüsselte Nutzlast an den Push-Dienst senden können, 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 wurde.

Das 16‑Byte-Salt sollte Base64-URL-sicher codiert und dem Verschlüsselungsheader hinzugefügt werden:

Encryption: salt=[URL Safe Base64 Encoded Salt]

Crypto-Key-Header

Wir haben gesehen, dass der Header Crypto-Key im Abschnitt „Application Server Keys“ (Anwendungsserver-Schlüssel) verwendet wird, um den öffentlichen Anwendungsserver-Schlüssel zu enthalten.

Mit diesem Header wird auch der lokale öffentliche Schlüssel geteilt, der zum Verschlüsseln der Nutzlast verwendet wird.

Der resultierende Header sieht 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 Header Content-Length gibt die Anzahl der Byte in der verschlüsselten Nutzlast an. Die Header „Content-Type“ und „Content-Encoding“ sind feste Werte. Dies wird unten dargestellt.

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

Wenn diese Header festgelegt sind, müssen wir die verschlüsselte Nutzlast als Text unserer Anfrage senden. Beachten Sie, dass Content-Type auf application/octet-stream festgelegt ist. Das liegt daran, dass die verschlüsselte Nutzlast als Byte-Stream gesendet werden muss.

In NodeJS würden wir so vorgehen:

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

Weitere Überschriften?

Wir haben die Header behandelt, die für JWT-/Anwendungsserver-Schlüssel verwendet werden (d.h. wie die Anwendung beim Push-Dienst identifiziert wird), und die Header, die zum Senden einer verschlüsselten Nutzlast verwendet werden.

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 TTL) ist eine Ganzzahl, die die Anzahl der Sekunden angibt, die Ihre Push-Nachricht im Push-Dienst verbleiben soll, bevor sie zugestellt wird. Wenn die TTL abläuft, wird die Nachricht aus der Push-Service-Warteschlange entfernt und nicht zugestellt.

TTL: [Time to live in seconds]

Wenn Sie einen TTL von null festlegen, versucht der Push-Dienst, die Nachricht sofort zuzustellen. Wenn das Gerät jedoch nicht erreicht werden kann, wird Ihre Nachricht sofort aus der Warteschlange des Push-Dienstes entfernt.

Technisch gesehen kann ein Push-Dienst die TTL einer Push-Nachricht reduzieren, wenn er möchte. Sie können anhand des TTL-Headers in der Antwort eines Push-Dienstes feststellen, ob dies der Fall ist.

Thema

Optional

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

Das ist nützlich, wenn mehrere Nachrichten gesendet werden, während ein Gerät offline ist, und Sie möchten, dass ein Nutzer nur die letzte Nachricht sieht, wenn das Gerät eingeschaltet wird.

Dringlichkeit

Optional

Die Dringlichkeit gibt dem Push-Dienst an, wie wichtig eine Nachricht für den Nutzer ist. Der Push-Dienst kann diese Informationen nutzen, um die Akkulaufzeit des Geräts eines Nutzers zu verlängern, indem er das Gerät nur bei wichtigen Nachrichten aktiviert, wenn der Akku schwach ist.

Der Headerwert ist wie unten 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-Benachrichtigungen in der Web-Push-Libs-Organisation auslösen.

Sobald Sie eine verschlüsselte Nutzlast und die oben genannten Headern haben, müssen Sie nur noch eine POST-Anfrage an endpoint in einem PushSubscription senden.

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

Antwort des Push-Dienstes

Nachdem Sie eine Anfrage an einen Push-Dienst gesendet haben, müssen Sie den Statuscode der Antwort prüfen, um festzustellen, ob die Anfrage erfolgreich war.

Statuscode Beschreibung
201 Erstellt Die Anfrage zum Senden einer Push-Benachrichtigung wurde empfangen und akzeptiert.
429 Zu viele Anfragen. Das bedeutet, dass Ihr Anwendungsserver ein Ratenlimit für einen Push-Dienst erreicht hat. Der Push-Dienst sollte einen „Retry-After“-Header enthalten, um anzugeben, wie lange es dauert, bis eine weitere Anfrage gestellt werden kann.
400 Ungültige Anfrage. Das bedeutet in der Regel, dass einer Ihrer Header ungültig oder falsch formatiert ist.
404 Nicht gefunden. Dies ist ein Hinweis darauf, dass das Abo abgelaufen ist und nicht mehr verwendet werden kann. In diesem Fall sollten Sie das `PushSubscription` löschen und warten, bis der Client den Nutzer neu registriert.
410 Nicht bei uns! Das Abo ist nicht mehr gültig und sollte vom Anwendungsserver entfernt werden. Dieser Fehler kann reproduziert werden, indem `unsubscribe()` für ein `PushSubscription` aufgerufen wird.
413 Die Nutzlast 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 auch im Web Push-Standard (RFC8030).

Weitere Informationen

Code labs