वेब पुश प्रोटोकॉल

हमने देखा है कि पुश मैसेज को ट्रिगर करने के लिए कैसे लाइब्रेरी का इस्तेमाल किया जा सकता है, लेकिन क्या ये लाइब्रेरी काम कर रही हैं?

वे नेटवर्क अनुरोध कर रहे हैं और यह सुनिश्चित कर रहे हैं कि सही फ़ॉर्मैट. इस नेटवर्क अनुरोध की जानकारी देने वाला स्पेसिफ़िकेशन, वेब पुश प्रोटोकॉल है.

आपके सर्वर से पुश मैसेज को पुश पर भेजने का डायग्राम
सेवा

इस सेक्शन में बताया गया है कि सर्वर, ऐप्लिकेशन सर्वर पासकोड की मदद से अपनी पहचान कैसे कर सकता है. साथ ही, एन्क्रिप्ट किए गए पेलोड और उससे जुड़े डेटा को भेजने का तरीका भी बताया गया है.

यह वेब पुश का अच्छा पक्ष नहीं है और मैं एन्क्रिप्शन का विशेषज्ञ नहीं हूं. हालांकि, आइए हर चीज़ को देखें, क्योंकि यह जानना आसान है कि ये लाइब्रेरी अंदरूनी तौर पर क्या कर रही हैं.

ऐप्लिकेशन सर्वर कुंजियां

जब हम किसी उपयोगकर्ता की सदस्यता लेते हैं, तो हम applicationServerKey पास करते हैं. इस कुंजी को पुश सेवा को पास किया जाता है. साथ ही, इसका इस्तेमाल यह जांचने के लिए किया जाता है कि उपयोगकर्ता ने जिस ऐप्लिकेशन की सदस्यता ली है वह वही ऐप्लिकेशन है जो पुश मैसेज ट्रिगर कर रहा है.

जब हम कोई पुश मैसेज ट्रिगर करते हैं, तो हम हेडर का एक सेट भेजते हैं. इससे पुश सेवा को ऐप्लिकेशन की पुष्टि करने में मदद मिलती है. (इसके बारे में VAPID स्पेसिफ़िकेशन में बताया गया है.)

इसका क्या मतलब है और क्या होता है? ऐप्लिकेशन सर्वर की पुष्टि करने के लिए, ये चरण अपनाए जाते हैं:

  1. ऐप्लिकेशन सर्वर, निजी ऐप्लिकेशन पासकोड की मदद से, JSON फ़ॉर्मैट में दी गई कुछ जानकारी पर हस्ताक्षर करता है.
  2. हस्ताक्षर की गई यह जानकारी पुश सेवा को पीओएसटी अनुरोध में हेडर के तौर पर भेजी जाती है.
  3. पुश सेवा, सेव की गई सार्वजनिक कुंजी का इस्तेमाल करती है. यह कुंजी उसे मिली जानकारी पर हस्ताक्षर करने के लिए, pushManager.subscribe() सार्वजनिक पासकोड से जुड़ी निजी कुंजी होती है. याद रखें: सार्वजनिक पासकोड, subscribe कॉल में पास किया गया applicationServerKey होता है.
  4. अगर हस्ताक्षर की गई जानकारी सही है, तो पुश सेवा पुश उपयोगकर्ता को कोई मैसेज न मिले.

जानकारी के इस फ़्लो का एक उदाहरण नीचे दिया गया है. (यह बताने के लिए नीचे बाईं ओर मौजूद लेजेंड को नोट करें सार्वजनिक और निजी पासकोड.)

इस इलस्ट्रेशन में दिखाया गया है कि
मैसेज

अनुरोध में हेडर में जोड़ी गई "हस्ताक्षर की गई जानकारी", JSON वेब टोकन होती है.

JSON वेब टोकन

JSON वेब टोकन (या छोटा नाम JWT) का इस्तेमाल, तीसरे पक्ष को मैसेज भेजने के लिए किया जाता है. इससे, मैसेज पाने वाला व्यक्ति यह पुष्टि कर सकता है कि मैसेज किसने भेजा है.

