हमने देखा है कि पुश मैसेज को ट्रिगर करने के लिए लाइब्रेरी का इस्तेमाल कैसे किया जा सकता है, लेकिन ये लाइब्रेरी असल में क्या कर रही हैं?
वे नेटवर्क से जुड़े अनुरोध कर रहे हैं और पक्का कर रहे हैं कि ऐसे अनुरोध सही फ़ॉर्मैट में हैं. वेब पुश प्रोटोकॉल, इस नेटवर्क अनुरोध के बारे में बताता है.
यह सेक्शन बताता है कि सर्वर, ऐप्लिकेशन सर्वर कुंजियों के साथ अपनी पहचान कैसे कर सकता है. साथ ही, यह भी बताता है कि एन्क्रिप्ट (सुरक्षित) किए गए पेलोड और उससे जुड़े डेटा को कैसे भेजा जाता है.
यह किसी वेब पुश के लिए अच्छा नहीं है और मैं एन्क्रिप्शन में कोई विशेषज्ञ नहीं हूं.
ऐप्लिकेशन सर्वर कुंजियां
जब हम किसी उपयोगकर्ता की सदस्यता लेते हैं, तो हम एक applicationServerKey
पास करते हैं. इस कुंजी को
पुश सेवा को भेज दिया जाता है. साथ ही, इसका इस्तेमाल यह जांचने के लिए किया जाता है कि जिस ऐप्लिकेशन ने उपयोगकर्ता की सदस्यता ली है
वह भी ऐसा ऐप्लिकेशन है जो पुश मैसेज ट्रिगर कर रहा है.
जब हम किसी पुश मैसेज को ट्रिगर करते हैं, तो हमारे भेजे जाने वाले हेडर का एक सेट होता है, जो पुश सेवा को ऐप्लिकेशन की पुष्टि करने की अनुमति देता है. (इसके बारे में VAPID स्पेसिफ़िकेशन से बताया गया है.)
इन सबका क्या मतलब है और असल में क्या होता है? ऐप्लिकेशन सर्वर की पुष्टि करने के लिए ये कदम उठाए जाते हैं:
- ऐप्लिकेशन सर्वर अपने निजी ऐप्लिकेशन कुंजी से कुछ JSON जानकारी पर हस्ताक्षर करता है.
- हस्ताक्षर की गई यह जानकारी, पुश सेवा को पोस्ट अनुरोध के हेडर के तौर पर भेजी जाती है.
- पुश सेवा,
pushManager.subscribe()
से मिली सेव की गई सार्वजनिक कुंजी का इस्तेमाल करती है, ताकि यह जांच की जा सके कि मिली जानकारी पर सार्वजनिक कुंजी से जुड़ी निजी कुंजी से हस्ताक्षर किया गया है या नहीं. याद रखें: सार्वजनिक कुंजी, सदस्यता कॉल में पास किया गयाapplicationServerKey
होता है. - अगर हस्ताक्षर की गई जानकारी मान्य है, तो पुश सेवा उपयोगकर्ता को पुश मैसेज भेजती है.
जानकारी के इस फ़्लो का एक उदाहरण नीचे दिया गया है. (सार्वजनिक और निजी कुंजियों को दिखाने के लिए नीचे बाईं ओर मौजूद लेजेंड पर ध्यान दें.)
अनुरोध के हेडर में जोड़ी गई "साइन की गई जानकारी", JSON वेब टोकन है.
JSON का वेब टोकन
JSON वेब टोकन (या कम शब्दों में JWT) किसी तीसरे पक्ष को मैसेज भेजने का एक तरीका है. इससे, ईमेल पाने वाला यह पुष्टि कर सकता है कि मैसेज किसने भेजा है.
जब किसी तीसरे पक्ष को मैसेज मिलता है, तो उसे भेजने वाले की सार्वजनिक कुंजी हासिल करनी होगी और उसका इस्तेमाल JWT के हस्ताक्षर की पुष्टि करने के लिए करना होगा. अगर हस्ताक्षर मान्य है, तो JWT को मेल खाने वाली निजी कुंजी से हस्ताक्षर किया जाना चाहिए. साथ ही, यह भी ज़रूरी है कि यह न्योता भेजने वाले व्यक्ति से ही हो.
https://jwt.io/ पर कई लाइब्रेरी मौजूद हैं जो साइन इन कर सकती हैं. हमारा सुझाव है कि आप ऐसा करें. पूरी जानकारी के लिए, साइन किए गए JWT बनाने का तरीका देखते हैं.
वेब पुश और साइन किए गए JWT
साइन किया गया JWT सिर्फ़ एक स्ट्रिंग है. हालांकि, इसे तीन स्ट्रिंग के तौर पर माना जा सकता है जो बिंदुओं से जुड़ी होती हैं.
पहली और दूसरी स्ट्रिंग (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 की जानकारी की तरह ही, JWT डेटा को यूआरएल के हिसाब से सुरक्षित base64 स्ट्रिंग के तौर पर एन्कोड किया जाता है.
तीसरी स्ट्रिंग, सिग्नेचर, पहली दो स्ट्रिंग (JWT की जानकारी और JWT डेटा) को लेने का नतीजा है. इन स्ट्रिंग को एक डॉट कैरेक्टर के साथ जोड़कर बनाया जाता है. इसे हम "बिना हस्ताक्षर वाले टोकन" के नाम से कॉल करेंगे और उस पर हस्ताक्षर करेंगे.
हस्ताक्षर करने की प्रोसेस में, ES256 का इस्तेमाल करके "बिना हस्ताक्षर वाले टोकन" को एन्क्रिप्ट (सुरक्षित) करना ज़रूरी होता है. JWT की जानकारी के मुताबिक, "P-256 कर्व और SHA-256 हैश एल्गोरिदम का इस्तेमाल करने वाले ECDSA" के लिए, ES256 छोटा है. वेब क्रिप्टो का इस्तेमाल करके ऐसा हस्ताक्षर बनाया जा सकता है:
// 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]
पेलोड एन्क्रिप्ट (सुरक्षित) करने का तरीका
अब जानते हैं कि हम पुश मैसेज के साथ पेलोड कैसे भेज सकते हैं, ताकि जब हमारे वेब ऐप्लिकेशन को कोई पुश मैसेज मिले, तो वह उसे मिलने वाले डेटा को ऐक्सेस कर सके.
अन्य पुश सेवाओं का इस्तेमाल करने वाले किसी भी व्यक्ति के मन में यह सवाल उठता है कि वेब पुश पेलोड को एन्क्रिप्ट (सुरक्षित) करने की ज़रूरत क्यों है? नेटिव ऐप्लिकेशन में, पुश मैसेज की मदद से डेटा को सादे टेक्स्ट के तौर पर भेजा जा सकता है.
वेब पुश की खास बात यह है कि सभी पुश सेवाएं एक ही एपीआई (वेब पुश प्रोटोकॉल) का इस्तेमाल करती हैं. इसलिए, डेवलपर को इस बात से फ़र्क़ नहीं पड़ता कि पुश सेवा कौन है. हम सही फ़ॉर्मैट में अनुरोध कर सकते हैं और उनसे एक पुश मैसेज भेजे जाने की उम्मीद कर सकते हैं. इसमें एक समस्या यह है कि डेवलपर ऐसे पुश सेवा को शायद मैसेज भेज पाएं जो भरोसेमंद न हो. पेलोड को एन्क्रिप्ट (सुरक्षित) करने से, पुश सेवा भेजे गए डेटा को नहीं पढ़ पाती. सिर्फ़ ब्राउज़र इस जानकारी को डिक्रिप्ट कर सकता है. यह उपयोगकर्ता के डेटा को सुरक्षित रखता है.
पेलोड को एन्क्रिप्ट (सुरक्षित) करने के बारे में, मैसेज के एन्क्रिप्शन की खास जानकारी में बताया गया है.
पुश मैसेज पेलोड को एन्क्रिप्ट (सुरक्षित) करने के खास चरणों को देखने से पहले, हमें कुछ ऐसी तकनीकों को कवर करना होगा जिनका इस्तेमाल एन्क्रिप्ट (सुरक्षित) करने की प्रोसेस के दौरान किया जाएगा. (पुश एन्क्रिप्ट करने के बारे में इस शानदार लेख के लिए, मैट स्केल्स को शानदार हैट टिप दी है.)
ECDH और HKDF
ECDH और HKDF, दोनों का इस्तेमाल एन्क्रिप्ट (सुरक्षित) करने की पूरी प्रक्रिया के दौरान किया जाता है. साथ ही, जानकारी को एन्क्रिप्ट (सुरक्षित) करने के मकसद से फ़ायदे भी मिलते हैं.
ECDH: एलिप्टिक कर्व डिफ़ी-हेलमैन की एक्सचेंज
मान लें कि आपके पास जानकारी शेयर करने के लिए दो लोग हैं, ऐलिस और बॉब. ऐलिस और बॉब, दोनों के पास अपनी सार्वजनिक और निजी कुंजियां होती हैं. ऐलिस और बॉब अपनी सार्वजनिक कुंजियां एक-दूसरे के साथ शेयर करते हैं.
ईसीडीएच की मदद से जनरेट की गई कुंजियों का इस्तेमाल यह पता करने के लिए किया जाता है कि ऐलिस अपनी निजी कुंजी और बॉब की सार्वजनिक कुंजी का इस्तेमाल करके, सीक्रेट वैल्यू 'X' बना सकती हैं. बॉब भी ऐसा ही कर सकता है. वह अपनी निजी कुंजी और ऐलिस की सार्वजनिक कुंजी लेकर स्वतंत्र रूप से वही मान 'X' बना सकता है. इससे 'X' एक साझा राज़ बन जाता है और ऐलिस और बॉब को केवल अपनी सार्वजनिक कुंजी साझा करनी पड़ती थी. अब बॉब और ऐलिस अपने बीच के मैसेज को एन्क्रिप्ट और डिक्रिप्ट करने के लिए 'X' का इस्तेमाल कर सकते हैं.
ECDH में, मेरी जानकारी के मुताबिक कर्व के प्रॉपर्टी के बारे में बताया गया है, जो शेयर किए जाने वाले सीक्रेट 'X' को बनाने की इस "सुविधा" को अनुमति देता है.
यह ECDH के बारे में बेहतर जानकारी है. अगर आपको ज़्यादा जानकारी चाहिए, तो मेरा सुझाव है कि आप यह वीडियो देखें.
कोड की बात करें, तो ज़्यादातर भाषाओं / प्लैटफ़ॉर्म में लाइब्रेरी होती हैं, ताकि इन कुंजियों को जनरेट करना आसान हो जाता है.
नोड में हम ये काम करेंगे:
const keyCurve = crypto.createECDH('prime256v1');
keyCurve.generateKeys();
const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();
HKDF: एचएमएसी आधारित कुंजी डेरिवेशन फ़ंक्शन
Wikipedia में HKDF का कम शब्दों में ब्यौरा दिया गया है:
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 में कमियां शामिल होती हैं.
ECDH, सार्वजनिक कुंजियों को शेयर करने और एक सीक्रेट सीक्रेट जनरेट करने का सुरक्षित तरीका है. HKDF असुरक्षित सामग्री को चुनने और उसे सुरक्षित बनाने का एक तरीका है.
इसका इस्तेमाल हमारे पेलोड को एन्क्रिप्ट (सुरक्षित) करने के दौरान किया जाएगा. अब जानते हैं कि हम इनपुट के तौर पर किस चीज़ को स्वीकार करते हैं और उसे कैसे एन्क्रिप्ट किया जाता है.
इनपुट
जब हम पेलोड वाले किसी उपयोगकर्ता को पुश मैसेज भेजना चाहते हैं, तो हमें तीन इनपुट की ज़रूरत होती है:
- खुद पेलोड.
PushSubscription
काauth
सीक्रेट.PushSubscription
से मिलीp256dh
कुंजी.
हमने देखा है कि PushSubscription
से auth
और p256dh
की वैल्यू वापस आती हैं. हालांकि, हम आपको याद दिलाना चाहते हैं कि
सदस्यता को ध्यान में रखते हुए, हमें इन वैल्यू की ज़रूरत होगी:
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 वाली बाइट होती है
और उसके बाद,
एन्क्रिप्ट किया गया डेटा भेजा जाता है.
हमारी सूडो रैंडम कुंजी बस HKDF के ज़रिए पुष्टि करने वाला, शेयर किया गया सीक्रेट, और कोड में बदलने की जानकारी का एक हिस्सा चला रही है (यानी इसे क्रिप्टोग्राफ़िक तरीके से मज़बूत बनाना).
संदर्भ
"context", बाइट का एक ऐसा सेट होता है जिसका इस्तेमाल, बाद में एन्क्रिप्शन ब्राउज़र में दो वैल्यू का हिसाब लगाने के लिए किया जाता है. यह असल में बाइट की एक कैटगरी है, जिसमें सदस्यता की सार्वजनिक कुंजी और लोकल सार्वजनिक कुंजी होती है.
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,
]);
फ़ाइनल कॉन्टेक्स्ट बफ़र एक लेबल होता है. यह सदस्यता की सार्वजनिक कुंजी में मौजूद बाइट की संख्या होती है. इसके बाद कुंजी, फिर लोकल सार्वजनिक कुंजी, और उसके बाद बाइट की संख्या होती है.
इस कॉन्टेक्स्ट वैल्यू की मदद से, हम इसका इस्तेमाल नॉन्स और कॉन्टेंट एन्क्रिप्ट (सुरक्षित) करने की कुंजी (सीईके) बनाने में कर सकते हैं.
कॉन्टेंट को एन्क्रिप्ट (सुरक्षित) करने की कुंजी और नॉन्स
nonce ऐसी वैल्यू है जो रीप्ले की प्रोसेस को रोकती है, क्योंकि इसका इस्तेमाल सिर्फ़ एक बार किया जाना चाहिए.
कॉन्टेंट एन्क्रिप्ट (सुरक्षित) करने की कुंजी (सीईके) वह कुंजी है जिसका इस्तेमाल आखिर में, हमारे पेलोड को एन्क्रिप्ट (सुरक्षित) करने के लिए किया जाएगा.
सबसे पहले हमें नॉन्स और सीईके के लिए डेटा की बाइट बनानी होंगी, जो सिर्फ़ कॉन्टेंट को कोड में बदलने वाली स्ट्रिंग होती है. इसके बाद, कॉन्टेक्स्ट बफ़र की ज़रूरत होती है, जिसका हमने अभी हिसाब लगाया है:
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 के ज़रिए मिलती है. इसमें नमक और पीआरके को 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 साइफ़र बनाते हैं और नॉन्स एक इनिशलाइज़ेशन वेक्टर होता है.
नोड में यह इस तरह किया जाता है:
const cipher = crypto.createCipheriv(
'id-aes128-GCM',
contentEncryptionKey,
nonce,
);
अपने पेलोड को एन्क्रिप्ट करने से पहले, हमें यह तय करना होगा कि हमें पेलोड के सामने कितनी पैडिंग (जगह) जोड़नी है. हम पैडिंग (जगह) जोड़ने की वजह यह है कि इससे पेलोड के साइज़ के आधार पर, छिपकर देखने वाले लोगों को मैसेज के "टाइप" का पता लगाने का जोखिम नहीं रहता.
किसी भी अतिरिक्त पैडिंग की लंबाई बताने के लिए, आपको दो बाइट पैडिंग (जगह) जोड़नी होगी.
उदाहरण के लिए, अगर आपने कोई पैडिंग (जगह) नहीं जोड़ी है, तो आपके पास 0 वैल्यू वाली दो बाइट होंगी, यानी कोई पैडिंग (जगह) मौजूद नहीं है. इन दो बाइट के बाद, पेलोड को पढ़ा जाएगा. अगर आपने 5 बाइट की पैडिंग (जगह) जोड़ी है, तो पहली दो बाइट की वैल्यू 5 होगी. इससे, उपभोक्ता अतिरिक्त पांच बाइट और फिर पेलोड पढ़ना शुरू करेगा.
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
हेडर की जांच करके, यह पता लगाया जा सकता है कि ऐसा हुआ है या नहीं.
विषय
ज़रूरी नहीं
विषय ऐसी स्ट्रिंग होती हैं जिनका इस्तेमाल बाकी बचे मैसेज को नए मैसेज से बदलने के लिए किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि उनके नाम मेल खाते हों.
यह उस स्थिति में फ़ायदेमंद होता है जब डिवाइस के ऑफ़लाइन होने पर एक से ज़्यादा मैसेज भेजे जाते हैं और आप चाहते हैं कि डिवाइस चालू होने पर उपयोगकर्ता को सिर्फ़ नया मैसेज दिखे.
अत्यावश्यकता
ज़रूरी नहीं
ज़रूरी सूचना, पुश सर्विस को बताती है कि उपयोगकर्ता के लिए कोई मैसेज कितना ज़रूरी है. उपयोगकर्ता के डिवाइस की बैटरी लाइफ़ बचाने के लिए, पुश सेवा इसका इस्तेमाल कर सकती है. ऐसा सिर्फ़ तब किया जा सकता है, जब बैटरी कम हो.
हेडर की वैल्यू इस तरह तय की गई है: डिफ़ॉल्ट वैल्यू
normal
है.
Urgency: [very-low | low | normal | high]
सब कुछ एक साथ
अगर इन सभी के काम करने के तरीके के बारे में आपका कोई और सवाल है, तो आप हमेशा यह देख सकते हैं कि लाइब्रेरी किस तरह web-push-libs org पर पुश मैसेज को ट्रिगर करती हैं.
एन्क्रिप्ट (सुरक्षित) किया गया पेलोड और ऊपर दिए गए हेडर मिलने के बाद, आपको बस PushSubscription
में endpoint
के लिए, पोस्ट अनुरोध करना होगा.
तो हम इस पोस्ट अनुरोध का जवाब क्या करेंगे?
पुश सेवा से मिला जवाब
पुश सेवा का अनुरोध करने के बाद, आपको रिस्पॉन्स का स्टेटस कोड देखना होगा. इससे पता चलेगा कि अनुरोध पूरा हुआ या नहीं.
स्थिति कोड | ब्यौरा |
---|---|
201 | बनाया गया. एक पुश मैसेज भेजने का अनुरोध मिला और उसे स्वीकार कर लिया गया है. |
429 | बहुत सारे अनुरोध. इसका मतलब है कि पुश सेवा की मदद से आपका ऐप्लिकेशन सर्वर रेट की सीमा तक पहुंच गया है. पुश सर्विस में 'फिर से कोशिश करें' हेडर शामिल होना चाहिए, ताकि यह पता चल सके कि दूसरा अनुरोध किए जाने में कितने समय बाद. |
400 | अनुरोध अमान्य है. आम तौर पर इसका मतलब यह है कि आपका कोई हेडर अमान्य है या गलत तरीके से फ़ॉर्मैट किया गया है. |
404 | नहीं मिला. इससे पता चलता है कि सदस्यता की समयसीमा खत्म हो गई है और इसका इस्तेमाल नहीं किया जा सकता. इस मामले में, आपको `pushSubscription` को मिटा देना चाहिए और क्लाइंट की ओर से उपयोगकर्ता की फिर से सदस्यता लेने का इंतज़ार करना चाहिए. |
410 | हो गया. सदस्यता अब मान्य नहीं है और इसे ऐप्लिकेशन सर्वर से हटा दिया जाना चाहिए. ` PushSubscription` पर, `unsubscribe()` को कॉल करके, इसे फिर से बनाया जा सकता है. |
413 | पेलोड का साइज़ बहुत बड़ा है. किसी पुश सर्विस के लिए, कम से कम साइज़ का पेलोड 4096 बाइट (या 4 केबी) होना चाहिए. |
इसके बाद कहां जाना है
- वेब पुश नोटिफ़िकेशन की खास जानकारी
- पुश की सुविधा कैसे काम करती है
- किसी उपयोगकर्ता की सदस्यता लेना
- अनुमति के लिए UX
- वेब पुश लाइब्रेरी से मैसेज भेजना
- वेब पुश प्रोटोकॉल
- पुश इवेंट मैनेज करना
- सूचनाएं दिखाना
- सूचना के व्यवहार
- सूचनाओं के सामान्य पैटर्न
- पुश नोटिफ़िकेशन के बारे में अक्सर पूछे जाने वाले सवाल
- सामान्य समस्याएं और गड़बड़ियों की शिकायत करना