Chrome 50-এর আগে, পুশ বার্তাগুলিতে কোনও পেলোড ডেটা থাকতে পারে না। যখন 'পুশ' ইভেন্টটি আপনার পরিষেবা কর্মীকে বরখাস্ত করা হয়েছিল, আপনি কেবলমাত্র জানতেন যে সার্ভার আপনাকে কিছু বলার চেষ্টা করছে, কিন্তু এটি কী হতে পারে তা নয়। তারপরে আপনাকে সার্ভারে একটি ফলো-আপ অনুরোধ করতে হয়েছিল এবং দেখানোর জন্য বিজ্ঞপ্তির বিশদটি পেতে হয়েছিল, যা খারাপ নেটওয়ার্ক পরিস্থিতিতে ব্যর্থ হতে পারে।
এখন Chrome 50 এ (এবং ডেস্কটপে ফায়ারফক্সের বর্তমান সংস্করণে) আপনি পুশ সহ কিছু নির্বিচারে ডেটা পাঠাতে পারেন যাতে ক্লায়েন্ট অতিরিক্ত অনুরোধ করা এড়াতে পারে। যাইহোক, মহান শক্তির সাথে মহান দায়িত্ব আসে, তাই সমস্ত পেলোড ডেটা এনক্রিপ্ট করা আবশ্যক।
পেলোডগুলির এনক্রিপশন ওয়েব পুশের জন্য নিরাপত্তা গল্পের একটি গুরুত্বপূর্ণ অংশ। ব্রাউজার এবং আপনার নিজের সার্ভারের মধ্যে যোগাযোগ করার সময় HTTPS আপনাকে নিরাপত্তা দেয়, কারণ আপনি সার্ভারকে বিশ্বাস করেন। যাইহোক, ব্রাউজার চয়ন করে যে কোন পুশ প্রদানকারীকে প্রকৃতপক্ষে পেলোড সরবরাহ করতে ব্যবহার করা হবে, তাই অ্যাপ বিকাশকারী হিসাবে আপনার এটির উপর কোন নিয়ন্ত্রণ নেই।
এখানে, HTTPS শুধুমাত্র গ্যারান্টি দিতে পারে যে পুশ পরিষেবা প্রদানকারীর কাছে ট্রানজিট করার সময় কেউ বার্তাটি স্নুপ করতে পারবে না। একবার তারা এটি পেয়ে গেলে, তারা তৃতীয় পক্ষের কাছে পেলোডটি পুনরায় প্রেরণ করা বা অন্য কিছুতে দূষিতভাবে পরিবর্তন করা সহ তারা যা পছন্দ করে তা করতে স্বাধীন। এর বিরুদ্ধে সুরক্ষার জন্য আমরা এনক্রিপশন ব্যবহার করি যাতে পুশ পরিষেবাগুলি ট্রানজিটে পেলোডগুলি পড়তে বা বিকৃত করতে না পারে।
ক্লায়েন্ট-সাইড পরিবর্তন
আপনি যদি ইতিমধ্যেই পেলোড ছাড়াই পুশ বিজ্ঞপ্তিগুলি প্রয়োগ করে থাকেন তবে ক্লায়েন্ট-সাইডে আপনাকে শুধুমাত্র দুটি ছোট পরিবর্তন করতে হবে।
এটি প্রথমে হল যে আপনি যখন আপনার ব্যাকএন্ড সার্ভারে সাবস্ক্রিপশন তথ্য পাঠান তখন আপনাকে কিছু অতিরিক্ত তথ্য সংগ্রহ করতে হবে। আপনি যদি ইতিমধ্যেই PushSubscription অবজেক্টে JSON.stringify()
ব্যবহার করেন আপনার সার্ভারে পাঠানোর জন্য এটিকে সিরিয়ালাইজ করতে তাহলে আপনাকে কিছু পরিবর্তন করতে হবে না। সাবস্ক্রিপশনে এখন কী বৈশিষ্ট্যে কিছু অতিরিক্ত ডেটা থাকবে।
> JSON.stringify(subscription)
{"endpoint":"https://android.googleapis.com/gcm/send/f1LsxkKphfQ:APA91bFUx7ja4BK4JVrNgVjpg1cs9lGSGI6IMNL4mQ3Xe6mDGxvt_C_gItKYJI9CAx5i_Ss6cmDxdWZoLyhS2RJhkcv7LeE6hkiOsK6oBzbyifvKCdUYU7ADIRBiYNxIVpLIYeZ8kq_A",
"keys":{"p256dh":"BLc4xRzKlKORKWlbdgFaBrrPK3ydWAHo4M0gs0i1oEKgPpWC5cW8OCzVrOQRv-1npXRWk8udnW3oYhIO4475rds=",
"auth":"5I2Bu2oKdyy9CwL8QVF0NQ=="}}
দুটি মান p256dh
এবং auth
বেস64-এর একটি ভেরিয়েন্টে এনকোড করা হয়েছে যেটিকে আমি URL-Safe Base64 বলব।
আপনি যদি পরিবর্তে বাইটগুলিতে ডান পেতে চান তবে আপনি সাবস্ক্রিপশনে নতুন getKey()
পদ্ধতি ব্যবহার করতে পারেন যা একটি ArrayBuffer
হিসাবে একটি প্যারামিটার প্রদান করে। আপনার যে দুটি প্যারামিটার প্রয়োজন তা হল auth
এবং p256dh
।
> new Uint8Array(subscription.getKey('auth'));
[228, 141, 129, ...] (16 bytes)
> new Uint8Array(subscription.getKey('p256dh'));
[4, 183, 56, ...] (65 bytes)
দ্বিতীয় পরিবর্তনটি হল একটি নতুন ডেটা বৈশিষ্ট্য যখন push
ইভেন্ট ফায়ার হয়। এটিতে প্রাপ্ত ডেটা পার্স করার জন্য বিভিন্ন সিঙ্ক্রোনাস পদ্ধতি রয়েছে, যেমন .text()
, .json()
, .arrayBuffer()
এবং .blob()
।
self.addEventListener('push', function(event) {
if (event.data) {
console.log(event.data.json());
}
});
সার্ভার-সাইড পরিবর্তন
সার্ভারের দিকে, জিনিসগুলি একটু বেশি পরিবর্তিত হয়। মূল প্রক্রিয়াটি হল আপনি পেলোড এনক্রিপ্ট করতে ক্লায়েন্টের কাছ থেকে পাওয়া এনক্রিপশন কী তথ্য ব্যবহার করেন এবং তারপরে কিছু অতিরিক্ত HTTP শিরোনাম যোগ করে সাবস্ক্রিপশনের শেষ পয়েন্টে একটি POST অনুরোধের বডি হিসেবে পাঠান।
বিশদগুলি তুলনামূলকভাবে জটিল, এবং এনক্রিপশন সম্পর্কিত যে কোনও কিছুর মতো আপনার নিজের রোল করার চেয়ে সক্রিয়ভাবে বিকাশ করা লাইব্রেরি ব্যবহার করা ভাল। Chrome টিম Node.js-এর জন্য একটি লাইব্রেরি প্রকাশ করেছে, আরও ভাষা এবং প্ল্যাটফর্ম শীঘ্রই আসছে৷ এটি এনক্রিপশন এবং ওয়েব পুশ প্রোটোকল উভয়ই পরিচালনা করে, যাতে একটি Node.js সার্ভার থেকে একটি পুশ বার্তা পাঠানো webpush.sendWebPush(message, subscription)
এর মতোই সহজ।
যদিও আমরা অবশ্যই একটি লাইব্রেরি ব্যবহার করার পরামর্শ দিই, এটি একটি নতুন বৈশিষ্ট্য এবং এমন অনেক জনপ্রিয় ভাষা রয়েছে যেগুলির এখনও কোনও লাইব্রেরি নেই৷ আপনি যদি নিজের জন্য এটি বাস্তবায়ন করতে চান তবে এখানে বিশদ রয়েছে।
আমি নোড-স্বাদযুক্ত জাভাস্ক্রিপ্ট ব্যবহার করে অ্যালগরিদমগুলি চিত্রিত করব, তবে মৌলিক নীতিগুলি যে কোনও ভাষায় একই হওয়া উচিত।
ইনপুট
একটি বার্তা এনক্রিপ্ট করার জন্য, আমাদের প্রথমে সাবস্ক্রিপশন অবজেক্ট থেকে দুটি জিনিস পেতে হবে যা আমরা ক্লায়েন্টের কাছ থেকে পেয়েছি। আপনি যদি ক্লায়েন্টে JSON.stringify()
ব্যবহার করেন এবং সেটি আপনার সার্ভারে প্রেরণ করেন তাহলে ক্লায়েন্টের সর্বজনীন কী keys.p256dh
ক্ষেত্রে সংরক্ষণ করা হয়, যখন ভাগ করা প্রমাণীকরণ গোপনীয়তা keys.auth
ক্ষেত্রে থাকে। এই দুটিই URL-নিরাপদ বেস64 এনকোড করা হবে, যেমন উপরে উল্লিখিত হয়েছে। ক্লায়েন্ট পাবলিক কী-এর বাইনারি বিন্যাস হল একটি অসংকুচিত P-256 উপবৃত্তাকার বক্ররেখা বিন্দু।
const clientPublicKey = new Buffer(subscription.keys.p256dh, 'base64');
const clientAuthSecret = new Buffer(subscription.keys.auth, 'base64');
পাবলিক কী আমাদের বার্তাটিকে এনক্রিপ্ট করার অনুমতি দেয় যাতে এটি শুধুমাত্র ক্লায়েন্টের ব্যক্তিগত কী ব্যবহার করে ডিক্রিপ্ট করা যায়।
সর্বজনীন কীগুলিকে সাধারণত, ভাল, সর্বজনীন হিসাবে বিবেচনা করা হয়, তাই ক্লায়েন্টকে প্রমাণীকরণ করার অনুমতি দেওয়ার জন্য যে বার্তাটি একটি বিশ্বস্ত সার্ভার দ্বারা পাঠানো হয়েছে আমরা প্রমাণীকরণ গোপনীয়তাও ব্যবহার করি। আশ্চর্যজনকভাবে, এটি গোপন রাখা উচিত, শুধুমাত্র সেই অ্যাপ্লিকেশন সার্ভারের সাথে ভাগ করা উচিত যা আপনি আপনাকে বার্তা পাঠাতে চান এবং একটি পাসওয়ার্ডের মতো আচরণ করা উচিত৷
আমাদের কিছু নতুন ডেটাও তৈরি করতে হবে। আমাদের একটি 16-বাইট ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত এলোমেলো লবণ এবং উপবৃত্তাকার কার্ভ কীগুলির একটি পাবলিক/প্রাইভেট জোড়া দরকার৷ পুশ এনক্রিপশন স্পেক দ্বারা ব্যবহৃত বিশেষ বক্ররেখাকে P-256 বা prime256v1
বলা হয়। সর্বোত্তম নিরাপত্তার জন্য আপনি যখনই একটি বার্তা এনক্রিপ্ট করবেন তখনই মূল জোড়াটি স্ক্র্যাচ থেকে তৈরি করা উচিত এবং আপনার কখনই লবণ পুনরায় ব্যবহার করা উচিত নয়।
ইসিডিএইচ
উপবৃত্তাকার বক্ররেখা ক্রিপ্টোগ্রাফির একটি ঝরঝরে সম্পত্তি সম্পর্কে কথা বলার জন্য আসুন একটু দূরে নিয়ে যাই। তুলনামূলকভাবে সহজ প্রক্রিয়া আছে যা আপনার প্রাইভেট কীকে অন্য কারো পাবলিক কী-এর সাথে একত্রিত করে একটি মান বের করে। তাই কি? ঠিক আছে, যদি অন্য পক্ষ তাদের ব্যক্তিগত কী এবং আপনার সর্বজনীন কী নেয় তবে এটি একই মান অর্জন করবে!
এটি উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান (ECDH) কী চুক্তি প্রোটোকলের ভিত্তি, যা উভয় পক্ষকে একই ভাগ করা গোপন রাখতে দেয় যদিও তারা শুধুমাত্র পাবলিক কী বিনিময় করে। আমরা আমাদের প্রকৃত এনক্রিপশন কীটির ভিত্তি হিসাবে এই ভাগ করা গোপনীয়তাটি ব্যবহার করব৷
const crypto = require('crypto');
const salt = crypto.randomBytes(16);
// Node has ECDH built-in to the standard crypto library. For some languages
// you may need to use a third-party library.
const serverECDH = crypto.createECDH('prime256v1');
const serverPublicKey = serverECDH.generateKeys();
const sharedSecret = serverECDH.computeSecret(clientPublicKey);
HKDF
ইতিমধ্যে অন্য একটি সরাইয়া জন্য সময়. ধরা যাক যে আপনার কাছে কিছু গোপন ডেটা আছে যা আপনি একটি এনক্রিপশন কী হিসাবে ব্যবহার করতে চান, কিন্তু এটি ক্রিপ্টোগ্রাফিকভাবে যথেষ্ট সুরক্ষিত নয়৷ কম নিরাপত্তা সহ একটি গোপন বিষয়কে উচ্চ নিরাপত্তায় পরিণত করতে আপনি HMAC-ভিত্তিক কী ডেরিভেশন ফাংশন (HKDF) ব্যবহার করতে পারেন।
এটি যেভাবে কাজ করে তার একটি ফলাফল হল যে এটি আপনাকে যেকোন সংখ্যক বিটের গোপনীয়তা নিতে দেয় এবং আপনি যে হ্যাশিং অ্যালগরিদম ব্যবহার করেন তার দ্বারা উত্পাদিত হ্যাশের 255 গুণ পর্যন্ত যে কোনো আকারের আরেকটি গোপনীয়তা তৈরি করতে দেয়। পুশের জন্য, স্পেক এর জন্য আমাদের SHA-256 ব্যবহার করতে হবে, যার হ্যাশ দৈর্ঘ্য 32 বাইট (256 বিট)।
যেহেতু এটি ঘটে, আমরা জানি যে আমাদের কেবলমাত্র 32 বাইট পর্যন্ত আকারের কী তৈরি করতে হবে। এর মানে হল যে আমরা অ্যালগরিদমের একটি সরলীকৃত সংস্করণ ব্যবহার করতে পারি যা বড় আউটপুট আকারগুলি পরিচালনা করতে পারে না।
আমি নীচে একটি নোড সংস্করণের জন্য কোডটি অন্তর্ভুক্ত করেছি, তবে আপনি এটি RFC 5869 এ আসলে কীভাবে কাজ করে তা খুঁজে পেতে পারেন।
HKDF-এর ইনপুটগুলি হল একটি লবণ, কিছু প্রাথমিক কী করার উপাদান (ikm), বর্তমান ব্যবহারের ক্ষেত্রে (তথ্য) নির্দিষ্ট কাঠামোগত ডেটার একটি ঐচ্ছিক অংশ এবং পছন্দসই আউটপুট কীটির বাইটে দৈর্ঘ্য।
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
if (length > 32) {
throw new Error('Cannot return keys of more than 32 bytes, ${length} requested');
}
// 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);
}
এনক্রিপশন পরামিতি প্রাপ্ত
প্রকৃত এনক্রিপশনের জন্য আমাদের কাছে থাকা ডেটাকে প্যারামিটারে পরিণত করতে আমরা এখন HKDF ব্যবহার করি।
আমরা প্রথম যে কাজটি করি তা হল HKDF ব্যবহার করে ক্লায়েন্ট অথ সিক্রেট এবং শেয়ার করা সিক্রেটকে মিশ্রিত করে একটি দীর্ঘ, আরও ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত গোপনীয়তায়। বিশেষত্বে এটিকে সিউডো-র্যান্ডম কী (PRK) হিসাবে উল্লেখ করা হয়েছে তাই আমি এটিকে এখানে বলব, যদিও ক্রিপ্টোগ্রাফি বিশুদ্ধতাবাদীরা মনে রাখতে পারেন যে এটি কঠোরভাবে একটি PRK নয়।
এখন আমরা চূড়ান্ত বিষয়বস্তু এনক্রিপশন কী এবং একটি নন্স তৈরি করি যা সাইফারে পাঠানো হবে। এগুলি প্রতিটির জন্য একটি সাধারণ ডেটা কাঠামো তৈরি করে তৈরি করা হয়, যাকে একটি তথ্য হিসাবে উল্লেখ করা হয় , যাতে বার্তাটির উত্স আরও যাচাই করার জন্য তথ্যের উপবৃত্তাকার বক্ররেখা, প্রেরক এবং প্রাপকের জন্য নির্দিষ্ট তথ্য থাকে। তারপরে আমরা PRK, আমাদের লবণ এবং তথ্যের সাথে HKDF ব্যবহার করি সঠিক আকারের কী এবং ননস বের করতে।
বিষয়বস্তু এনক্রিপশনের জন্য তথ্যের ধরন হল 'aesgcm' যা পুশ এনক্রিপশনের জন্য ব্যবহৃত সাইফারের নাম।
const authInfo = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(clientAuthSecret, sharedSecret, authInfo, 32);
function createInfo(type, clientPublicKey, serverPublicKey) {
const len = type.length;
// The start index for each element within the buffer is:
// value | length | start |
// -----------------------------------------
// 'Content-Encoding: '| 18 | 0 |
// type | len | 18 |
// nul byte | 1 | 18 + len |
// 'P-256' | 5 | 19 + len |
// nul byte | 1 | 24 + len |
// client key length | 2 | 25 + len |
// client key | 65 | 27 + len |
// server key length | 2 | 92 + len |
// server key | 65 | 94 + len |
// For the purposes of push encryption the length of the keys will
// always be 65 bytes.
const info = new Buffer(18 + len + 1 + 5 + 1 + 2 + 65 + 2 + 65);
// The string 'Content-Encoding: ', as utf-8
info.write('Content-Encoding: ');
// The 'type' of the record, a utf-8 string
info.write(type, 18);
// A single null-byte
info.write('\0', 18 + len);
// The string 'P-256', declaring the elliptic curve being used
info.write('P-256', 19 + len);
// A single null-byte
info.write('\0', 24 + len);
// The length of the client's public key as a 16-bit integer
info.writeUInt16BE(clientPublicKey.length, 25 + len);
// Now the actual client public key
clientPublicKey.copy(info, 27 + len);
// Length of our public key
info.writeUInt16BE(serverPublicKey.length, 92 + len);
// The key itself
serverPublicKey.copy(info, 94 + len);
return info;
}
// Derive the Content Encryption Key
const contentEncryptionKeyInfo = createInfo('aesgcm', clientPublicKey, serverPublicKey);
const contentEncryptionKey = hkdf(salt, prk, contentEncryptionKeyInfo, 16);
// Derive the Nonce
const nonceInfo = createInfo('nonce', clientPublicKey, serverPublicKey);
const nonce = hkdf(salt, prk, nonceInfo, 12);
প্যাডিং
আরেকটি বাদ দিয়ে, এবং একটি নির্বোধ এবং কল্পিত উদাহরণের জন্য সময়। ধরা যাক যে আপনার বসের একটি সার্ভার রয়েছে যা তাকে প্রতি কয়েক মিনিটে কোম্পানির স্টক মূল্যের সাথে একটি পুশ বার্তা পাঠায়। এর জন্য সরল বার্তাটি সর্বদা সেন্টের মান সহ একটি 32-বিট পূর্ণসংখ্যা হবে। ক্যাটারিং স্টাফদের সাথেও তার একটি গোপন চুক্তি রয়েছে যার অর্থ হল যে তারা আসলে ডেলিভারির 5 মিনিট আগে তাকে স্ট্রিং "ব্রেক রুমে ডোনাটস" পাঠাতে পারে যাতে তারা পৌঁছানোর সময় "কাকতালীয়ভাবে" সেখানে উপস্থিত হতে পারে এবং সেরাটি ধরতে পারে৷
ওয়েব পুশ দ্বারা ব্যবহৃত সাইফারটি এনক্রিপ্ট করা মান তৈরি করে যা এনক্রিপ্ট করা ইনপুটের চেয়ে ঠিক 16 বাইট দীর্ঘ। যেহেতু "ডোনাটস ইন দ্য ব্রেক রুমে" 32-বিট স্টক মূল্যের চেয়ে দীর্ঘ, যেকোন স্নুপিং কর্মচারী বার্তাগুলিকে ডিক্রিপ্ট না করেই ডোনাটগুলি কখন আসছে তা কেবল ডেটার দৈর্ঘ্য থেকে বলতে সক্ষম হবে৷
এই কারণে, ওয়েব পুশ প্রোটোকল আপনাকে ডেটার শুরুতে প্যাডিং যুক্ত করতে দেয়। আপনি কীভাবে এটি ব্যবহার করবেন তা আপনার অ্যাপ্লিকেশনের উপর নির্ভর করে, কিন্তু উপরের উদাহরণে আপনি সমস্ত বার্তাগুলিকে ঠিক 32 বাইট হতে প্যাড করতে পারেন, যা শুধুমাত্র দৈর্ঘ্যের উপর ভিত্তি করে বার্তাগুলিকে আলাদা করা অসম্ভব করে তোলে৷
প্যাডিং মান হল একটি 16-বিট বিগ-এন্ডিয়ান পূর্ণসংখ্যা যা প্যাডিংয়ের দৈর্ঘ্যের পরে প্যাডিংয়ের NUL
বাইটের সংখ্যা উল্লেখ করে। তাই ন্যূনতম প্যাডিং হল দুটি বাইট - শূন্য সংখ্যাটি 16 বিটে এনকোড করা হয়েছে।
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeroes, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
যখন আপনার পুশ বার্তাটি ক্লায়েন্টের কাছে আসে, তখন ব্রাউজারটি স্বয়ংক্রিয়ভাবে যেকোনো প্যাডিং বের করে দিতে সক্ষম হবে, তাই আপনার ক্লায়েন্ট কোডটি কেবলমাত্র প্যাড না করা বার্তাটি পাবে।
এনক্রিপশন
এখন আমরা অবশেষে এনক্রিপশন করতে সব জিনিস আছে. ওয়েব পুশের জন্য প্রয়োজনীয় সাইফার হল GCM ব্যবহার করে AES128 । আমরা আমাদের বিষয়বস্তু এনক্রিপশন কী ব্যবহার করি কী হিসেবে এবং ননসটিকে ইনিশিয়ালাইজেশন ভেক্টর (IV) হিসেবে ব্যবহার করি।
এই উদাহরণে আমাদের ডেটা একটি স্ট্রিং, তবে এটি যেকোনো বাইনারি ডেটা হতে পারে। আপনি এনক্রিপশন তথ্যের জন্য 16-বাইট এবং প্যাডিংয়ের জন্য কমপক্ষে 2 বাইট সহ, 4078 বাইট - 4096 বাইট সর্বোচ্চ পোস্ট প্রতি পেলোড পাঠাতে পারেন।
// Create a buffer from our data, in this case a UTF-8 encoded string
const plaintext = new Buffer('Push notification payload!', 'utf8');
const cipher = crypto.createCipheriv('id-aes128-GCM', contentEncryptionKey,
nonce);
const result = cipher.update(Buffer.concat(padding, plaintext));
cipher.final();
// Append the auth tag to the result - https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
return Buffer.concat([result, cipher.getAuthTag()]);
ওয়েব পুশ
ফাউ! এখন আপনার কাছে একটি এনক্রিপ্ট করা পেলোড আছে, আপনাকে ব্যবহারকারীর সাবস্ক্রিপশন দ্বারা নির্দিষ্ট শেষ পয়েন্টে অপেক্ষাকৃত সহজ HTTP POST অনুরোধ করতে হবে।
আপনাকে তিনটি হেডার সেট করতে হবে।
Encryption: salt=<SALT>
Crypto-Key: dh=<PUBLICKEY>
Content-Encoding: aesgcm
<SALT>
এবং <PUBLICKEY>
হল এনক্রিপশনে ব্যবহৃত সল্ট এবং সার্ভার পাবলিক কী, URL-নিরাপদ বেস64 হিসাবে এনকোড করা।
ওয়েব পুশ প্রোটোকল ব্যবহার করার সময়, POST এর মূল অংশটি তখন এনক্রিপ্ট করা বার্তার কাঁচা বাইট। যাইহোক, যতক্ষণ না Chrome এবং Firebase ক্লাউড মেসেজিং প্রোটোকল সমর্থন করে, আপনি সহজেই আপনার বিদ্যমান JSON পেলোডে ডেটা অন্তর্ভুক্ত করতে পারেন।
{
"registration_ids": [ "…" ],
"raw_data": "BIXzEKOFquzVlr/1tS1bhmobZ…"
}
rawData
সম্পত্তির মান অবশ্যই এনক্রিপ্ট করা বার্তার base64 এনকোডেড উপস্থাপনা হতে হবে।
ডিবাগিং / যাচাইকারী
পিটার বেভারলু, ক্রোম ইঞ্জিনিয়ারদের মধ্যে একজন যারা বৈশিষ্ট্যটি বাস্তবায়ন করেছেন (সেইসাথে সেই ব্যক্তিদের মধ্যে একজন যারা বিশেষত্বে কাজ করেছেন), একটি যাচাইকারী তৈরি করেছেন ।
এনক্রিপশনের প্রতিটি মধ্যবর্তী মানের আউটপুট করার জন্য আপনার কোড পেয়ে আপনি সেগুলি যাচাইকারীতে পেস্ট করতে পারেন এবং আপনি সঠিক পথে আছেন কিনা তা পরীক্ষা করতে পারেন।
,Chrome 50-এর আগে, পুশ বার্তাগুলিতে কোনও পেলোড ডেটা থাকতে পারে না। যখন 'পুশ' ইভেন্টটি আপনার পরিষেবা কর্মীকে বরখাস্ত করা হয়েছিল, আপনি কেবলমাত্র জানতেন যে সার্ভার আপনাকে কিছু বলার চেষ্টা করছে, কিন্তু এটি কী হতে পারে তা নয়। তারপরে আপনাকে সার্ভারে একটি ফলো-আপ অনুরোধ করতে হয়েছিল এবং দেখানোর জন্য বিজ্ঞপ্তির বিশদটি পেতে হয়েছিল, যা খারাপ নেটওয়ার্ক পরিস্থিতিতে ব্যর্থ হতে পারে।
এখন Chrome 50 এ (এবং ডেস্কটপে ফায়ারফক্সের বর্তমান সংস্করণে) আপনি পুশ সহ কিছু নির্বিচারে ডেটা পাঠাতে পারেন যাতে ক্লায়েন্ট অতিরিক্ত অনুরোধ করা এড়াতে পারে। যাইহোক, মহান শক্তির সাথে মহান দায়িত্ব আসে, তাই সমস্ত পেলোড ডেটা এনক্রিপ্ট করা আবশ্যক।
পেলোডগুলির এনক্রিপশন ওয়েব পুশের জন্য নিরাপত্তা গল্পের একটি গুরুত্বপূর্ণ অংশ। ব্রাউজার এবং আপনার নিজের সার্ভারের মধ্যে যোগাযোগ করার সময় HTTPS আপনাকে নিরাপত্তা দেয়, কারণ আপনি সার্ভারকে বিশ্বাস করেন। যাইহোক, ব্রাউজার চয়ন করে যে কোন পুশ প্রদানকারীকে প্রকৃতপক্ষে পেলোড সরবরাহ করতে ব্যবহার করা হবে, তাই অ্যাপ বিকাশকারী হিসাবে আপনার এটির উপর কোন নিয়ন্ত্রণ নেই।
এখানে, HTTPS শুধুমাত্র গ্যারান্টি দিতে পারে যে পুশ পরিষেবা প্রদানকারীর কাছে ট্রানজিট করার সময় কেউ বার্তাটি স্নুপ করতে পারবে না। একবার তারা এটি পেয়ে গেলে, তারা তৃতীয় পক্ষের কাছে পেলোডটি পুনরায় প্রেরণ করা বা অন্য কিছুতে দূষিতভাবে পরিবর্তন করা সহ তারা যা পছন্দ করে তা করতে স্বাধীন। এর বিরুদ্ধে সুরক্ষার জন্য আমরা এনক্রিপশন ব্যবহার করি যাতে পুশ পরিষেবাগুলি ট্রানজিটে পেলোডগুলি পড়তে বা বিকৃত করতে না পারে।
ক্লায়েন্ট-সাইড পরিবর্তন
আপনি যদি ইতিমধ্যেই পেলোড ছাড়াই পুশ বিজ্ঞপ্তিগুলি প্রয়োগ করে থাকেন তবে ক্লায়েন্ট-সাইডে আপনাকে শুধুমাত্র দুটি ছোট পরিবর্তন করতে হবে।
এটি প্রথমে হল যে আপনি যখন আপনার ব্যাকএন্ড সার্ভারে সাবস্ক্রিপশন তথ্য পাঠান তখন আপনাকে কিছু অতিরিক্ত তথ্য সংগ্রহ করতে হবে। আপনি যদি ইতিমধ্যেই PushSubscription অবজেক্টে JSON.stringify()
ব্যবহার করেন আপনার সার্ভারে পাঠানোর জন্য এটিকে সিরিয়ালাইজ করতে তাহলে আপনাকে কিছু পরিবর্তন করতে হবে না। সাবস্ক্রিপশনে এখন কী বৈশিষ্ট্যে কিছু অতিরিক্ত ডেটা থাকবে।
> JSON.stringify(subscription)
{"endpoint":"https://android.googleapis.com/gcm/send/f1LsxkKphfQ:APA91bFUx7ja4BK4JVrNgVjpg1cs9lGSGI6IMNL4mQ3Xe6mDGxvt_C_gItKYJI9CAx5i_Ss6cmDxdWZoLyhS2RJhkcv7LeE6hkiOsK6oBzbyifvKCdUYU7ADIRBiYNxIVpLIYeZ8kq_A",
"keys":{"p256dh":"BLc4xRzKlKORKWlbdgFaBrrPK3ydWAHo4M0gs0i1oEKgPpWC5cW8OCzVrOQRv-1npXRWk8udnW3oYhIO4475rds=",
"auth":"5I2Bu2oKdyy9CwL8QVF0NQ=="}}
দুটি মান p256dh
এবং auth
বেস64-এর একটি ভেরিয়েন্টে এনকোড করা হয়েছে যেটিকে আমি URL-Safe Base64 বলব।
আপনি যদি পরিবর্তে বাইটগুলিতে ডান পেতে চান তবে আপনি সাবস্ক্রিপশনে নতুন getKey()
পদ্ধতি ব্যবহার করতে পারেন যা একটি ArrayBuffer
হিসাবে একটি প্যারামিটার প্রদান করে। আপনার যে দুটি প্যারামিটার প্রয়োজন তা হল auth
এবং p256dh
।
> new Uint8Array(subscription.getKey('auth'));
[228, 141, 129, ...] (16 bytes)
> new Uint8Array(subscription.getKey('p256dh'));
[4, 183, 56, ...] (65 bytes)
দ্বিতীয় পরিবর্তনটি হল একটি নতুন ডেটা বৈশিষ্ট্য যখন push
ইভেন্ট ফায়ার হয়। এটিতে প্রাপ্ত ডেটা পার্স করার জন্য বিভিন্ন সিঙ্ক্রোনাস পদ্ধতি রয়েছে, যেমন .text()
, .json()
, .arrayBuffer()
এবং .blob()
।
self.addEventListener('push', function(event) {
if (event.data) {
console.log(event.data.json());
}
});
সার্ভার-সাইড পরিবর্তন
সার্ভারের দিকে, জিনিসগুলি একটু বেশি পরিবর্তিত হয়। মূল প্রক্রিয়াটি হল আপনি পেলোড এনক্রিপ্ট করতে ক্লায়েন্টের কাছ থেকে পাওয়া এনক্রিপশন কী তথ্য ব্যবহার করেন এবং তারপরে কিছু অতিরিক্ত HTTP শিরোনাম যোগ করে সাবস্ক্রিপশনের শেষ পয়েন্টে একটি POST অনুরোধের বডি হিসেবে পাঠান।
বিশদগুলি তুলনামূলকভাবে জটিল, এবং এনক্রিপশন সম্পর্কিত যে কোনও কিছুর মতো আপনার নিজের রোল করার চেয়ে সক্রিয়ভাবে বিকাশ করা লাইব্রেরি ব্যবহার করা ভাল। Chrome টিম Node.js-এর জন্য একটি লাইব্রেরি প্রকাশ করেছে, আরও ভাষা এবং প্ল্যাটফর্ম শীঘ্রই আসছে৷ এটি এনক্রিপশন এবং ওয়েব পুশ প্রোটোকল উভয়ই পরিচালনা করে, যাতে একটি Node.js সার্ভার থেকে একটি পুশ বার্তা পাঠানো webpush.sendWebPush(message, subscription)
এর মতোই সহজ।
যদিও আমরা অবশ্যই একটি লাইব্রেরি ব্যবহার করার পরামর্শ দিই, এটি একটি নতুন বৈশিষ্ট্য এবং এমন অনেক জনপ্রিয় ভাষা রয়েছে যেগুলির এখনও কোনও লাইব্রেরি নেই৷ আপনি যদি নিজের জন্য এটি বাস্তবায়ন করতে চান তবে এখানে বিশদ রয়েছে।
আমি নোড-স্বাদযুক্ত জাভাস্ক্রিপ্ট ব্যবহার করে অ্যালগরিদমগুলি চিত্রিত করব, তবে মৌলিক নীতিগুলি যে কোনও ভাষায় একই হওয়া উচিত।
ইনপুট
একটি বার্তা এনক্রিপ্ট করার জন্য, আমাদের প্রথমে সাবস্ক্রিপশন অবজেক্ট থেকে দুটি জিনিস পেতে হবে যা আমরা ক্লায়েন্টের কাছ থেকে পেয়েছি। আপনি যদি ক্লায়েন্টে JSON.stringify()
ব্যবহার করেন এবং সেটি আপনার সার্ভারে প্রেরণ করেন তাহলে ক্লায়েন্টের সর্বজনীন কী keys.p256dh
ক্ষেত্রে সংরক্ষণ করা হয়, যখন ভাগ করা প্রমাণীকরণ গোপনীয়তা keys.auth
ক্ষেত্রে থাকে। এই দুটিই URL-নিরাপদ বেস64 এনকোড করা হবে, যেমন উপরে উল্লিখিত হয়েছে। ক্লায়েন্ট পাবলিক কী-এর বাইনারি বিন্যাস হল একটি অসংকুচিত P-256 উপবৃত্তাকার বক্ররেখা বিন্দু।
const clientPublicKey = new Buffer(subscription.keys.p256dh, 'base64');
const clientAuthSecret = new Buffer(subscription.keys.auth, 'base64');
পাবলিক কী আমাদের বার্তাটিকে এনক্রিপ্ট করার অনুমতি দেয় যাতে এটি শুধুমাত্র ক্লায়েন্টের ব্যক্তিগত কী ব্যবহার করে ডিক্রিপ্ট করা যায়।
সর্বজনীন কীগুলিকে সাধারণত, ভাল, সর্বজনীন হিসাবে বিবেচনা করা হয়, তাই ক্লায়েন্টকে প্রমাণীকরণ করার অনুমতি দেওয়ার জন্য যে বার্তাটি একটি বিশ্বস্ত সার্ভার দ্বারা পাঠানো হয়েছে আমরা প্রমাণীকরণ গোপনীয়তাও ব্যবহার করি। আশ্চর্যজনকভাবে, এটি গোপন রাখা উচিত, শুধুমাত্র সেই অ্যাপ্লিকেশন সার্ভারের সাথে ভাগ করা উচিত যা আপনি আপনাকে বার্তা পাঠাতে চান এবং একটি পাসওয়ার্ডের মতো আচরণ করা উচিত৷
আমাদের কিছু নতুন ডেটাও তৈরি করতে হবে। আমাদের একটি 16-বাইট ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত এলোমেলো লবণ এবং উপবৃত্তাকার কার্ভ কীগুলির একটি পাবলিক/প্রাইভেট জোড়া দরকার৷ পুশ এনক্রিপশন স্পেক দ্বারা ব্যবহৃত বিশেষ বক্ররেখাকে P-256 বা prime256v1
বলা হয়। সর্বোত্তম নিরাপত্তার জন্য আপনি যখনই একটি বার্তা এনক্রিপ্ট করবেন তখনই মূল জোড়াটি স্ক্র্যাচ থেকে তৈরি করা উচিত এবং আপনার কখনই লবণ পুনরায় ব্যবহার করা উচিত নয়।
ইসিডিএইচ
উপবৃত্তাকার বক্ররেখা ক্রিপ্টোগ্রাফির একটি ঝরঝরে সম্পত্তি সম্পর্কে কথা বলার জন্য আসুন একটু দূরে নিয়ে যাই। তুলনামূলকভাবে সহজ প্রক্রিয়া আছে যা আপনার প্রাইভেট কীকে অন্য কারো পাবলিক কী-এর সাথে একত্রিত করে একটি মান বের করে। তাই কি? ঠিক আছে, যদি অন্য পক্ষ তাদের ব্যক্তিগত কী এবং আপনার সর্বজনীন কী নেয় তবে এটি একই মান অর্জন করবে!
এটি উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান (ECDH) কী চুক্তি প্রোটোকলের ভিত্তি, যা উভয় পক্ষকে একই ভাগ করা গোপন রাখতে দেয় যদিও তারা শুধুমাত্র পাবলিক কী বিনিময় করে। আমরা আমাদের প্রকৃত এনক্রিপশন কীটির ভিত্তি হিসাবে এই ভাগ করা গোপনীয়তাটি ব্যবহার করব৷
const crypto = require('crypto');
const salt = crypto.randomBytes(16);
// Node has ECDH built-in to the standard crypto library. For some languages
// you may need to use a third-party library.
const serverECDH = crypto.createECDH('prime256v1');
const serverPublicKey = serverECDH.generateKeys();
const sharedSecret = serverECDH.computeSecret(clientPublicKey);
HKDF
ইতিমধ্যে অন্য একটি সরাইয়া জন্য সময়. ধরা যাক যে আপনার কাছে কিছু গোপন ডেটা আছে যা আপনি একটি এনক্রিপশন কী হিসাবে ব্যবহার করতে চান, কিন্তু এটি ক্রিপ্টোগ্রাফিকভাবে যথেষ্ট সুরক্ষিত নয়৷ কম নিরাপত্তা সহ একটি গোপন বিষয়কে উচ্চ নিরাপত্তায় পরিণত করতে আপনি HMAC-ভিত্তিক কী ডেরিভেশন ফাংশন (HKDF) ব্যবহার করতে পারেন।
এটি যেভাবে কাজ করে তার একটি ফলাফল হল যে এটি আপনাকে যেকোন সংখ্যক বিটের গোপনীয়তা নিতে দেয় এবং আপনি যে হ্যাশিং অ্যালগরিদম ব্যবহার করেন তার দ্বারা উত্পাদিত হ্যাশের 255 গুণ পর্যন্ত যে কোনো আকারের আরেকটি গোপনীয়তা তৈরি করতে দেয়। পুশের জন্য, স্পেক এর জন্য আমাদের SHA-256 ব্যবহার করতে হবে, যার হ্যাশ দৈর্ঘ্য 32 বাইট (256 বিট)।
যেহেতু এটি ঘটে, আমরা জানি যে আমাদের কেবলমাত্র 32 বাইট পর্যন্ত আকারের কী তৈরি করতে হবে। এর মানে হল যে আমরা অ্যালগরিদমের একটি সরলীকৃত সংস্করণ ব্যবহার করতে পারি যা বড় আউটপুট আকারগুলি পরিচালনা করতে পারে না।
আমি নীচে একটি নোড সংস্করণের জন্য কোডটি অন্তর্ভুক্ত করেছি, তবে আপনি এটি RFC 5869 এ আসলে কীভাবে কাজ করে তা খুঁজে পেতে পারেন।
HKDF-এর ইনপুটগুলি হল একটি লবণ, কিছু প্রাথমিক কী করার উপাদান (ikm), বর্তমান ব্যবহারের ক্ষেত্রে (তথ্য) নির্দিষ্ট কাঠামোগত ডেটার একটি ঐচ্ছিক অংশ এবং পছন্দসই আউটপুট কীটির বাইটে দৈর্ঘ্য।
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
if (length > 32) {
throw new Error('Cannot return keys of more than 32 bytes, ${length} requested');
}
// 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);
}
এনক্রিপশন পরামিতি প্রাপ্ত
প্রকৃত এনক্রিপশনের জন্য আমাদের কাছে থাকা ডেটাকে প্যারামিটারে পরিণত করতে আমরা এখন HKDF ব্যবহার করি।
আমরা প্রথম যে কাজটি করি তা হল HKDF ব্যবহার করে ক্লায়েন্ট অথ সিক্রেট এবং শেয়ার করা সিক্রেটকে মিশ্রিত করে একটি দীর্ঘ, আরও ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত গোপনীয়তায়। বিশেষত্বে এটিকে সিউডো-র্যান্ডম কী (PRK) হিসাবে উল্লেখ করা হয়েছে তাই আমি এটিকে এখানে বলব, যদিও ক্রিপ্টোগ্রাফি বিশুদ্ধতাবাদীরা মনে রাখতে পারেন যে এটি কঠোরভাবে একটি PRK নয়।
এখন আমরা চূড়ান্ত বিষয়বস্তু এনক্রিপশন কী এবং একটি নন্স তৈরি করি যা সাইফারে পাঠানো হবে। এগুলি প্রতিটির জন্য একটি সাধারণ ডেটা কাঠামো তৈরি করে তৈরি করা হয়, যাকে একটি তথ্য হিসাবে উল্লেখ করা হয় , যাতে বার্তাটির উত্স আরও যাচাই করার জন্য তথ্যের উপবৃত্তাকার বক্ররেখা, প্রেরক এবং প্রাপকের জন্য নির্দিষ্ট তথ্য থাকে। তারপরে আমরা PRK, আমাদের লবণ এবং তথ্যের সাথে HKDF ব্যবহার করি সঠিক আকারের কী এবং ননস বের করতে।
বিষয়বস্তু এনক্রিপশনের জন্য তথ্যের ধরন হল 'aesgcm' যা পুশ এনক্রিপশনের জন্য ব্যবহৃত সাইফারের নাম।
const authInfo = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(clientAuthSecret, sharedSecret, authInfo, 32);
function createInfo(type, clientPublicKey, serverPublicKey) {
const len = type.length;
// The start index for each element within the buffer is:
// value | length | start |
// -----------------------------------------
// 'Content-Encoding: '| 18 | 0 |
// type | len | 18 |
// nul byte | 1 | 18 + len |
// 'P-256' | 5 | 19 + len |
// nul byte | 1 | 24 + len |
// client key length | 2 | 25 + len |
// client key | 65 | 27 + len |
// server key length | 2 | 92 + len |
// server key | 65 | 94 + len |
// For the purposes of push encryption the length of the keys will
// always be 65 bytes.
const info = new Buffer(18 + len + 1 + 5 + 1 + 2 + 65 + 2 + 65);
// The string 'Content-Encoding: ', as utf-8
info.write('Content-Encoding: ');
// The 'type' of the record, a utf-8 string
info.write(type, 18);
// A single null-byte
info.write('\0', 18 + len);
// The string 'P-256', declaring the elliptic curve being used
info.write('P-256', 19 + len);
// A single null-byte
info.write('\0', 24 + len);
// The length of the client's public key as a 16-bit integer
info.writeUInt16BE(clientPublicKey.length, 25 + len);
// Now the actual client public key
clientPublicKey.copy(info, 27 + len);
// Length of our public key
info.writeUInt16BE(serverPublicKey.length, 92 + len);
// The key itself
serverPublicKey.copy(info, 94 + len);
return info;
}
// Derive the Content Encryption Key
const contentEncryptionKeyInfo = createInfo('aesgcm', clientPublicKey, serverPublicKey);
const contentEncryptionKey = hkdf(salt, prk, contentEncryptionKeyInfo, 16);
// Derive the Nonce
const nonceInfo = createInfo('nonce', clientPublicKey, serverPublicKey);
const nonce = hkdf(salt, prk, nonceInfo, 12);
প্যাডিং
আরেকটি বাদ দিয়ে, এবং একটি নির্বোধ এবং কল্পিত উদাহরণের জন্য সময়। ধরা যাক যে আপনার বসের একটি সার্ভার রয়েছে যা তাকে প্রতি কয়েক মিনিটে কোম্পানির স্টক মূল্যের সাথে একটি পুশ বার্তা পাঠায়। এর জন্য সরল বার্তাটি সর্বদা সেন্টের মান সহ একটি 32-বিট পূর্ণসংখ্যা হবে। ক্যাটারিং স্টাফদের সাথেও তার একটি গোপন চুক্তি রয়েছে যার অর্থ হল যে তারা আসলে ডেলিভারির 5 মিনিট আগে তাকে স্ট্রিং "ব্রেক রুমে ডোনাটস" পাঠাতে পারে যাতে তারা পৌঁছানোর সময় "কাকতালীয়ভাবে" সেখানে উপস্থিত হতে পারে এবং সেরাটি ধরতে পারে৷
ওয়েব পুশ দ্বারা ব্যবহৃত সাইফারটি এনক্রিপ্ট করা মান তৈরি করে যা এনক্রিপ্ট করা ইনপুটের চেয়ে ঠিক 16 বাইট দীর্ঘ। যেহেতু "ডোনাটস ইন দ্য ব্রেক রুমে" 32-বিট স্টক মূল্যের চেয়ে দীর্ঘ, যেকোন স্নুপিং কর্মচারী বার্তাগুলিকে ডিক্রিপ্ট না করেই ডোনাটগুলি কখন আসছে তা কেবল ডেটার দৈর্ঘ্য থেকে বলতে সক্ষম হবে৷
এই কারণে, ওয়েব পুশ প্রোটোকল আপনাকে ডেটার শুরুতে প্যাডিং যুক্ত করতে দেয়। আপনি কীভাবে এটি ব্যবহার করবেন তা আপনার অ্যাপ্লিকেশনের উপর নির্ভর করে, কিন্তু উপরের উদাহরণে আপনি সমস্ত বার্তাগুলিকে ঠিক 32 বাইট হতে প্যাড করতে পারেন, যা শুধুমাত্র দৈর্ঘ্যের উপর ভিত্তি করে বার্তাগুলিকে আলাদা করা অসম্ভব করে তোলে৷
প্যাডিং মান হল একটি 16-বিট বিগ-এন্ডিয়ান পূর্ণসংখ্যা যা প্যাডিংয়ের দৈর্ঘ্যের পরে প্যাডিংয়ের NUL
বাইটের সংখ্যা উল্লেখ করে। তাই ন্যূনতম প্যাডিং হল দুটি বাইট - শূন্য সংখ্যাটি 16 বিটে এনকোড করা হয়েছে।
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeroes, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
যখন আপনার পুশ বার্তাটি ক্লায়েন্টের কাছে আসে, তখন ব্রাউজারটি স্বয়ংক্রিয়ভাবে যেকোনো প্যাডিং বের করে দিতে সক্ষম হবে, তাই আপনার ক্লায়েন্ট কোডটি কেবলমাত্র প্যাড না করা বার্তাটি পাবে।
এনক্রিপশন
এখন আমরা অবশেষে এনক্রিপশন করতে সব জিনিস আছে. ওয়েব পুশের জন্য প্রয়োজনীয় সাইফার হল GCM ব্যবহার করে AES128 । আমরা আমাদের বিষয়বস্তু এনক্রিপশন কী ব্যবহার করি কী হিসেবে এবং ননসটিকে ইনিশিয়ালাইজেশন ভেক্টর (IV) হিসেবে ব্যবহার করি।
এই উদাহরণে আমাদের ডেটা একটি স্ট্রিং, তবে এটি যেকোনো বাইনারি ডেটা হতে পারে। আপনি এনক্রিপশন তথ্যের জন্য 16-বাইট এবং প্যাডিংয়ের জন্য কমপক্ষে 2 বাইট সহ, 4078 বাইট - 4096 বাইট সর্বোচ্চ পোস্ট প্রতি পেলোড পাঠাতে পারেন।
// Create a buffer from our data, in this case a UTF-8 encoded string
const plaintext = new Buffer('Push notification payload!', 'utf8');
const cipher = crypto.createCipheriv('id-aes128-GCM', contentEncryptionKey,
nonce);
const result = cipher.update(Buffer.concat(padding, plaintext));
cipher.final();
// Append the auth tag to the result - https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
return Buffer.concat([result, cipher.getAuthTag()]);
ওয়েব পুশ
ফাউ! এখন আপনার কাছে একটি এনক্রিপ্ট করা পেলোড আছে, আপনাকে ব্যবহারকারীর সাবস্ক্রিপশন দ্বারা নির্দিষ্ট শেষ পয়েন্টে অপেক্ষাকৃত সহজ HTTP POST অনুরোধ করতে হবে।
আপনাকে তিনটি হেডার সেট করতে হবে।
Encryption: salt=<SALT>
Crypto-Key: dh=<PUBLICKEY>
Content-Encoding: aesgcm
<SALT>
এবং <PUBLICKEY>
হল এনক্রিপশনে ব্যবহৃত সল্ট এবং সার্ভার পাবলিক কী, URL-নিরাপদ বেস64 হিসাবে এনকোড করা।
ওয়েব পুশ প্রোটোকল ব্যবহার করার সময়, POST এর মূল অংশটি তখন এনক্রিপ্ট করা বার্তার কাঁচা বাইট। যাইহোক, যতক্ষণ না Chrome এবং Firebase ক্লাউড মেসেজিং প্রোটোকল সমর্থন করে, আপনি সহজেই আপনার বিদ্যমান JSON পেলোডে ডেটা অন্তর্ভুক্ত করতে পারেন।
{
"registration_ids": [ "…" ],
"raw_data": "BIXzEKOFquzVlr/1tS1bhmobZ…"
}
rawData
সম্পত্তির মান অবশ্যই এনক্রিপ্ট করা বার্তার base64 এনকোডেড উপস্থাপনা হতে হবে।
ডিবাগিং / যাচাইকারী
পিটার বেভারলু, ক্রোম ইঞ্জিনিয়ারদের মধ্যে একজন যারা বৈশিষ্ট্যটি বাস্তবায়ন করেছেন (সেইসাথে সেই ব্যক্তিদের মধ্যে একজন যারা বিশেষত্বে কাজ করেছেন), একটি যাচাইকারী তৈরি করেছেন ।
এনক্রিপশনের প্রতিটি মধ্যবর্তী মানের আউটপুট করার জন্য আপনার কোড পেয়ে আপনি সেগুলি যাচাইকারীতে পেস্ট করতে পারেন এবং আপনি সঠিক পথে আছেন কিনা তা পরীক্ষা করতে পারেন।
,Chrome 50-এর আগে, পুশ বার্তাগুলিতে কোনও পেলোড ডেটা থাকতে পারে না। যখন 'পুশ' ইভেন্টটি আপনার পরিষেবা কর্মীকে বরখাস্ত করা হয়েছিল, আপনি কেবলমাত্র জানতেন যে সার্ভার আপনাকে কিছু বলার চেষ্টা করছে, কিন্তু এটি কী হতে পারে তা নয়। তারপরে আপনাকে সার্ভারে একটি ফলো-আপ অনুরোধ করতে হয়েছিল এবং দেখানোর জন্য বিজ্ঞপ্তির বিশদটি পেতে হয়েছিল, যা খারাপ নেটওয়ার্ক পরিস্থিতিতে ব্যর্থ হতে পারে।
এখন Chrome 50 এ (এবং ডেস্কটপে ফায়ারফক্সের বর্তমান সংস্করণে) আপনি পুশ সহ কিছু নির্বিচারে ডেটা পাঠাতে পারেন যাতে ক্লায়েন্ট অতিরিক্ত অনুরোধ করা এড়াতে পারে। যাইহোক, মহান শক্তির সাথে মহান দায়িত্ব আসে, তাই সমস্ত পেলোড ডেটা এনক্রিপ্ট করা আবশ্যক।
পেলোডগুলির এনক্রিপশন ওয়েব পুশের জন্য নিরাপত্তা গল্পের একটি গুরুত্বপূর্ণ অংশ। ব্রাউজার এবং আপনার নিজের সার্ভারের মধ্যে যোগাযোগ করার সময় HTTPS আপনাকে নিরাপত্তা দেয়, কারণ আপনি সার্ভারকে বিশ্বাস করেন। যাইহোক, ব্রাউজার চয়ন করে যে কোন পুশ প্রদানকারীকে প্রকৃতপক্ষে পেলোড সরবরাহ করতে ব্যবহার করা হবে, তাই অ্যাপ বিকাশকারী হিসাবে আপনার এটির উপর কোন নিয়ন্ত্রণ নেই।
এখানে, HTTPS শুধুমাত্র গ্যারান্টি দিতে পারে যে পুশ পরিষেবা প্রদানকারীর কাছে ট্রানজিট করার সময় কেউ বার্তাটি স্নুপ করতে পারবে না। একবার তারা এটি পেয়ে গেলে, তারা তৃতীয় পক্ষের কাছে পেলোডটি পুনরায় প্রেরণ করা বা অন্য কিছুতে দূষিতভাবে পরিবর্তন করা সহ তারা যা পছন্দ করে তা করতে স্বাধীন। এর বিরুদ্ধে সুরক্ষার জন্য আমরা এনক্রিপশন ব্যবহার করি যাতে পুশ পরিষেবাগুলি ট্রানজিটে পেলোডগুলি পড়তে বা বিকৃত করতে না পারে।
ক্লায়েন্ট-সাইড পরিবর্তন
আপনি যদি ইতিমধ্যেই পেলোড ছাড়াই পুশ বিজ্ঞপ্তিগুলি প্রয়োগ করে থাকেন তবে ক্লায়েন্ট-সাইডে আপনাকে শুধুমাত্র দুটি ছোট পরিবর্তন করতে হবে।
এটি প্রথমে হল যে আপনি যখন আপনার ব্যাকএন্ড সার্ভারে সাবস্ক্রিপশন তথ্য পাঠান তখন আপনাকে কিছু অতিরিক্ত তথ্য সংগ্রহ করতে হবে। আপনি যদি ইতিমধ্যেই PushSubscription অবজেক্টে JSON.stringify()
ব্যবহার করেন আপনার সার্ভারে পাঠানোর জন্য এটিকে সিরিয়ালাইজ করতে তাহলে আপনাকে কিছু পরিবর্তন করতে হবে না। সাবস্ক্রিপশনে এখন কী বৈশিষ্ট্যে কিছু অতিরিক্ত ডেটা থাকবে।
> JSON.stringify(subscription)
{"endpoint":"https://android.googleapis.com/gcm/send/f1LsxkKphfQ:APA91bFUx7ja4BK4JVrNgVjpg1cs9lGSGI6IMNL4mQ3Xe6mDGxvt_C_gItKYJI9CAx5i_Ss6cmDxdWZoLyhS2RJhkcv7LeE6hkiOsK6oBzbyifvKCdUYU7ADIRBiYNxIVpLIYeZ8kq_A",
"keys":{"p256dh":"BLc4xRzKlKORKWlbdgFaBrrPK3ydWAHo4M0gs0i1oEKgPpWC5cW8OCzVrOQRv-1npXRWk8udnW3oYhIO4475rds=",
"auth":"5I2Bu2oKdyy9CwL8QVF0NQ=="}}
দুটি মান p256dh
এবং auth
বেস64-এর একটি ভেরিয়েন্টে এনকোড করা হয়েছে যেটিকে আমি URL-Safe Base64 বলব।
আপনি যদি পরিবর্তে বাইটগুলিতে ডান পেতে চান তবে আপনি সাবস্ক্রিপশনে নতুন getKey()
পদ্ধতি ব্যবহার করতে পারেন যা একটি ArrayBuffer
হিসাবে একটি প্যারামিটার প্রদান করে। আপনার যে দুটি প্যারামিটার প্রয়োজন তা হল auth
এবং p256dh
।
> new Uint8Array(subscription.getKey('auth'));
[228, 141, 129, ...] (16 bytes)
> new Uint8Array(subscription.getKey('p256dh'));
[4, 183, 56, ...] (65 bytes)
দ্বিতীয় পরিবর্তনটি হল একটি নতুন ডেটা বৈশিষ্ট্য যখন push
ইভেন্ট ফায়ার হয়। এটিতে প্রাপ্ত ডেটা পার্স করার জন্য বিভিন্ন সিঙ্ক্রোনাস পদ্ধতি রয়েছে, যেমন .text()
, .json()
, .arrayBuffer()
এবং .blob()
।
self.addEventListener('push', function(event) {
if (event.data) {
console.log(event.data.json());
}
});
সার্ভার-সাইড পরিবর্তন
সার্ভারের দিকে, জিনিসগুলি একটু বেশি পরিবর্তিত হয়। মূল প্রক্রিয়াটি হল আপনি পেলোড এনক্রিপ্ট করতে ক্লায়েন্টের কাছ থেকে পাওয়া এনক্রিপশন কী তথ্য ব্যবহার করেন এবং তারপরে কিছু অতিরিক্ত HTTP শিরোনাম যোগ করে সাবস্ক্রিপশনের শেষ পয়েন্টে একটি POST অনুরোধের বডি হিসেবে পাঠান।
বিশদগুলি তুলনামূলকভাবে জটিল, এবং এনক্রিপশন সম্পর্কিত যে কোনও কিছুর মতো আপনার নিজের রোল করার চেয়ে সক্রিয়ভাবে বিকাশ করা লাইব্রেরি ব্যবহার করা ভাল। Chrome টিম Node.js-এর জন্য একটি লাইব্রেরি প্রকাশ করেছে, আরও ভাষা এবং প্ল্যাটফর্ম শীঘ্রই আসছে৷ এটি এনক্রিপশন এবং ওয়েব পুশ প্রোটোকল উভয়ই পরিচালনা করে, যাতে একটি Node.js সার্ভার থেকে একটি পুশ বার্তা পাঠানো webpush.sendWebPush(message, subscription)
এর মতোই সহজ।
যদিও আমরা অবশ্যই একটি লাইব্রেরি ব্যবহার করার পরামর্শ দিই, এটি একটি নতুন বৈশিষ্ট্য এবং এমন অনেক জনপ্রিয় ভাষা রয়েছে যেগুলির এখনও কোনও লাইব্রেরি নেই৷ আপনি যদি নিজের জন্য এটি বাস্তবায়ন করতে চান তবে এখানে বিশদ রয়েছে।
আমি নোড-স্বাদযুক্ত জাভাস্ক্রিপ্ট ব্যবহার করে অ্যালগরিদমগুলি চিত্রিত করব, তবে মৌলিক নীতিগুলি যে কোনও ভাষায় একই হওয়া উচিত।
ইনপুট
একটি বার্তা এনক্রিপ্ট করার জন্য, আমাদের প্রথমে সাবস্ক্রিপশন অবজেক্ট থেকে দুটি জিনিস পেতে হবে যা আমরা ক্লায়েন্টের কাছ থেকে পেয়েছি। আপনি যদি ক্লায়েন্টে JSON.stringify()
ব্যবহার করেন এবং সেটি আপনার সার্ভারে প্রেরণ করেন তাহলে ক্লায়েন্টের সর্বজনীন কী keys.p256dh
ক্ষেত্রে সংরক্ষণ করা হয়, যখন ভাগ করা প্রমাণীকরণ গোপনীয়তা keys.auth
ক্ষেত্রে থাকে। এই দুটিই URL-নিরাপদ বেস64 এনকোড করা হবে, যেমন উপরে উল্লিখিত হয়েছে। ক্লায়েন্ট পাবলিক কী-এর বাইনারি বিন্যাস হল একটি অসংকুচিত P-256 উপবৃত্তাকার বক্ররেখা বিন্দু।
const clientPublicKey = new Buffer(subscription.keys.p256dh, 'base64');
const clientAuthSecret = new Buffer(subscription.keys.auth, 'base64');
পাবলিক কী আমাদের বার্তাটিকে এনক্রিপ্ট করার অনুমতি দেয় যাতে এটি শুধুমাত্র ক্লায়েন্টের ব্যক্তিগত কী ব্যবহার করে ডিক্রিপ্ট করা যায়।
সর্বজনীন কীগুলিকে সাধারণত, ভাল, সর্বজনীন হিসাবে বিবেচনা করা হয়, তাই ক্লায়েন্টকে প্রমাণীকরণ করার অনুমতি দেওয়ার জন্য যে বার্তাটি একটি বিশ্বস্ত সার্ভার দ্বারা পাঠানো হয়েছে আমরা প্রমাণীকরণ গোপনীয়তাও ব্যবহার করি। আশ্চর্যজনকভাবে, এটি গোপন রাখা উচিত, শুধুমাত্র সেই অ্যাপ্লিকেশন সার্ভারের সাথে ভাগ করা উচিত যা আপনি আপনাকে বার্তা পাঠাতে চান এবং একটি পাসওয়ার্ডের মতো আচরণ করা উচিত৷
আমাদের কিছু নতুন ডেটাও তৈরি করতে হবে। আমাদের একটি 16-বাইট ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত এলোমেলো লবণ এবং উপবৃত্তাকার কার্ভ কীগুলির একটি পাবলিক/প্রাইভেট জোড়া দরকার৷ পুশ এনক্রিপশন স্পেক দ্বারা ব্যবহৃত বিশেষ বক্ররেখাকে P-256 বা prime256v1
বলা হয়। সর্বোত্তম নিরাপত্তার জন্য আপনি যখনই একটি বার্তা এনক্রিপ্ট করবেন তখনই মূল জোড়াটি স্ক্র্যাচ থেকে তৈরি করা উচিত এবং আপনার কখনই লবণ পুনরায় ব্যবহার করা উচিত নয়।
ইসিডিএইচ
উপবৃত্তাকার বক্ররেখা ক্রিপ্টোগ্রাফির একটি ঝরঝরে সম্পত্তি সম্পর্কে কথা বলার জন্য আসুন একটু দূরে নিয়ে যাই। তুলনামূলকভাবে সহজ প্রক্রিয়া আছে যা আপনার প্রাইভেট কীকে অন্য কারো পাবলিক কী-এর সাথে একত্রিত করে একটি মান বের করে। তাই কি? ঠিক আছে, যদি অন্য পক্ষ তাদের ব্যক্তিগত কী এবং আপনার সর্বজনীন কী নেয় তবে এটি একই মান অর্জন করবে!
এটি উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান (ECDH) কী চুক্তি প্রোটোকলের ভিত্তি, যা উভয় পক্ষকে একই ভাগ করা গোপন রাখতে দেয় যদিও তারা শুধুমাত্র পাবলিক কী বিনিময় করে। আমরা আমাদের প্রকৃত এনক্রিপশন কীটির ভিত্তি হিসাবে এই ভাগ করা গোপনীয়তাটি ব্যবহার করব৷
const crypto = require('crypto');
const salt = crypto.randomBytes(16);
// Node has ECDH built-in to the standard crypto library. For some languages
// you may need to use a third-party library.
const serverECDH = crypto.createECDH('prime256v1');
const serverPublicKey = serverECDH.generateKeys();
const sharedSecret = serverECDH.computeSecret(clientPublicKey);
HKDF
ইতিমধ্যে অন্য একটি সরাইয়া জন্য সময়. ধরা যাক যে আপনার কাছে কিছু গোপন ডেটা আছে যা আপনি একটি এনক্রিপশন কী হিসাবে ব্যবহার করতে চান, কিন্তু এটি ক্রিপ্টোগ্রাফিকভাবে যথেষ্ট সুরক্ষিত নয়৷ কম নিরাপত্তা সহ একটি গোপন বিষয়কে উচ্চ নিরাপত্তায় পরিণত করতে আপনি HMAC-ভিত্তিক কী ডেরিভেশন ফাংশন (HKDF) ব্যবহার করতে পারেন।
এটি যেভাবে কাজ করে তার একটি ফলাফল হল যে এটি আপনাকে যেকোন সংখ্যক বিটের গোপনীয়তা নিতে দেয় এবং আপনি যে হ্যাশিং অ্যালগরিদম ব্যবহার করেন তার দ্বারা উত্পাদিত হ্যাশের 255 গুণ পর্যন্ত যে কোনো আকারের আরেকটি গোপনীয়তা তৈরি করতে দেয়। পুশের জন্য, স্পেক এর জন্য আমাদের SHA-256 ব্যবহার করতে হবে, যার হ্যাশ দৈর্ঘ্য 32 বাইট (256 বিট)।
যেহেতু এটি ঘটে, আমরা জানি যে আমাদের কেবলমাত্র 32 বাইট পর্যন্ত আকারের কী তৈরি করতে হবে। এর মানে হল যে আমরা অ্যালগরিদমের একটি সরলীকৃত সংস্করণ ব্যবহার করতে পারি যা বড় আউটপুট আকারগুলি পরিচালনা করতে পারে না।
আমি নীচে একটি নোড সংস্করণের জন্য কোডটি অন্তর্ভুক্ত করেছি, তবে আপনি এটি RFC 5869 এ আসলে কীভাবে কাজ করে তা খুঁজে পেতে পারেন।
HKDF-এর ইনপুটগুলি হল একটি লবণ, কিছু প্রাথমিক কী করার উপাদান (ikm), বর্তমান ব্যবহারের ক্ষেত্রে (তথ্য) নির্দিষ্ট কাঠামোগত ডেটার একটি ঐচ্ছিক অংশ এবং পছন্দসই আউটপুট কীটির বাইটে দৈর্ঘ্য।
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
if (length > 32) {
throw new Error('Cannot return keys of more than 32 bytes, ${length} requested');
}
// 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);
}
এনক্রিপশন পরামিতি প্রাপ্ত
প্রকৃত এনক্রিপশনের জন্য আমাদের কাছে থাকা ডেটাকে প্যারামিটারে পরিণত করতে আমরা এখন HKDF ব্যবহার করি।
আমরা প্রথম যে কাজটি করি তা হল HKDF ব্যবহার করে ক্লায়েন্ট অথ সিক্রেট এবং শেয়ার করা সিক্রেটকে মিশ্রিত করে একটি দীর্ঘ, আরও ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত গোপনীয়তায়। বিশেষত্বে এটিকে সিউডো-র্যান্ডম কী (PRK) হিসাবে উল্লেখ করা হয়েছে তাই আমি এটিকে এখানে বলব, যদিও ক্রিপ্টোগ্রাফি বিশুদ্ধতাবাদীরা মনে রাখতে পারেন যে এটি কঠোরভাবে একটি PRK নয়।
এখন আমরা চূড়ান্ত বিষয়বস্তু এনক্রিপশন কী এবং একটি নন্স তৈরি করি যা সাইফারে পাঠানো হবে। এগুলি প্রতিটির জন্য একটি সাধারণ ডেটা কাঠামো তৈরি করে তৈরি করা হয়, যাকে একটি তথ্য হিসাবে উল্লেখ করা হয় , যাতে বার্তাটির উত্স আরও যাচাই করার জন্য তথ্যের উপবৃত্তাকার বক্ররেখা, প্রেরক এবং প্রাপকের জন্য নির্দিষ্ট তথ্য থাকে। তারপরে আমরা PRK, আমাদের লবণ এবং তথ্যের সাথে HKDF ব্যবহার করি সঠিক আকারের কী এবং ননস বের করতে।
বিষয়বস্তু এনক্রিপশনের জন্য তথ্যের ধরন হল 'aesgcm' যা পুশ এনক্রিপশনের জন্য ব্যবহৃত সাইফারের নাম।
const authInfo = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(clientAuthSecret, sharedSecret, authInfo, 32);
function createInfo(type, clientPublicKey, serverPublicKey) {
const len = type.length;
// The start index for each element within the buffer is:
// value | length | start |
// -----------------------------------------
// 'Content-Encoding: '| 18 | 0 |
// type | len | 18 |
// nul byte | 1 | 18 + len |
// 'P-256' | 5 | 19 + len |
// nul byte | 1 | 24 + len |
// client key length | 2 | 25 + len |
// client key | 65 | 27 + len |
// server key length | 2 | 92 + len |
// server key | 65 | 94 + len |
// For the purposes of push encryption the length of the keys will
// always be 65 bytes.
const info = new Buffer(18 + len + 1 + 5 + 1 + 2 + 65 + 2 + 65);
// The string 'Content-Encoding: ', as utf-8
info.write('Content-Encoding: ');
// The 'type' of the record, a utf-8 string
info.write(type, 18);
// A single null-byte
info.write('\0', 18 + len);
// The string 'P-256', declaring the elliptic curve being used
info.write('P-256', 19 + len);
// A single null-byte
info.write('\0', 24 + len);
// The length of the client's public key as a 16-bit integer
info.writeUInt16BE(clientPublicKey.length, 25 + len);
// Now the actual client public key
clientPublicKey.copy(info, 27 + len);
// Length of our public key
info.writeUInt16BE(serverPublicKey.length, 92 + len);
// The key itself
serverPublicKey.copy(info, 94 + len);
return info;
}
// Derive the Content Encryption Key
const contentEncryptionKeyInfo = createInfo('aesgcm', clientPublicKey, serverPublicKey);
const contentEncryptionKey = hkdf(salt, prk, contentEncryptionKeyInfo, 16);
// Derive the Nonce
const nonceInfo = createInfo('nonce', clientPublicKey, serverPublicKey);
const nonce = hkdf(salt, prk, nonceInfo, 12);
প্যাডিং
আরেকটি বাদ দিয়ে, এবং একটি নির্বোধ এবং কল্পিত উদাহরণের জন্য সময়। ধরা যাক যে আপনার বসের একটি সার্ভার রয়েছে যা তাকে প্রতি কয়েক মিনিটে কোম্পানির স্টক মূল্যের সাথে একটি পুশ বার্তা পাঠায়। এর জন্য সরল বার্তাটি সর্বদা সেন্টের মান সহ একটি 32-বিট পূর্ণসংখ্যা হবে। ক্যাটারিং স্টাফদের সাথেও তার একটি গোপন চুক্তি রয়েছে যার অর্থ হল যে তারা আসলে ডেলিভারির 5 মিনিট আগে তাকে স্ট্রিং "ব্রেক রুমে ডোনাটস" পাঠাতে পারে যাতে তারা পৌঁছানোর সময় "কাকতালীয়ভাবে" সেখানে উপস্থিত হতে পারে এবং সেরাটি ধরতে পারে৷
ওয়েব পুশ দ্বারা ব্যবহৃত সাইফারটি এনক্রিপ্ট করা মান তৈরি করে যা এনক্রিপ্ট করা ইনপুটের চেয়ে ঠিক 16 বাইট দীর্ঘ। যেহেতু "ডোনাটস ইন দ্য ব্রেক রুমে" 32-বিট স্টক মূল্যের চেয়ে দীর্ঘ, যেকোন স্নুপিং কর্মচারী বার্তাগুলিকে ডিক্রিপ্ট না করেই ডোনাটগুলি কখন আসছে তা কেবল ডেটার দৈর্ঘ্য থেকে বলতে সক্ষম হবে৷
এই কারণে, ওয়েব পুশ প্রোটোকল আপনাকে ডেটার শুরুতে প্যাডিং যুক্ত করতে দেয়। আপনি কীভাবে এটি ব্যবহার করবেন তা আপনার অ্যাপ্লিকেশনের উপর নির্ভর করে, কিন্তু উপরের উদাহরণে আপনি সমস্ত বার্তাগুলিকে ঠিক 32 বাইট হতে প্যাড করতে পারেন, যা শুধুমাত্র দৈর্ঘ্যের উপর ভিত্তি করে বার্তাগুলিকে আলাদা করা অসম্ভব করে তোলে৷
প্যাডিং মান হল একটি 16-বিট বিগ-এন্ডিয়ান পূর্ণসংখ্যা যা প্যাডিংয়ের দৈর্ঘ্যের পরে প্যাডিংয়ের NUL
বাইটের সংখ্যা উল্লেখ করে। তাই ন্যূনতম প্যাডিং হল দুটি বাইট - শূন্য সংখ্যাটি 16 বিটে এনকোড করা হয়েছে।
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeroes, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
যখন আপনার পুশ বার্তাটি ক্লায়েন্টের কাছে আসে, তখন ব্রাউজারটি স্বয়ংক্রিয়ভাবে যেকোনো প্যাডিং বের করে দিতে সক্ষম হবে, তাই আপনার ক্লায়েন্ট কোডটি কেবলমাত্র প্যাড না করা বার্তাটি পাবে।
এনক্রিপশন
এখন আমরা অবশেষে এনক্রিপশন করতে সব জিনিস আছে. ওয়েব পুশের জন্য প্রয়োজনীয় সাইফার হল GCM ব্যবহার করে AES128 । আমরা আমাদের বিষয়বস্তু এনক্রিপশন কী ব্যবহার করি কী হিসেবে এবং ননসটিকে ইনিশিয়ালাইজেশন ভেক্টর (IV) হিসেবে ব্যবহার করি।
এই উদাহরণে আমাদের ডেটা একটি স্ট্রিং, তবে এটি যেকোনো বাইনারি ডেটা হতে পারে। আপনি এনক্রিপশন তথ্যের জন্য 16-বাইট এবং প্যাডিংয়ের জন্য কমপক্ষে 2 বাইট সহ, 4078 বাইট - 4096 বাইট সর্বোচ্চ পোস্ট প্রতি পেলোড পাঠাতে পারেন।
// Create a buffer from our data, in this case a UTF-8 encoded string
const plaintext = new Buffer('Push notification payload!', 'utf8');
const cipher = crypto.createCipheriv('id-aes128-GCM', contentEncryptionKey,
nonce);
const result = cipher.update(Buffer.concat(padding, plaintext));
cipher.final();
// Append the auth tag to the result - https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
return Buffer.concat([result, cipher.getAuthTag()]);
ওয়েব পুশ
ফাউ! এখন আপনার কাছে একটি এনক্রিপ্ট করা পেলোড আছে, আপনাকে ব্যবহারকারীর সাবস্ক্রিপশন দ্বারা নির্দিষ্ট শেষ পয়েন্টে অপেক্ষাকৃত সহজ HTTP POST অনুরোধ করতে হবে।
আপনাকে তিনটি হেডার সেট করতে হবে।
Encryption: salt=<SALT>
Crypto-Key: dh=<PUBLICKEY>
Content-Encoding: aesgcm
<SALT>
এবং <PUBLICKEY>
হল এনক্রিপশনে ব্যবহৃত সল্ট এবং সার্ভার পাবলিক কী, URL-নিরাপদ বেস64 হিসাবে এনকোড করা।
ওয়েব পুশ প্রোটোকল ব্যবহার করার সময়, POST এর মূল অংশটি তখন এনক্রিপ্ট করা বার্তার কাঁচা বাইট। যাইহোক, যতক্ষণ না Chrome এবং Firebase ক্লাউড মেসেজিং প্রোটোকল সমর্থন করে, আপনি সহজেই আপনার বিদ্যমান JSON পেলোডে ডেটা অন্তর্ভুক্ত করতে পারেন।
{
"registration_ids": [ "…" ],
"raw_data": "BIXzEKOFquzVlr/1tS1bhmobZ…"
}
rawData
সম্পত্তির মান অবশ্যই এনক্রিপ্ট করা বার্তার base64 এনকোডেড উপস্থাপনা হতে হবে।
ডিবাগিং / যাচাইকারী
পিটার বেভারলু, অন্যতম ক্রোম ইঞ্জিনিয়ার যারা বৈশিষ্ট্যটি বাস্তবায়ন করেছিলেন (পাশাপাশি স্পেসে কাজ করা লোকদের মধ্যে একজন) একটি যাচাইকারী তৈরি করেছেন ।
আপনার কোডটি এনক্রিপশনের প্রতিটি মধ্যবর্তী মানকে আউটপুট করার জন্য আপনি সেগুলি যাচাই করতে পারেন এবং আপনি সঠিক পথে আছেন কিনা তা পরীক্ষা করে দেখতে পারেন।
,ক্রোম 50 এর আগে, পুশ বার্তাগুলিতে কোনও পেডলোড ডেটা থাকতে পারে না। যখন আপনার পরিষেবা কর্মীর মধ্যে 'পুশ' ইভেন্টটি বরখাস্ত করা হয়েছিল, তখন আপনি কেবল জানতেন যে সার্ভার আপনাকে কিছু বলার চেষ্টা করছে, তবে এটি কী হতে পারে তা নয়। তারপরে আপনাকে সার্ভারে একটি ফলোআপ অনুরোধ করতে হয়েছিল এবং দেখানোর জন্য বিজ্ঞপ্তির বিশদটি পেতে হয়েছিল, যা দুর্বল নেটওয়ার্কের পরিস্থিতিতে ব্যর্থ হতে পারে।
এখন ক্রোম 50 এ (এবং ডেস্কটপে ফায়ারফক্সের বর্তমান সংস্করণে) আপনি ধাক্কা সহ কিছু স্বেচ্ছাচারিত ডেটা প্রেরণ করতে পারেন যাতে ক্লায়েন্ট অতিরিক্ত অনুরোধ করা এড়াতে পারে। যাইহোক, দুর্দান্ত শক্তির সাথে দুর্দান্ত দায়িত্ব আসে, তাই সমস্ত পে -লোড ডেটা অবশ্যই এনক্রিপ্ট করা উচিত।
পে -লোডগুলির এনক্রিপশন ওয়েব পুশের জন্য সুরক্ষা গল্পের একটি গুরুত্বপূর্ণ অঙ্গ। ব্রাউজার এবং আপনার নিজস্ব সার্ভারের মধ্যে যোগাযোগ করার সময় এইচটিটিপিএস আপনাকে সুরক্ষা দেয়, কারণ আপনি সার্ভারে বিশ্বাস করেন। যাইহোক, ব্রাউজারটি চয়ন করে যে কোন পুশ সরবরাহকারীকে আসলে পে -লোড সরবরাহ করতে ব্যবহৃত হবে, সুতরাং অ্যাপ্লিকেশন বিকাশকারী হিসাবে আপনার এটির কোনও নিয়ন্ত্রণ নেই।
এখানে, এইচটিটিপিএস কেবলমাত্র গ্যারান্টি দিতে পারে যে কেউ পুশ পরিষেবা সরবরাহকারীর কাছে ট্রানজিটের বার্তায় স্নুপ করতে পারে না। একবার তারা এটি গ্রহণ করার পরে, তারা তৃতীয় পক্ষের কাছে পে-লোড পুনরায় সংস্কার করা বা দূষিতভাবে এটিকে অন্য কোনও কিছুতে পরিবর্তন করা সহ তারা যা পছন্দ করে তা করতে নির্দ্বিধায়। এর থেকে রক্ষা করার জন্য আমরা ট্রানজিটে পে -লোডগুলির সাথে পুশ পরিষেবাগুলি পড়তে বা টেম্পার করতে পারবেন না তা নিশ্চিত করতে আমরা এনক্রিপশন ব্যবহার করি।
ক্লায়েন্ট-সাইড পরিবর্তন
আপনি যদি ইতিমধ্যে পে-লোড ছাড়াই পুশ বিজ্ঞপ্তিগুলি প্রয়োগ করে থাকেন তবে ক্লায়েন্ট-সাইডে আপনাকে কেবল দুটি ছোট পরিবর্তন করতে হবে।
এটি প্রথমটি হ'ল আপনি যখন আপনার ব্যাকএন্ড সার্ভারে সাবস্ক্রিপশন তথ্য প্রেরণ করেন তখন আপনাকে কিছু অতিরিক্ত তথ্য সংগ্রহ করতে হবে। আপনি যদি ইতিমধ্যে আপনার সার্ভারে প্রেরণের জন্য এটি সিরিয়ালাইজ করার জন্য পুশসুবস্ক্রিপশন অবজেক্টে JSON.stringify()
ব্যবহার করেন তবে আপনাকে কিছু পরিবর্তন করার দরকার নেই। সাবস্ক্রিপশনে এখন কী সম্পত্তিতে কিছু অতিরিক্ত ডেটা থাকবে।
> JSON.stringify(subscription)
{"endpoint":"https://android.googleapis.com/gcm/send/f1LsxkKphfQ:APA91bFUx7ja4BK4JVrNgVjpg1cs9lGSGI6IMNL4mQ3Xe6mDGxvt_C_gItKYJI9CAx5i_Ss6cmDxdWZoLyhS2RJhkcv7LeE6hkiOsK6oBzbyifvKCdUYU7ADIRBiYNxIVpLIYeZ8kq_A",
"keys":{"p256dh":"BLc4xRzKlKORKWlbdgFaBrrPK3ydWAHo4M0gs0i1oEKgPpWC5cW8OCzVrOQRv-1npXRWk8udnW3oYhIO4475rds=",
"auth":"5I2Bu2oKdyy9CwL8QVF0NQ=="}}
দুটি মান p256dh
এবং auth
BASE64 এর একটি বৈকল্পিকভাবে এনকোড করা হয়েছে যা আমি URL-SAFE BASE64 কল করব।
পরিবর্তে আপনি যদি বাইটে ডান পেতে চান তবে আপনি সাবস্ক্রিপশনে নতুন getKey()
পদ্ধতিটি ব্যবহার করতে পারেন যা ArrayBuffer
হিসাবে প্যারামিটারটি ফেরত দেয়। আপনার যে দুটি প্যারামিটার প্রয়োজন তা হ'ল auth
এবং p256dh
।
> new Uint8Array(subscription.getKey('auth'));
[228, 141, 129, ...] (16 bytes)
> new Uint8Array(subscription.getKey('p256dh'));
[4, 183, 56, ...] (65 bytes)
দ্বিতীয় পরিবর্তনটি যখন push
ইভেন্টটি আগুন লাগে তখন একটি নতুন ডেটা সম্পত্তি। এতে প্রাপ্ত ডেটা পার্স করার জন্য এটিতে বিভিন্ন সিঙ্ক্রোনাস পদ্ধতি রয়েছে যেমন .text()
, .json()
, .arrayBuffer()
এবং .blob()
।
self.addEventListener('push', function(event) {
if (event.data) {
console.log(event.data.json());
}
});
সার্ভার-সাইড পরিবর্তন
সার্ভারের পাশে, জিনিসগুলি আরও কিছুটা পরিবর্তন করে। মূল প্রক্রিয়াটি হ'ল আপনি ক্লায়েন্টের কাছ থেকে প্রাপ্ত এনক্রিপশন কী তথ্য ব্যবহার করেন পে -লোড এনক্রিপ্ট করতে এবং তারপরে সাবস্ক্রিপশনে শেষ পয়েন্টে একটি পোস্ট অনুরোধের বডি হিসাবে প্রেরণ করুন, কিছু অতিরিক্ত এইচটিটিপি শিরোনাম যুক্ত করে।
বিশদগুলি তুলনামূলকভাবে জটিল, এবং এনক্রিপশন সম্পর্কিত যে কোনও কিছুর সাথে আপনার নিজের রোল করার চেয়ে সক্রিয়ভাবে বিকাশিত গ্রন্থাগারটি ব্যবহার করা ভাল। ক্রোম টিম নোড.জেএসের জন্য একটি গ্রন্থাগার প্রকাশ করেছে, আরও শীঘ্রই আরও ভাষা এবং প্ল্যাটফর্মগুলি আসবে। এটি এনক্রিপশন এবং ওয়েব পুশ প্রোটোকল উভয়ই পরিচালনা করে, যাতে একটি নোড.জেএস সার্ভার থেকে একটি পুশ বার্তা প্রেরণ করা webpush.sendWebPush(message, subscription)
এর মতোই সহজ।
যদিও আমরা অবশ্যই একটি লাইব্রেরি ব্যবহার করার পরামর্শ দিচ্ছি, এটি একটি নতুন বৈশিষ্ট্য এবং এমন অনেক জনপ্রিয় ভাষা রয়েছে যা এখনও কোনও লাইব্রেরি নেই। আপনার যদি নিজের জন্য এটি বাস্তবায়নের প্রয়োজন হয় তবে এখানে বিশদটি এখানে।
আমি নোড-স্বাদযুক্ত জাভাস্ক্রিপ্ট ব্যবহার করে অ্যালগরিদমগুলি চিত্রিত করব, তবে প্রাথমিক নীতিগুলি যে কোনও ভাষায় একই হওয়া উচিত।
ইনপুট
কোনও বার্তা এনক্রিপ্ট করার জন্য, আমাদের প্রথমে ক্লায়েন্টের কাছ থেকে প্রাপ্ত সাবস্ক্রিপশন অবজেক্ট থেকে দুটি জিনিস পেতে হবে। আপনি যদি ক্লায়েন্টের উপর JSON.stringify()
ব্যবহার করেন এবং এটি আপনার সার্ভারে প্রেরণ করেন তবে ক্লায়েন্টের পাবলিক কীটি keys.p256dh
ক্ষেত্রে সংরক্ষণ করা হয়, যখন ভাগ করা প্রমাণীকরণের গোপনীয়তা keys.auth
ক্ষেত্রে রয়েছে। এই দুটিই ইউআরএল-নিরাপদ বেস 64 এনকোডেড হবে, যেমন উপরে উল্লিখিত হয়েছে। ক্লায়েন্ট পাবলিক কী এর বাইনারি ফর্ম্যাটটি একটি সংকুচিত পি -256 উপবৃত্তাকার বক্ররেখা পয়েন্ট।
const clientPublicKey = new Buffer(subscription.keys.p256dh, 'base64');
const clientAuthSecret = new Buffer(subscription.keys.auth, 'base64');
পাবলিক কী আমাদের বার্তাটি এনক্রিপ্ট করার অনুমতি দেয় যে এটি কেবল ক্লায়েন্টের ব্যক্তিগত কী ব্যবহার করে ডিক্রিপ্ট করা যেতে পারে।
পাবলিক কীগুলি সাধারণত, ভাল, পাবলিক হিসাবে বিবেচিত হয়, তাই ক্লায়েন্টকে প্রমাণ করার জন্য যে বার্তাটি একটি বিশ্বস্ত সার্ভার দ্বারা প্রেরণ করা হয়েছিল তা আমরা প্রমাণীকরণের গোপনীয়তাও ব্যবহার করি। আশ্চর্যজনকভাবে, এটি গোপন রাখা উচিত, কেবলমাত্র অ্যাপ্লিকেশন সার্ভারের সাথে ভাগ করে নেওয়া উচিত যা আপনি আপনাকে বার্তা প্রেরণ করতে চান এবং পাসওয়ার্ডের মতো চিকিত্সা করা উচিত।
আমাদের কিছু নতুন ডেটাও তৈরি করতে হবে। আমাদের একটি 16-বাইট ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত এলোমেলো লবণ এবং উপবৃত্তাকার বক্ররেখার কীগুলির একটি সরকারী/ব্যক্তিগত জুড়ি দরকার। পুশ এনক্রিপশন স্পেক দ্বারা ব্যবহৃত বিশেষ বক্ররেখাকে পি -256, বা prime256v1
বলা হয়। সেরা সুরক্ষার জন্য প্রতিবার আপনি যখন কোনও বার্তা এনক্রিপ্ট করেন তখন কী জুটি স্ক্র্যাচ থেকে তৈরি করা উচিত এবং আপনার কখনই লবণ পুনরায় ব্যবহার করা উচিত নয়।
ইসিডিএইচ
উপবৃত্তাকার কার্ভ ক্রিপ্টোগ্রাফির একটি ঝরঝরে সম্পত্তি সম্পর্কে কথা বলার জন্য কিছুটা দূরে নিয়ে যাওয়া যাক। তুলনামূলকভাবে সহজ প্রক্রিয়া রয়েছে যা কোনও মান অর্জনের জন্য আপনার ব্যক্তিগত কীটিকে অন্য কারও পাবলিক কী এর সাথে একত্রিত করে। তাই কি? ঠিক আছে, যদি অন্য পক্ষটি তাদের ব্যক্তিগত কী এবং আপনার পাবলিক কী গ্রহণ করে তবে এটি ঠিক একই মানটি অর্জন করবে!
এটি উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান (ইসিডিএইচ) মূল চুক্তি প্রোটোকলের ভিত্তি, যা উভয় পক্ষকে কেবল পাবলিক কীগুলি বিনিময় করলেও একই ভাগ করা গোপনীয়তা থাকতে দেয়। আমরা আমাদের আসল এনক্রিপশন কীটির ভিত্তি হিসাবে এই ভাগ করা গোপনীয়তাটি ব্যবহার করব।
const crypto = require('crypto');
const salt = crypto.randomBytes(16);
// Node has ECDH built-in to the standard crypto library. For some languages
// you may need to use a third-party library.
const serverECDH = crypto.createECDH('prime256v1');
const serverPublicKey = serverECDH.generateKeys();
const sharedSecret = serverECDH.computeSecret(clientPublicKey);
এইচকেডিএফ
ইতিমধ্যে অন্য একপাশে সময়। ধরা যাক যে আপনার কাছে কিছু গোপন ডেটা রয়েছে যা আপনি এনক্রিপশন কী হিসাবে ব্যবহার করতে চান তবে এটি ক্রিপ্টোগ্রাফিকভাবে যথেষ্ট সুরক্ষিত নয়। উচ্চ সুরক্ষার সাথে স্বল্প সুরক্ষার সাথে একটি গোপনীয়তা রূপান্তর করতে আপনি এইচএমএসি-ভিত্তিক কী ডেরাইভেশন ফাংশন (এইচকেডিএফ) ব্যবহার করতে পারেন।
এটি যেভাবে কাজ করে তার একটি পরিণতি হ'ল এটি আপনাকে যে কোনও সংখ্যক বিটের একটি গোপনীয়তা নিতে এবং আপনার যে কোনও হ্যাশিং অ্যালগরিদম ব্যবহার করে তা দ্বারা উত্পাদিত হ্যাশের চেয়ে 255 গুণ পর্যন্ত যে কোনও আকারের অন্য কোনও গোপনীয়তা তৈরি করতে দেয়। ধাক্কা দেওয়ার জন্য, স্পেকটির জন্য আমাদের SHA-256 ব্যবহার করা প্রয়োজন, যার হ্যাশ দৈর্ঘ্য 32 বাইট (256 বিট)।
যেমনটি ঘটে, আমরা জানি যে আমাদের কেবল আকারে 32 বাইট পর্যন্ত কী তৈরি করতে হবে। এর অর্থ হ'ল আমরা অ্যালগরিদমের একটি সরল সংস্করণ ব্যবহার করতে পারি যা বৃহত্তর আউটপুট আকারগুলি পরিচালনা করতে পারে না।
আমি নীচে একটি নোড সংস্করণের জন্য কোডটি অন্তর্ভুক্ত করেছি, তবে আপনি এটি আরএফসি 5869 এ আসলে কীভাবে কাজ করে তা জানতে পারেন।
এইচকেডিএফ-এর ইনপুটগুলি হ'ল একটি লবণ, কিছু প্রাথমিক কীিং উপাদান (আইকেএম), বর্তমান ব্যবহার-কেস (তথ্য) এর জন্য নির্দিষ্ট কাঠামোগত ডেটার একটি al চ্ছিক টুকরা এবং কাঙ্ক্ষিত আউটপুট কীটির বাইটের দৈর্ঘ্য।
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
if (length > 32) {
throw new Error('Cannot return keys of more than 32 bytes, ${length} requested');
}
// 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);
}
এনক্রিপশন প্যারামিটারগুলি প্রাপ্ত
আমরা এখন আমাদের কাছে থাকা ডেটাগুলি প্রকৃত এনক্রিপশনের জন্য পরামিতিগুলিতে পরিণত করতে এইচকেডিএফ ব্যবহার করি।
আমরা প্রথমে যা করি তা হ'ল ক্লায়েন্ট এথ সিক্রেট এবং ভাগ করা সিক্রেটকে আরও দীর্ঘ, আরও ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত গোপনে মিশ্রিত করতে এইচকেডিএফ ব্যবহার করুন। স্পেসে এটিকে সিউডো-র্যান্ডম কী (পিআরকে) হিসাবে উল্লেখ করা হয়েছে যাতে আমি এটিকে এখানে বলব, যদিও ক্রিপ্টোগ্রাফি পিউরিস্টরা লক্ষ করতে পারে যে এটি কঠোরভাবে পিআরকে নয়।
এখন আমরা চূড়ান্ত সামগ্রী এনক্রিপশন কী এবং একটি ননস তৈরি করি যা সাইফারে পাস করা হবে। এগুলি প্রত্যেকের জন্য একটি সাধারণ ডেটা স্ট্রাকচার তৈরি করে তৈরি করা হয়, যা স্পেসে একটি তথ্য হিসাবে উল্লেখ করা হয় , এতে বার্তার উত্সটি আরও যাচাই করার জন্য উপবৃত্তাকার বক্ররেখা, প্রেরক এবং রিসিভার সম্পর্কিত নির্দিষ্ট তথ্য রয়েছে। তারপরে আমরা PRK, আমাদের লবণ এবং তথ্যটি সঠিক আকারের কী এবং ননসটি অর্জনের জন্য এইচকেডিএফ ব্যবহার করি।
সামগ্রী এনক্রিপশনের জন্য তথ্যের ধরণটি হ'ল 'এএসজিসিএম' যা পুশ এনক্রিপশনের জন্য ব্যবহৃত সাইফারের নাম।
const authInfo = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(clientAuthSecret, sharedSecret, authInfo, 32);
function createInfo(type, clientPublicKey, serverPublicKey) {
const len = type.length;
// The start index for each element within the buffer is:
// value | length | start |
// -----------------------------------------
// 'Content-Encoding: '| 18 | 0 |
// type | len | 18 |
// nul byte | 1 | 18 + len |
// 'P-256' | 5 | 19 + len |
// nul byte | 1 | 24 + len |
// client key length | 2 | 25 + len |
// client key | 65 | 27 + len |
// server key length | 2 | 92 + len |
// server key | 65 | 94 + len |
// For the purposes of push encryption the length of the keys will
// always be 65 bytes.
const info = new Buffer(18 + len + 1 + 5 + 1 + 2 + 65 + 2 + 65);
// The string 'Content-Encoding: ', as utf-8
info.write('Content-Encoding: ');
// The 'type' of the record, a utf-8 string
info.write(type, 18);
// A single null-byte
info.write('\0', 18 + len);
// The string 'P-256', declaring the elliptic curve being used
info.write('P-256', 19 + len);
// A single null-byte
info.write('\0', 24 + len);
// The length of the client's public key as a 16-bit integer
info.writeUInt16BE(clientPublicKey.length, 25 + len);
// Now the actual client public key
clientPublicKey.copy(info, 27 + len);
// Length of our public key
info.writeUInt16BE(serverPublicKey.length, 92 + len);
// The key itself
serverPublicKey.copy(info, 94 + len);
return info;
}
// Derive the Content Encryption Key
const contentEncryptionKeyInfo = createInfo('aesgcm', clientPublicKey, serverPublicKey);
const contentEncryptionKey = hkdf(salt, prk, contentEncryptionKeyInfo, 16);
// Derive the Nonce
const nonceInfo = createInfo('nonce', clientPublicKey, serverPublicKey);
const nonce = hkdf(salt, prk, nonceInfo, 12);
প্যাডিং
অন্য একদিকে, এবং একটি নির্বোধ এবং স্বীকৃত উদাহরণের জন্য সময়। ধরা যাক যে আপনার বসের একটি সার্ভার রয়েছে যা প্রতি কয়েক মিনিটে কোম্পানির শেয়ারের দামের সাথে তাকে একটি পুশ বার্তা প্রেরণ করে। এর জন্য সরল বার্তাটি সর্বদা সেন্টের মান সহ একটি 32-বিট পূর্ণসংখ্যা হবে। ক্যাটারিং স্টাফদের সাথেও তার একটি ছদ্মবেশী চুক্তি রয়েছে যার অর্থ তারা প্রকৃতপক্ষে পৌঁছে দেওয়ার 5 মিনিট আগে "ব্রেক রুমে ডোনটস" স্ট্রিংটি পাঠাতে পারে যাতে তারা উপস্থিত হয়ে সেখানে "কাকতালীয়ভাবে" থাকতে পারে এবং সেরাটি ধরতে পারে।
ওয়েব পুশ দ্বারা ব্যবহৃত সাইফার এনক্রিপ্টড মান তৈরি করে যা এনক্রিপ্টড ইনপুটটির চেয়ে ঠিক 16 বাইট দীর্ঘ। যেহেতু "ব্রেক রুমে ডোনটস" দীর্ঘতর যে 32-বিট স্টক মূল্য, যে কোনও স্নুপিং কর্মচারী যখন ডোনটগুলি বার্তাগুলি ডিক্রিপ্ট না করে কেবল তথ্যের দৈর্ঘ্য থেকে আগত হবে তা বলতে সক্ষম হবেন।
এই কারণে, ওয়েব পুশ প্রোটোকল আপনাকে ডেটার শুরুতে প্যাডিং যুক্ত করতে দেয়। আপনি কীভাবে এটি ব্যবহার করেন তা আপনার অ্যাপ্লিকেশনটির উপর নির্ভর করে তবে উপরের উদাহরণে আপনি সমস্ত বার্তাগুলি ঠিক 32 বাইট হতে প্যাড করতে পারেন, কেবল দৈর্ঘ্যের ভিত্তিতে বার্তাগুলি পৃথক করা অসম্ভব করে তোলে।
প্যাডিং মানটি একটি 16-বিট বিগ-এন্ডিয়ান পূর্ণসংখ্যা যা প্যাডিংয়ের দৈর্ঘ্য নির্দিষ্ট করে তারপরে প্যাডিংয়ের NUL
বাইটের সংখ্যা। সুতরাং সর্বনিম্ন প্যাডিং দুটি বাইট - সংখ্যাটি 16 বিটগুলিতে এনকোড করা হয়েছে।
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeroes, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
যখন আপনার পুশ বার্তাটি ক্লায়েন্টে উপস্থিত হয়, ব্রাউজারটি স্বয়ংক্রিয়ভাবে কোনও প্যাডিং সরিয়ে ফেলতে সক্ষম হবে, সুতরাং আপনার ক্লায়েন্ট কোডটি কেবল আনপ্যাডেড বার্তা গ্রহণ করে।
এনক্রিপশন
এখন আমাদের শেষ পর্যন্ত এনক্রিপশন করার জন্য সমস্ত জিনিস রয়েছে। ওয়েব পুশের জন্য প্রয়োজনীয় সাইফারটি হ'ল জিসিএম ব্যবহার করে এইএস 128 । আমরা আমাদের সামগ্রী এনক্রিপশন কীটি কী হিসাবে এবং ননসকে ইনিশিয়ালাইজেশন ভেক্টর (iv) হিসাবে ব্যবহার করি।
এই উদাহরণে আমাদের ডেটা একটি স্ট্রিং, তবে এটি কোনও বাইনারি ডেটা হতে পারে। আপনি এনক্রিপশন তথ্যের জন্য 16 -বাইট এবং প্যাডিংয়ের জন্য কমপক্ষে 2 বাইট সহ 4078 বাইট - 4096 বাইটের সর্বাধিক পে -লোড পাঠাতে পারেন।
// Create a buffer from our data, in this case a UTF-8 encoded string
const plaintext = new Buffer('Push notification payload!', 'utf8');
const cipher = crypto.createCipheriv('id-aes128-GCM', contentEncryptionKey,
nonce);
const result = cipher.update(Buffer.concat(padding, plaintext));
cipher.final();
// Append the auth tag to the result - https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
return Buffer.concat([result, cipher.getAuthTag()]);
ওয়েব পুশ
ফাউ! এখন আপনার কাছে একটি এনক্রিপ্ট করা পে -লোড রয়েছে, আপনাকে কেবল ব্যবহারকারীর সাবস্ক্রিপশন দ্বারা নির্দিষ্ট শেষ পয়েন্টে একটি তুলনামূলকভাবে সহজ এইচটিটিপি পোস্টের অনুরোধ তৈরি করতে হবে।
আপনার তিনটি শিরোনাম সেট করা দরকার।
Encryption: salt=<SALT>
Crypto-Key: dh=<PUBLICKEY>
Content-Encoding: aesgcm
<SALT>
এবং <PUBLICKEY>
হ'ল এনক্রিপশনে ব্যবহৃত লবণ এবং সার্ভার পাবলিক কী, ইউআরএল-নিরাপদ বেস 64 হিসাবে এনকোড করা।
ওয়েব পুশ প্রোটোকল ব্যবহার করার সময়, পোস্টের দেহটি তখন এনক্রিপ্ট করা বার্তার কাঁচা বাইট হয়। তবে, ক্রোম এবং ফায়ারবেস ক্লাউড মেসেজিং প্রোটোকল সমর্থন না করা পর্যন্ত আপনি সহজেই আপনার বিদ্যমান জসন পে -লোডের ডেটা নিম্নলিখিত হিসাবে অন্তর্ভুক্ত করতে পারেন।
{
"registration_ids": [ "…" ],
"raw_data": "BIXzEKOFquzVlr/1tS1bhmobZ…"
}
rawData
সম্পত্তির মান অবশ্যই এনক্রিপ্ট করা বার্তার বেস 64 এনকোডেড উপস্থাপনা হতে হবে।
ডিবাগিং / যাচাইকারী
পিটার বেভারলু, অন্যতম ক্রোম ইঞ্জিনিয়ার যারা বৈশিষ্ট্যটি বাস্তবায়ন করেছিলেন (পাশাপাশি স্পেসে কাজ করা লোকদের মধ্যে একজন) একটি যাচাইকারী তৈরি করেছেন ।
আপনার কোডটি এনক্রিপশনের প্রতিটি মধ্যবর্তী মানকে আউটপুট করার জন্য আপনি সেগুলি যাচাই করতে পারেন এবং আপনি সঠিক পথে আছেন কিনা তা পরীক্ষা করে দেখতে পারেন।