जब तीसरे पक्ष को कोई मैसेज मिलता है, तो उसे मैसेज भेजने वाले लोगों की जानकारी चाहिए होती है सार्वजनिक पासकोड दबाएं और JWT के हस्ताक्षर की पुष्टि करने के लिए इसका इस्तेमाल करें. अगर हस्ताक्षर मान्य है फिर JWT में मेल खाने वाले हस्ताक्षर होने चाहिए निजी कुंजी होनी चाहिए, इसलिए इसे भेजने वाले की ओर से भेजा जाना चाहिए.

https://jwt.io/ पर इसके लिए कई लाइब्रेरी मौजूद हैं आपके लिए साइन इन कर सकता/सकती हूं और मेरा सुझाव है कि आप वहीं से ऐसा करें कर सकते हैं. आइए, देखते हैं कि हस्ताक्षर की गई JWT कैसे मैन्युअल तरीके से बनाई जाती है.

वेब पुश और हस्ताक्षर किए गए JWT

हस्ताक्षर वाला JWT सिर्फ़ एक स्ट्रिंग होती है. हालांकि, इसे बिंदुओं से जुड़ी तीन स्ट्रिंग के तौर पर भी देखा जा सकता है.

JSON वेब में स्ट्रिंग की इमेज
टोकन

पहली और दूसरी स्ट्रिंग (JWT की जानकारी और JWT का डेटा) ये हैं JSON जिसे base64 कोड में बदला गया है. इसका मतलब है कि इसे सार्वजनिक तौर पर पढ़ा जा सकता है.

पहली स्ट्रिंग में JWT के बारे में जानकारी होती है. इससे पता चलता है कि कौनसा एल्गोरिदम का इस्तेमाल हस्ताक्षर बनाने के लिए किया गया था.

वेब पुश के लिए JWT की जानकारी में यह जानकारी होनी चाहिए:

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

दूसरी स्ट्रिंग, JWT डेटा है. इससे JWT भेजने वाले व्यक्ति के बारे में जानकारी मिलती है. साथ ही, यह भी पता चलता है कि यह किसके लिए है और यह कितने समय के लिए मान्य है.

वेब पुश के लिए, डेटा का यह फ़ॉर्मैट होगा:

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

aud वैल्यू, "ऑडियंस" होती है. इसका मतलब है कि JWT किसके लिए है. वेब के लिए ऑडियंस, पुश सेवा है, इसलिए हम इसे पुश की शुरुआत की जगह पर सेट करते हैं सेवा.

exp वैल्यू, JWT की समयसीमा खत्म होने की तारीख होती है. इससे, स्नूपर्स को JWT को फिर से इस्तेमाल करने से रोका जा सकता है. समयसीमा खत्म होने की तारीख, टाइमस्टैंप है सेकंड और 24 घंटे से ज़्यादा का नहीं होना चाहिए.

Node.js में समयसीमा खत्म होने की तारीख, इन चीज़ों का इस्तेमाल करके सेट की जाती है:

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

यह 24 घंटे के बजाय 12 घंटे का होता है, ताकि मैसेज भेजने वाले ऐप्लिकेशन और पुश सेवा के बीच, घड़ी के अंतर से जुड़ी कोई समस्या न आए.

आखिर में, sub वैल्यू को यूआरएल या mailto ईमेल पता होना चाहिए. ऐसा इसलिए किया जाता है, ताकि अगर ईमेल भेजने वाले व्यक्ति से संपर्क करने की ज़रूरत पड़े, तो वह से संपर्क जानकारी मिलेगी. (इस वजह से, वेब-पुश लाइब्रेरी को ईमेल पते की ज़रूरत थी).

JWT की जानकारी की तरह ही, JWT डेटा को यूआरएल सेफ़ base64 स्ट्रिंग के तौर पर एन्कोड किया जाता है.

तीसरी स्ट्रिंग, हस्ताक्षर, पहली दो स्ट्रिंग (JWT की जानकारी और JWT डेटा) को बिंदु वर्ण से जोड़ने पर बनती है. इसे हम "बिना हस्ताक्षर वाला टोकन" कहेंगे.

हस्ताक्षर करने की प्रोसेस के लिए, ES256 का इस्तेमाल करके "बिना हस्ताक्षर वाला टोकन" एन्क्रिप्ट करना ज़रूरी है. JWT के स्पेसिफ़िकेशन के मुताबिक, ES256 का मतलब है "P-256 कर्व और SHA-256 हैश एल्गोरिदम का इस्तेमाल करने वाला ईसीडीए". वेब क्रिप्टो का इस्तेमाल करके, इस तरह से सिग्नेचर बनाया जा सकता है:

// 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);
});

पुश सेवा, सार्वजनिक ऐप्लिकेशन सर्वर कुंजी का इस्तेमाल करके JWT की पुष्टि कर सकती है ताकि हस्ताक्षर को डिक्रिप्ट किया जा सके. साथ ही, यह पक्का किया जा सके कि डिक्रिप्ट की गई स्ट्रिंग वही हो "बिना हस्ताक्षर वाले टोकन" के तौर पर (जैसे, JWT की पहली दो स्ट्रिंग).

हस्ताक्षर किया गया JWT (यानी बिंदु से जोड़ी गई सभी तीन स्ट्रिंग), वेब पर भेजा जाता है पुश सेवा को Authorization हेडर के तौर पर जोड़ने के लिए, WebPush को पहले जोड़ा जाता है, जैसे:

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

वेब पुश प्रोटोकॉल यह भी बताता है कि सार्वजनिक ऐप्लिकेशन सर्वर कुंजी को इसे Crypto-Key हेडर में, यूआरएल के हिसाब से सुरक्षित base64 कोड में बदली गई स्ट्रिंग के तौर पर भेजा गया है. p256ecdsa= उससे पहले जोड़ा गया.

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

पेलोड एन्क्रिप्शन

अब देखते हैं कि पुश मैसेज के साथ पेलोड कैसे भेजा जा सकता है, ताकि जब हमारे वेब ऐप्लिकेशन को पुश मैसेज मिले, तो वह मिलने वाले डेटा को ऐक्सेस कर सके.

अन्य पुश सेवाओं का उपयोग करने वाले लोगों में एक आम सवाल यह उठता है कि वेब क्या पेलोड को एन्क्रिप्ट (सुरक्षित) करना है? नेटिव ऐप्लिकेशन के लिए, पुश मैसेज में डेटा को सादे टेक्स्ट के तौर पर भेजा जा सकता है.

वेब पुश की खूबसूरती का एक हिस्सा यह भी है कि सभी पुश सेवाएं एक ही एपीआई (वेब पुश प्रोटोकॉल) के बिना, डेवलपर को इस बात की परवाह नहीं करनी होती कि पुश सर्विस है. हम सही फ़ॉर्मैट में अनुरोध कर सकते हैं. साथ ही, पुश मैसेज भेजने के लिए कहें. इसका नकारात्मक पहलू यह है कि डेवलपर ताकि किसी ऐसी पुश सेवा पर मैसेज भेजे जा सकें जो भरोसेमंद नहीं है. पेलोड को एन्क्रिप्ट करने पर, पुश सेवा भेजे गए डेटा को नहीं पढ़ सकती. सिर्फ़ ब्राउज़र इस जानकारी को डिक्रिप्ट कर सकता है. इससे उपयोगकर्ता की डेटा शामिल है.

मैसेज एन्क्रिप्शन के लिए स्पेसिफ़िकेशन में, पेलोड को एन्क्रिप्ट करने के बारे में बताया गया है.

पुश मैसेज पेलोड को एन्क्रिप्ट (सुरक्षित) करने के खास तरीके देखने से पहले, हमें कुछ ऐसी तकनीकों के बारे में बताना होगा जिनका इस्तेमाल एन्क्रिप्ट (सुरक्षित) करने के दौरान किया जाएगा प्रोसेस. (पुश एन्क्रिप्शन के बारे में बेहतरीन लेख लिखने के लिए, मैट स्केल को धन्यवाद.)

ईसीडीएच और HKDF

एईसीएच और एचकेडीएफ़, एन्क्रिप्शन की पूरी प्रोसेस में इस्तेमाल किए जाते हैं. साथ ही, जानकारी को एन्क्रिप्ट करने के लिए फ़ायदे भी देते हैं.

ईसीडीएच: एलिप्टिक कर्व डिफ़ी-हेलमैन की एक्सचेंज

मान लें कि ऐलिस और बॉब, दोनों को जानकारी शेयर करनी है. ऐलिस और बॉब, दोनों के पास अपनी सार्वजनिक और निजी कुंजियां होती हैं. एलिस और बॉब एक-दूसरे के साथ अपनी सार्वजनिक कुंजियां शेयर करें.

ईसीडीएच की मदद से जनरेट की गई कुंजियों की एक खास बात यह है कि ऐलिस, गुप्त वैल्यू 'X' बनाने के लिए अपनी निजी कुंजी और बॉब की सार्वजनिक कुंजी का इस्तेमाल कर सकती है. बॉब भी अपनी निजी कुंजी और ऐलिस की सार्वजनिक कुंजी का इस्तेमाल करके, वैल्यू 'X' को स्वतंत्र रूप से बना सकता है. इससे 'X' बन जाता है एक शेयर किया हुआ सीक्रेट साथ ही, ऐलिस और बॉब को सिर्फ़ अपनी सार्वजनिक कुंजी शेयर करनी पड़ी. अब बॉब और ऐलिस, एक-दूसरे के मैसेज को एन्क्रिप्ट और डिक्रिप्ट करने के लिए, 'X' का इस्तेमाल कर सकते हैं.

मेरी जानकारी के मुताबिक, ECDH उन कर्व के गुण बताता है जो इस "सुविधा" को अनुमति देते हैं कि एक शेयर गुप्त 'X' बनाया जा सकता है.

यह ईसीडीएच के बारे में खास जानकारी है. अगर आपको ज़्यादा जानना है, तो हमारा सुझाव है कि यह वीडियो देखें.

अगर कोड का इस्तेमाल किया जाए, तो; ज़्यादातर भाषाओं / प्लैटफ़ॉर्म में लाइब्रेरी की सुविधा मौजूद होती है, आपको इन पासकोड को जनरेट करना आसान हो जाएगा.

नोड में, हम ये काम करेंगे:

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

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

HKDF: एचएमएसी पर आधारित पासकोड बनाने का फ़ंक्शन

Wikipedia पर HKDF के बारे में कम शब्दों में जानकारी दी गई है:

एचकेडीएफ़, एचएमएसी पर आधारित कुंजी डेरिवेशन फ़ंक्शन है. यह किसी भी कमज़ोर कुंजी के कॉन्टेंट को क्रिप्टोग्राफ़िक तौर पर मज़बूत कुंजी के कॉन्टेंट में बदल देता है. उदाहरण के लिए, इसका इस्तेमाल डिफ़ी हेलमैन के ज़रिए शेयर किए गए गोपनीय डेटा को, एन्क्रिप्शन, इंटिग्रिटी की जांच या पुष्टि करने के लिए इस्तेमाल किए जाने वाले कुंजी कॉन्टेंट में बदलने के लिए किया जा सकता है.

दरअसल, HKDF वह इनपुट लेगा जो ज़्यादा सुरक्षित नहीं है. इससे उसे ज़्यादा सुरक्षित बनाया जा सकता है.

इस एन्क्रिप्शन की खास बात के लिए, हैश एल्गोरिदम के तौर पर SHA-256 का इस्तेमाल करना ज़रूरी है और वेब पुश में HKDF के नतीजे के तौर पर मिलने वाली कुंजियां 256 बिट से ज़्यादा नहीं होनी चाहिए (32 बाइट).

नोड में इसे इस तरह लागू किया जा सकता है:

// 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);
}

इस उदाहरण कोड के लिए मैट स्केल के लेख के लिए हैट टिप.

इसमें आम तौर पर ECDH के बारे में बताया गया है और HKDF.

ईसीडीएच, सार्वजनिक कुंजियों को शेयर करने और शेयर किया जाने वाला सीक्रेट कोड जनरेट करने का एक सुरक्षित तरीका है. HKDF बेहतरीन विकल्प है को सुरक्षित बनाया जा सकता है और उसे सुरक्षित बनाया जा सकता है.

इसका इस्तेमाल हमारे पेलोड को एन्क्रिप्ट (सुरक्षित) करने के दौरान किया जाएगा. चलिए, अब देखते हैं कि हम कैसे एन्क्रिप्ट करें.

इनपुट

जब हमें किसी उपयोगकर्ता को पेलोड के साथ पुश मैसेज भेजना हो, तो हमें तीन इनपुट की ज़रूरत होती है:

  1. पेलोड.
  2. PushSubscription से मिला auth सीक्रेट.
  3. PushSubscription से मिला p256dh बटन.

हमने देखा है कि auth और p256dh वैल्यू, PushSubscription से वापस लाई जा रही हैं. हालांकि, एक छोटा रिमाइंडर देने के लिए, हमें सदस्यता के लिए इन वैल्यू की ज़रूरत होगी:

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

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

auth वैल्यू को गोपनीय माना जाना चाहिए और इसे अपने ऐप्लिकेशन से बाहर शेयर नहीं किया जाना चाहिए.

p256dh कुंजी एक सार्वजनिक कुंजी है. इसे कभी-कभी क्लाइंट सार्वजनिक कुंजी भी कहा जाता है. यहां हम p256dh को सदस्यता की सार्वजनिक कुंजी मानेंगे. सदस्यता की सार्वजनिक कुंजी, ब्राउज़र जनरेट करता है. ब्राउज़र, निजी पासकोड को गुप्त रखेगा और इसका इस्तेमाल, पेलोड को डिक्रिप्ट करने के लिए करेगा.

इन तीन वैल्यू, auth, p256dh, और payload को इनपुट के तौर पर डालना ज़रूरी है. एन्क्रिप्शन की प्रोसेस के नतीजे के तौर पर, एन्क्रिप्ट किया गया पेलोड, सॉल्ट वैल्यू, और सार्वजनिक कुंजी मिलेगी. इसका इस्तेमाल सिर्फ़ डेटा को एन्क्रिप्ट करने के लिए किया जाता है.

सॉल्ट

सॉल्ट, 16 बाइट का कोई भी डेटा हो सकता है. NodeJS में, सॉल्ट बनाने के लिए हमें ये काम करने होंगे:

const salt = crypto.randomBytes(16);

सार्वजनिक / निजी कुंजियां

सार्वजनिक और निजी पासकोड, P-256 एलिप्टिक कर्व का इस्तेमाल करके जनरेट किए जाने चाहिए, जिसे हम नोड में करेंगे, जैसा:

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

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

हम इन कुंजियों को "स्थानीय कुंजियां" कहेंगे. इनका इस्तेमाल सिर्फ़ एन्क्रिप्ट (सुरक्षित) करने के लिए किया जाता है और ऐप्लिकेशन सर्वर कुंजियों के साथ कुछ भी नहीं करना.

इनपुट के तौर पर पेलोड, पुष्टि करने वाले सीक्रेट, और सदस्यता सार्वजनिक पासकोड के साथ और नए जनरेट किए गए और स्थानीय कुंजियों के सेट हैं, तो हम वास्तव में कुछ एन्क्रिप्शन करने के लिए तैयार हैं.

शेयर किया गया सीक्रेट

पहला चरण, सदस्यता की सार्वजनिक कुंजी और हमारी नई निजी कुंजी का इस्तेमाल करके, शेयर किया जाने वाला सीक्रेट कोड बनाना है. क्या आपको ऐलिस और बॉब के साथ ईसीडीएच के बारे में बताई गई बातें याद हैं? बस ऐसे ही).

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

इसका इस्तेमाल, अगले चरण में स्यूडो रैंडम की (पीआरके) को कैलकुलेट करने के लिए किया जाता है.

स्यूडो रैंडम कुंजी

बदली हुई रैंडम कुंजी (पीआरके), पुश सदस्यता की पुष्टि करने की प्रक्रिया का मिला-जुला रूप होती है और वे राज़ हैं जो हमने अभी-अभी बनाए हैं.

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

आपको शायद यह जानना हो कि Content-Encoding: auth\0 स्ट्रिंग का क्या काम है. कम शब्दों में कहें, तो इसका कोई साफ़ मकसद नहीं है, हालांकि ब्राउज़र आने वाले मैसेज को डिक्रिप्ट करें. साथ ही, कॉन्टेंट को उस कोड में बदलने की प्रोसेस देखें जिस पर उम्मीद की जा सकती है. \0, बफ़र के आखिर में 0 वैल्यू वाली एक बाइट जोड़ता है. यह है ब्राउज़र, उस संदेश को डिक्रिप्ट करते हुए अपेक्षित रूप से इतने बाइट की उम्मीद करेंगे को, कॉन्टेंट को कोड में बदलने के लिए, एक बाइट है. इसकी वैल्यू 0 है और इसके बाद, एन्क्रिप्ट (सुरक्षित) किया गया डेटा है.

हमारी स्यूडो रैंडम पासकोड, एचकेडीएफ़ की मदद से पुष्टि करने की प्रक्रिया, शेयर किए गए सीक्रेट कोड, और कोड में बदली गई जानकारी को चला रही है. इसका मतलब है कि इसे क्रिप्टोग्राफ़िक तरीके से ज़्यादा मज़बूत बनाया जा रहा है.

संदर्भ

"कॉन्टेक्स्ट" बाइट का एक सेट है, जिसका इस्तेमाल बाद में एन्क्रिप्शन में दो वैल्यू को कैलकुलेट करने के लिए किया जाता है ब्राउज़र खोलें. यह ज़रूरी बाइट का वह कलेक्शन है जिसमें सदस्यता की सार्वजनिक कुंजी और स्थानीय सार्वजनिक कुंजी है.

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,
]);

आखिरी संदर्भ बफ़र एक लेबल होता है, यानी सदस्यता सार्वजनिक कुंजी में कितनी बाइट, कुंजी के बाद, बाइट की संख्या स्थानीय सार्वजनिक कुंजी और उसके बाद कुंजी वह भी ऐसा कर सकता है.

इस कॉन्टेक्स्ट वैल्यू की मदद से, हम नॉन्स और कॉन्टेंट एन्क्रिप्शन की कुंजी बनाने में इसका इस्तेमाल कर सकते हैं (सीईके).

कॉन्टेंट एन्क्रिप्शन पासकोड और नॉन्स

नॉन्स एक ऐसी वैल्यू है जिससे रीप्ले अटैक को रोका जा सकता है. इसका इस्तेमाल सिर्फ़ एक बार किया जाना चाहिए.

कॉन्टेंट एन्क्रिप्शन पासकोड (सीईके) वह पासकोड होता है जिसका इस्तेमाल, हमारे पेलोड को एन्क्रिप्ट करने के लिए किया जाएगा.

सबसे पहले हमें नॉन्स और सीईके के लिए डेटा की बाइट बनानी होंगी, जो कि सिर्फ़ एक कॉन्टेंट है एन्कोडिंग स्ट्रिंग के बाद उस संदर्भ बफ़र का इस्तेमाल किया जाता है, जिसे हमने अभी कैलकुलेट किया है:

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]);

यह जानकारी HKDF के ज़रिए सॉल्ट और PRK को nonceInfo और cekInfo के साथ जोड़कर चलाया जाता है:

// 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);

इससे हमें नॉन्स और कॉन्टेंट को एन्क्रिप्ट (सुरक्षित) करने की कुंजी मिलती है.

एन्क्रिप्ट (सुरक्षित) करना

अब हमारे पास कॉन्टेंट एन्क्रिप्ट करने की कुंजी मौजूद है, इसलिए हम पेलोड को एन्क्रिप्ट (सुरक्षित) कर सकते हैं.

हमने कॉन्टेंट एन्क्रिप्ट करने की कुंजी का इस्तेमाल करके, AES128 साइफ़र बनाया है और नॉन्स एक इनिशलाइज़ेशन वेक्टर है.

Node में ऐसा करने के लिए, यह तरीका अपनाएं:

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

अपने पेलोड को एन्क्रिप्ट करने से पहले, हमें यह तय करना होगा कि पेलोड के आगे कितनी पैडिंग जोड़नी है. हम पैडिंग (जगह) को क्यों जोड़ना चाहेंगे इससे जासूसी करने वाले लोग यह पता लगा पाते हैं कि वे "टाइप" मैसेज दिखाए जा सकते हैं.

किसी अतिरिक्त पैडिंग (जगह) की लंबाई के बारे में बताने के लिए, आपको दो बाइट पैडिंग (जगह) जोड़नी होंगी.

उदाहरण के लिए, अगर आपने कोई पैडिंग नहीं जोड़ी है, तो आपके पास वैल्यू 0 वाले दो बाइट होंगे. इसका मतलब है कि कोई पैडिंग मौजूद नहीं है. इन दो बाइट के बाद, आपको पेलोड पढ़ना होगा. अगर आपने पांच बाइट पैडिंग जोड़ी है, तो पहले दो बाइट की वैल्यू पांच होगी. इसलिए, उपभोक्ता पांच और बाइट पढ़ेगा और फिर पेलोड पढ़ना शुरू करेगा.

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

इसके बाद, हम इस साइफ़र की मदद से अपनी पैडिंग (जगह) और पेलोड को रन करते हैं.

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()]);

अब हमारे पास एन्क्रिप्ट किया गया पेलोड है. वाह!

बस यह तय करना है कि इस पेलोड को पुश सेवा को कैसे भेजा जाए.

एन्क्रिप्ट (सुरक्षित) किए गए पेलोड हेडर और मुख्य हिस्सा

एन्क्रिप्ट किए गए इस पेलोड को पुश सेवा पर भेजने के लिए, हमें कुछ अलग-अलग हेडर का इस्तेमाल करें.

एन्क्रिप्शन हेडर

'एन्क्रिप्शन' हेडर में, पेलोड को एन्क्रिप्ट करने के लिए इस्तेमाल किया जाने वाला सॉल्ट होना चाहिए.

16 बाइट वाला सॉल्ट, base64 यूआरएल की सुरक्षा वाले कोड में बदलना चाहिए और उसे एन्क्रिप्ट करने के हेडर में जोड़ा जाना चाहिए. जैसे:

Encryption: salt=[URL Safe Base64 Encoded Salt]

क्रिप्टो-की हेडर

हमने देखा कि Crypto-Key हेडर का इस्तेमाल, 'ऐप्लिकेशन सर्वर पासकोड' सेक्शन में किया जाता है, ताकि ऐप्लिकेशन सर्वर की सार्वजनिक पासकोड शामिल की जा सके.

इस हेडर का इस्तेमाल, पेलोड को एन्क्रिप्ट करने के लिए इस्तेमाल की जाने वाली स्थानीय सार्वजनिक कुंजी को शेयर करने के लिए भी किया जाता है.

इससे मिलने वाला हेडर कुछ ऐसा दिखता है:

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

कॉन्टेंट का टाइप, लंबाई, और कोड में बदलने के हेडर

Content-Length हेडर, एन्क्रिप्ट किए गए पेलोड में बाइट की संख्या होती है. 'कॉन्टेंट का टाइप' और 'कॉन्टेंट-एन्कोडिंग' हेडर, तय किए गए मान होते हैं. इसे नीचे दिखाया गया है.

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

इन हेडर के सेट होने के बाद, हमें अपने अनुरोध के मुख्य हिस्से के तौर पर एन्क्रिप्ट (सुरक्षित) किया गया पेलोड भेजना होगा. ध्यान दें कि Content-Type application/octet-stream. ऐसा इसलिए है, क्योंकि एन्क्रिप्ट (सुरक्षित) किए गए पेलोड को बाइट की स्ट्रीम के तौर पर भेजा जाना चाहिए.

NodeJS में, हम यह काम इस तरह करेंगे:

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

क्या आपको ज़्यादा हेडर चाहिए?

हमने JWT / ऐप्लिकेशन सर्वर पासकोड के लिए इस्तेमाल किए जाने वाले हेडर के बारे में बताया है. जैसे, पुश सेवा की मदद से ऐप्लिकेशन की पहचान कैसे करें. साथ ही, हमने एन्क्रिप्ट किए गए पेलोड को भेजने के लिए इस्तेमाल किए जाने वाले हेडर के बारे में भी बताया है.

कुछ और हेडर भी हैं, जो सेवाओं को पुश करने वाली सेवाओं का इस्तेमाल करते हैं. इससे, भेजे गए मैसेज. इनमें से कुछ हेडर ज़रूरी हैं, जबकि दूसरे ज़रूरी नहीं हैं.

TTL हेडर

ज़रूरी है

TTL (या लाइव होने में लगने वाला समय) एक पूर्णांक है, जो सेकंड की संख्या बताता है आपका पुश मैसेज, पुश मैसेज शुरू होने से पहले लाइव हो जाए डिलीवर हो गया. TTL की समयसीमा खत्म होने पर, मैसेज को पुश सर्विस लिस्ट नहीं बनाई जाती और वह डिलीवर नहीं होता.

TTL: [Time to live in seconds]

अगर TTL को शून्य पर सेट किया जाता है, तो पुश सेवा तुरंत मैसेज डिलीवर करने की कोशिश करेगी. हालांकि, अगर डिवाइस तक पहुंचा नहीं जा सकता, तो आपका मैसेज पुश सेवा की सूची से तुरंत हटा दिया जाएगा.

तकनीकी तौर पर, पुश सेवा चाहे, तो पुश मैसेज के TTL को कम कर सकती है. TTL हेडर की जांच करके यह बताया जा सकता है कि ऐसा हुआ है या नहीं CANNOT TRANSLATE

विषय

ज़रूरी नहीं

विषय ऐसी स्ट्रिंग होती हैं जिनका इस्तेमाल करके अटके हुए मैसेज को नया मैसेज तब दिखेगा, जब उनमें विषय के नाम से मेल खाते होंगे.

यह उन स्थितियों में उपयोगी होता है जब एक से अधिक संदेश भेजे जाते हैं डिवाइस ऑफ़लाइन है और आप वाकई में एक उपयोगकर्ता को केवल नवीनतम डिवाइस चालू होने पर एक मैसेज दिखाई देगा.

अत्यावश्यकता

ज़रूरी नहीं

'ज़रूरत' से पुश सेवा को पता चलता है कि उपयोगकर्ता के लिए कोई मैसेज कितना ज़रूरी है. पुश सेवा इस जानकारी का इस्तेमाल करके, उपयोगकर्ता के डिवाइस की बैटरी लाइफ़ को बचा सकती है. इसके लिए, वह सिर्फ़ बैटरी कम होने पर ज़रूरी मैसेज के लिए डिवाइस को चालू करती है.

हेडर वैल्यू को इस तरह से तय किया जाता है, जैसा कि यहां दिखाया गया है. डिफ़ॉल्ट वैल्यू normal है.

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

सब कुछ एक साथ

अगर इन सभी के काम करने के तरीके के बारे में आपका कोई और सवाल है, तो देखें कि लाइब्रेरी कैसे ट्रिगर होती हैं web-push-libs org पर मैसेज पुश करना.

एन्क्रिप्ट किया गया पेलोड और ऊपर दिए गए हेडर मिलने के बाद, आपको PushSubscription में endpoint पर सिर्फ़ एक पोस्ट अनुरोध करना होगा.

तो इस POST अनुरोध के जवाब का क्या करें?

पुश सेवा से मिला जवाब

पुश सेवा का अनुरोध करने के बाद, आपको जवाब का स्टेटस कोड देखना होगा. इससे आपको पता चलेगा कि अनुरोध पूरा हुआ है या नहीं.

स्थिति कोड ब्यौरा
201 बनाया गया. पुश मैसेज भेजने का अनुरोध मिल गया है और उसे स्वीकार कर लिया गया है.
429 बहुत सारे अनुरोध मिले हैं. इसका मतलब है कि आपके ऐप्लिकेशन सर्वर की दर पूरी हो गई है सीमा तक लगाना पड़ता है. पुश सेवा में 'फिर से कोशिश करें' शामिल होना चाहिए हेडर का इस्तेमाल करें. इससे पता चलता है कि दूसरा अनुरोध करने में कितना समय लगेगा.
400 अनुरोध अमान्य है. आम तौर पर इसका मतलब है कि आपका कोई एक हेडर अमान्य है या का फ़ॉर्मैट सही नहीं है.
404 नहीं मिला. इससे पता चलता है कि आपकी सदस्यता खत्म हो गई है इसका इस्तेमाल नहीं किया जा सकता. इस मामले में, आपको `PushSubscription` को मिटा देना चाहिए और क्लाइंट के उपयोगकर्ता की फिर से सदस्यता लेने का इंतज़ार करना चाहिए.
410 नहीं दिख रहा. सदस्यता अब मान्य नहीं है और इसे हटा दिया जाना चाहिए ऐप्लिकेशन सर्वर से. इसे कॉल करके फिर से बनाया जा सकता है `PushSubscription` पर `unsubscribe()`.
413 पेलोड का साइज़ बहुत बड़ा है. पुश सेवा के लिए पेलोड का कम से कम साइज़ सहायता 4096 बाइट है (या 4 केबी).

एचटीटीपी स्टेटस कोड के बारे में ज़्यादा जानने के लिए, वेब पुश स्टैंडर्ड (RFC8030) भी पढ़ें.

आगे कहां जाना है

कोड लैब