ওয়েব পুশ প্রোটোকল

আমরা দেখেছি কিভাবে একটি লাইব্রেরি পুশ বার্তা ট্রিগার করতে ব্যবহার করা যেতে পারে, কিন্তু এই লাইব্রেরিগুলি ঠিক কী করছে?

ঠিক আছে, তারা নেটওয়ার্ক অনুরোধ করছে যখন এই ধরনের অনুরোধগুলি সঠিক বিন্যাস নিশ্চিত করে। এই নেটওয়ার্ক অনুরোধটি সংজ্ঞায়িত করে সেটি হল ওয়েব পুশ প্রোটোকল

আপনার সার্ভার থেকে একটি পুশ পরিষেবাতে একটি পুশ বার্তা পাঠানোর চিত্র

এই বিভাগটি রূপরেখা দেয় যে কীভাবে সার্ভার নিজেকে অ্যাপ্লিকেশন সার্ভার কীগুলির সাথে সনাক্ত করতে পারে এবং কীভাবে এনক্রিপ্ট করা পেলোড এবং সম্পর্কিত ডেটা পাঠানো হয়।

এটি ওয়েব পুশের একটি সুন্দর দিক নয় এবং আমি এনক্রিপশনে কোনও বিশেষজ্ঞ নই, তবে আসুন প্রতিটি অংশটি দেখে নেওয়া যাক যেহেতু এই লাইব্রেরিগুলি হুডের নীচে কী করছে তা জানা সহজ।

অ্যাপ্লিকেশন সার্ভার কী

যখন আমরা একজন ব্যবহারকারীকে সাবস্ক্রাইব করি, তখন আমরা একটি applicationServerKey পাস করি। এই কীটি পুশ পরিষেবাতে পাস করা হয় এবং এটি পরীক্ষা করতে ব্যবহৃত হয় যে ব্যবহারকারীর সদস্যতা নেওয়া অ্যাপ্লিকেশনটিও সেই অ্যাপ্লিকেশন যা পুশ বার্তাগুলিকে ট্রিগার করছে৷

যখন আমরা একটি পুশ মেসেজ ট্রিগার করি, তখন হেডারের একটি সেট থাকে যা আমরা পাঠাই যা পুশ পরিষেবাকে অ্যাপ্লিকেশনকে প্রমাণীকরণ করতে দেয়। (এটি VAPID বৈশিষ্ট্য দ্বারা সংজ্ঞায়িত করা হয়।)

এই সব আসলে মানে কি এবং ঠিক কি ঘটে? আচ্ছা এইগুলি হল অ্যাপ্লিকেশন সার্ভার প্রমাণীকরণের জন্য নেওয়া পদক্ষেপগুলি:

  1. অ্যাপ্লিকেশন সার্ভার এটির ব্যক্তিগত অ্যাপ্লিকেশন কী দিয়ে কিছু JSON তথ্য স্বাক্ষর করে।
  2. এই স্বাক্ষরিত তথ্য একটি POST অনুরোধে শিরোনাম হিসাবে পুশ পরিষেবাতে পাঠানো হয়।
  3. পুশ পরিষেবাটি pushManager.subscribe() থেকে প্রাপ্ত সংরক্ষিত পাবলিক কী ব্যবহার করে প্রাপ্ত তথ্য চেক করতে পাবলিক কী সম্পর্কিত ব্যক্তিগত কী দ্বারা স্বাক্ষরিত। মনে রাখবেন : সর্বজনীন কী হল সাবস্ক্রাইব কলে পাস করা applicationServerKey
  4. স্বাক্ষরিত তথ্য বৈধ হলে পুশ পরিষেবা ব্যবহারকারীকে পুশ বার্তা পাঠায়।

তথ্যের এই প্রবাহের একটি উদাহরণ নীচে দেওয়া হল। (সর্বজনীন এবং ব্যক্তিগত কীগুলি নির্দেশ করতে নীচে বাম দিকের কিংবদন্তিটি নোট করুন।)

একটি বার্তা পাঠানোর সময় ব্যক্তিগত অ্যাপ্লিকেশন সার্ভার কী কীভাবে ব্যবহার করা হয় তার উদাহরণ

অনুরোধে একটি হেডারে যোগ করা "স্বাক্ষরিত তথ্য" হল একটি JSON ওয়েব টোকেন।

JSON ওয়েব টোকেন

একটি JSON ওয়েব টোকেন (বা সংক্ষেপে JWT) হল একটি তৃতীয় পক্ষের কাছে একটি বার্তা পাঠানোর একটি উপায় যাতে প্রাপক যাচাই করতে পারে যে এটি কে পাঠিয়েছে।

যখন কোনও তৃতীয় পক্ষ একটি বার্তা পায়, তখন তাদের প্রেরকদের সর্বজনীন কী পেতে হবে এবং JWT-এর স্বাক্ষর যাচাই করতে এটি ব্যবহার করতে হবে। যদি স্বাক্ষরটি বৈধ হয় তাহলে JWT-কে অবশ্যই প্রাইভেট কী দিয়ে স্বাক্ষর করতে হবে তাই প্রত্যাশিত প্রেরকের কাছ থেকে হতে হবে।

https://jwt.io/- এ অনেকগুলি লাইব্রেরি রয়েছে যেগুলি আপনার জন্য স্বাক্ষর করতে পারে এবং আমি আপনাকে সুপারিশ করব যে আপনি যেখানে পারেন তা করুন৷ সম্পূর্ণতার জন্য, আসুন দেখি কিভাবে ম্যানুয়ালি একটি স্বাক্ষরিত JWT তৈরি করা যায়।

ওয়েব পুশ এবং স্বাক্ষরিত JWTs

একটি স্বাক্ষরিত JWT শুধুমাত্র একটি স্ট্রিং, যদিও এটি বিন্দু দ্বারা যুক্ত তিনটি স্ট্রিং হিসাবে বিবেচিত হতে পারে।

একটি JSON ওয়েব টোকেনে স্ট্রিংগুলির একটি চিত্র

প্রথম এবং দ্বিতীয় স্ট্রিংগুলি (JWT তথ্য এবং JWT ডেটা) হল JSON-এর টুকরো যা বেস64 এনকোড করা হয়েছে, যার অর্থ এটি সর্বজনীনভাবে পাঠযোগ্য।

প্রথম স্ট্রিংটি 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 মান হল জেডব্লিউটি-এর মেয়াদ শেষ হয়ে যাওয়া, এটি স্নুপারদের একটি জেডব্লিউটিকে বাধা দিলে পুনরায় ব্যবহার করতে সক্ষম হতে বাধা দেয়। মেয়াদ সেকেন্ডের মধ্যে একটি টাইমস্ট্যাম্প এবং 24 ঘন্টা আর হতে হবে না।

Node.js-এ মেয়াদ শেষ হওয়ার তারিখটি ব্যবহার করে সেট করা হয়েছে:

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

প্রেরণ অ্যাপ্লিকেশন এবং পুশ পরিষেবার মধ্যে ঘড়ির পার্থক্যের সাথে কোনও সমস্যা এড়াতে এটি 24 ঘন্টার পরিবর্তে 12 ঘন্টা।

অবশেষে, sub মানটি একটি URL বা একটি mailto ইমেল ঠিকানা হতে হবে৷ এটি যাতে প্রেরকের কাছে পৌঁছানোর জন্য একটি পুশ পরিষেবার প্রয়োজন হলে, এটি JWT থেকে যোগাযোগের তথ্য খুঁজে পেতে পারে। (এই কারণেই ওয়েব-পুশ লাইব্রেরির একটি ইমেল ঠিকানা প্রয়োজন)।

JWT তথ্যের মতো, JWT ডেটা একটি URL নিরাপদ বেস64 স্ট্রিং হিসাবে এনকোড করা হয়েছে।

তৃতীয় স্ট্রিং, স্বাক্ষর হল প্রথম দুটি স্ট্রিং (জেডব্লিউটি ইনফো এবং জেডব্লিউটি ডেটা) নেওয়ার ফলাফল, তাদের একটি ডট অক্ষর দিয়ে যুক্ত করা, যাকে আমরা "আনসাইনড টোকেন" বলব, এবং এটিতে স্বাক্ষর করা।

সাইনিং প্রক্রিয়ার জন্য ES256 ব্যবহার করে "আনসাইন করা টোকেন" এনক্রিপ্ট করা প্রয়োজন। JWT স্পেক অনুসারে, ES256 "P-256 বক্ররেখা এবং SHA-256 হ্যাশ অ্যালগরিদম ব্যবহার করে ECDSA" এর জন্য সংক্ষিপ্ত। ওয়েব ক্রিপ্টো ব্যবহার করে আপনি স্বাক্ষর তৈরি করতে পারেন এভাবে:

// 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 (অর্থাৎ তিনটি স্ট্রিং ডট দ্বারা যুক্ত), ওয়েব পুশ পরিষেবাতে পাঠানো হয় যেমন WebPush এর সাথে Authorization হেডার, যেমন:

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

ওয়েব পুশ প্রোটোকল আরও বলেছে যে পাবলিক অ্যাপ্লিকেশন সার্ভার কীটি অবশ্যই Crypto-Key হেডারে একটি URL নিরাপদ বেস64 এনকোডেড স্ট্রিং হিসাবে পাঠাতে হবে যার সাথে p256ecdsa= পূর্বে প্রিপেন্ড করা হয়েছে।

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

পেলোড এনক্রিপশন

এর পরে দেখা যাক কিভাবে আমরা একটি পুশ বার্তা সহ একটি পেলোড পাঠাতে পারি যাতে আমাদের ওয়েব অ্যাপ যখন একটি পুশ বার্তা পায়, তখন এটি প্রাপ্ত ডেটা অ্যাক্সেস করতে পারে।

একটি সাধারণ প্রশ্ন যা অন্য পুশ পরিষেবাগুলি ব্যবহার করেছে এমন যে কোনও ব্যক্তির থেকে উদ্ভূত হয় তা হল কেন ওয়েব পুশ পেলোড এনক্রিপ্ট করা দরকার? নেটিভ অ্যাপের মাধ্যমে, পুশ মেসেজ প্লেইন টেক্সট হিসেবে ডেটা পাঠাতে পারে।

ওয়েব পুশের সৌন্দর্যের একটি অংশ হল যেহেতু সমস্ত পুশ পরিষেবা একই API (ওয়েব পুশ প্রোটোকল) ব্যবহার করে, তাই বিকাশকারীদের পুশ পরিষেবাটি কে তা চিন্তা করতে হবে না। আমরা সঠিক বিন্যাসে একটি অনুরোধ করতে পারি এবং একটি পুশ বার্তা পাঠানোর আশা করতে পারি। এর নেতিবাচক দিক হল যে বিকাশকারীরা ধারণাযোগ্যভাবে একটি পুশ পরিষেবাতে বার্তা পাঠাতে পারে যা বিশ্বাসযোগ্য নয়। পেলোড এনক্রিপ্ট করে, একটি পুশ পরিষেবা পাঠানো ডেটা পড়তে পারে না। শুধুমাত্র ব্রাউজার তথ্য ডিক্রিপ্ট করতে পারে. এটি ব্যবহারকারীর ডেটা রক্ষা করে।

পেলোডের এনক্রিপশন মেসেজ এনক্রিপশন স্পেসিকে সংজ্ঞায়িত করা হয়েছে।

পুশ মেসেজ পেলোড এনক্রিপ্ট করার নির্দিষ্ট ধাপগুলি দেখার আগে, আমাদের কিছু কৌশল কভার করা উচিত যা এনক্রিপশন প্রক্রিয়া চলাকালীন ব্যবহার করা হবে। (পুশ এনক্রিপশনের উপর তার চমৎকার নিবন্ধের জন্য ম্যাট স্কেলেসের বিশাল হ্যাট টিপ।)

ইসিডিএইচ এবং এইচকেডিএফ

ECDH এবং HKDF উভয়ই এনক্রিপশন প্রক্রিয়া জুড়ে ব্যবহৃত হয় এবং তথ্য এনক্রিপ্ট করার উদ্দেশ্যে সুবিধা প্রদান করে।

ECDH: উপবৃত্তাকার কার্ভ ডিফি-হেলম্যান কী বিনিময়

কল্পনা করুন যে আপনার কাছে দুটি লোক রয়েছে যারা তথ্য ভাগ করতে চায়, এলিস এবং বব। এলিস এবং বব উভয়েরই নিজস্ব পাবলিক এবং প্রাইভেট কী আছে। এলিস এবং বব তাদের পাবলিক কী একে অপরের সাথে ভাগ করে নেয়।

ECDH-এর সাহায্যে তৈরি করা কীগুলির দরকারী বৈশিষ্ট্য হল অ্যালিস তার ব্যক্তিগত কী এবং ববের পাবলিক কী ব্যবহার করে গোপন মান 'X' তৈরি করতে পারে। বব তার ব্যক্তিগত কী এবং অ্যালিসের পাবলিক কী নিয়ে স্বাধীনভাবে একই মান 'X' তৈরি করতে একই কাজ করতে পারে। এটি 'X' কে একটি ভাগ করা গোপনীয় করে তোলে এবং অ্যালিস এবং ববকে শুধুমাত্র তাদের সর্বজনীন কী ভাগ করতে হয়েছিল। এখন বব এবং এলিস তাদের মধ্যে বার্তা এনক্রিপ্ট এবং ডিক্রিপ্ট করতে 'X' ব্যবহার করতে পারে।

ECDH, আমার জানামতে, বক্ররেখার বৈশিষ্ট্যগুলিকে সংজ্ঞায়িত করে যা একটি ভাগ করা গোপন 'X' তৈরির এই "বৈশিষ্ট্য"কে অনুমতি দেয়।

এটি ECDH এর একটি উচ্চ স্তরের ব্যাখ্যা, আপনি যদি আরও জানতে চান তবে আমি এই ভিডিওটি দেখার পরামর্শ দিচ্ছি

কোড পদে; বেশিরভাগ ভাষা/প্ল্যাটফর্ম লাইব্রেরির সাথে আসে যাতে এই কীগুলি তৈরি করা সহজ হয়।

নোডে আমরা নিম্নলিখিতগুলি করব:

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

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

HKDF: HMAC ভিত্তিক কী ডেরিভেশন ফাংশন

উইকিপিডিয়ায় HKDF এর সংক্ষিপ্ত বিবরণ রয়েছে:

HKDF হল একটি HMAC ভিত্তিক কী ডেরিভেশন ফাংশন যা যেকোনো দুর্বল কী উপাদানকে ক্রিপ্টোগ্রাফিকভাবে শক্তিশালী কী উপাদানে রূপান্তরিত করে। এটি ব্যবহার করা যেতে পারে, উদাহরণস্বরূপ, ডিফি হেলম্যানের শেয়ার করা গোপনীয়তাগুলিকে এনক্রিপশন, অখণ্ডতা যাচাই বা প্রমাণীকরণে ব্যবহারের জন্য উপযুক্ত মূল উপাদানে রূপান্তর করতে।

মূলত, 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 হল অনিরাপদ উপাদান নেওয়া এবং এটিকে সুরক্ষিত করার একটি উপায়৷

এটি আমাদের পেলোডের এনক্রিপশনের সময় ব্যবহার করা হবে। এর পরে আসুন আমরা ইনপুট হিসাবে কী নিই এবং কীভাবে এটি এনক্রিপ্ট করা হয় তা দেখি।

ইনপুট

যখন আমরা পেলোড সহ একজন ব্যবহারকারীকে একটি পুশ বার্তা পাঠাতে চাই, তখন আমাদের তিনটি ইনপুট প্রয়োজন:

  1. পেলোড নিজেই.
  2. PushSubscription থেকে auth গোপন.
  3. 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 বাইট র্যান্ডম ডেটা হওয়া দরকার। নোডজেএস-এ, লবণ তৈরি করতে আমরা নিম্নলিখিতগুলি করব:

const salt = crypto.randomBytes(16);

পাবলিক/প্রাইভেট কী

পাবলিক এবং প্রাইভেট কীগুলি একটি P-256 উপবৃত্তাকার বক্ররেখা ব্যবহার করে তৈরি করা উচিত, যা আমরা নোডে করতে চাই:

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

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

আমরা এই কীগুলিকে "স্থানীয় কী" হিসাবে উল্লেখ করব। এগুলি কেবল এনক্রিপশনের জন্য ব্যবহৃত হয় এবং অ্যাপ্লিকেশন সার্ভার কীগুলির সাথে কোনও সম্পর্ক নেই৷

ইনপুট হিসাবে পেলোড, অথ সিক্রেট এবং সাবস্ক্রিপশন পাবলিক কী এবং একটি নতুন তৈরি সল্ট এবং স্থানীয় কীগুলির সেট সহ, আমরা আসলে কিছু এনক্রিপশন করতে প্রস্তুত।

শেয়ার করা গোপন কথা

প্রথম ধাপ হল সাবস্ক্রিপশন পাবলিক কী এবং আমাদের নতুন প্রাইভেট কী ব্যবহার করে একটি শেয়ার করা গোপনীয়তা তৈরি করা (এলিস এবং ববের সাথে ECDH ব্যাখ্যা মনে রাখবেন? ঠিক তেমনই)।

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

এটি সিউডো র্যান্ডম কী (PRK) গণনা করার জন্য পরবর্তী ধাপে ব্যবহৃত হয়।

সিউডো র্যান্ডম কী

Pseudo Random Key (PRK) হল পুশ সাবস্ক্রিপশনের প্রমাণীকরণ গোপনের সংমিশ্রণ, এবং শেয়ার করা গোপনীয়তা যা আমরা এইমাত্র তৈরি করেছি।

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 মান সহ একটি বাইট অনুসরণ করবে, এনক্রিপ্ট করা ডেটা অনুসরণ করবে৷

আমাদের Pseudo Random Key কেবলমাত্র HKDF এর মাধ্যমে প্রমাণীকরণ, ভাগ করা গোপন এবং এনকোডিং তথ্যের একটি অংশ (অর্থাৎ এটিকে ক্রিপ্টোগ্রাফিকভাবে শক্তিশালী করে) চালাচ্ছে।

প্রসঙ্গ

"প্রসঙ্গ" হল বাইটের একটি সেট যা পরে এনক্রিপশন ব্রাউজারে দুটি মান গণনা করতে ব্যবহৃত হয়। এটি মূলত সাবস্ক্রিপশন পাবলিক কী এবং স্থানীয় পাবলিক কী ধারণকারী বাইটের একটি অ্যারে।

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

চূড়ান্ত প্রসঙ্গ বাফার হল একটি লেবেল, সাবস্ক্রিপশন পাবলিক কী-তে বাইটের সংখ্যা, কী নিজেই অনুসরণ করে, তারপর স্থানীয় পাবলিক কী-এর পরে বাইটের সংখ্যা।

এই প্রসঙ্গ মান দিয়ে আমরা এটি একটি ননস এবং একটি বিষয়বস্তু এনক্রিপশন কী (CEK) তৈরিতে ব্যবহার করতে পারি।

বিষয়বস্তু এনক্রিপশন কী এবং ননস

একটি নন্স হল একটি মান যা রিপ্লে আক্রমণ প্রতিরোধ করে কারণ এটি শুধুমাত্র একবার ব্যবহার করা উচিত।

কন্টেন্ট এনক্রিপশন কী (CEK) হল সেই কী যা শেষ পর্যন্ত আমাদের পেলোড এনক্রিপ্ট করতে ব্যবহার করা হবে।

প্রথমে আমাদের ননস এবং CEK-এর জন্য ডেটার বাইট তৈরি করতে হবে, যা কেবলমাত্র একটি বিষয়বস্তু এনকোডিং স্ট্রিং যা আমরা এইমাত্র গণনা করেছি প্রসঙ্গ বাফার দ্বারা অনুসরণ করে:

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 সাইফার তৈরি করি এবং ননসটি একটি প্রাথমিক ভেক্টর।

নোডে এটি এভাবে করা হয়:

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

আমরা এখন আমাদের এনক্রিপ্ট করা পেলোড আছে. হ্যাঁ!

এই পেলোডটি কীভাবে পুশ পরিষেবাতে পাঠানো হয় তা নির্ধারণ করা বাকি থাকে।

এনক্রিপ্ট করা পেলোড হেডার এবং বডি

এই এনক্রিপ্ট করা পেলোডটি পুশ পরিষেবাতে পাঠাতে আমাদের POST অনুরোধে কয়েকটি ভিন্ন শিরোনাম সংজ্ঞায়িত করতে হবে।

এনক্রিপশন হেডার

'এনক্রিপশন' হেডারে অবশ্যই পেলোড এনক্রিপ্ট করার জন্য ব্যবহৃত লবণ থাকতে হবে।

16 বাইট লবণ বেস64 ইউআরএল নিরাপদ এনকোড করা উচিত এবং এনক্রিপশন হেডারে যোগ করা উচিত, যেমন:

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 সেট করা আছে। এর কারণ হল এনক্রিপ্ট করা পেলোড অবশ্যই বাইটের একটি স্ট্রীম হিসাবে পাঠাতে হবে।

নোডজেএস-এ আমরা এটি করতে চাই:

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

আরো হেডার?

আমরা জেডব্লিউটি/অ্যাপ্লিকেশন সার্ভার কী-এর জন্য ব্যবহৃত শিরোনামগুলি কভার করেছি (অর্থাৎ কীভাবে পুশ পরিষেবা দিয়ে অ্যাপ্লিকেশনটি সনাক্ত করতে হয়) এবং আমরা একটি এনক্রিপ্ট করা পেলোড পাঠাতে ব্যবহৃত হেডারগুলি কভার করেছি।

অতিরিক্ত শিরোনাম রয়েছে যা প্রেরিত বার্তাগুলির আচরণ পরিবর্তন করতে পুশ পরিষেবাগুলি ব্যবহার করে। এই শিরোনামগুলির মধ্যে কিছু প্রয়োজন, অন্যগুলি ঐচ্ছিক৷

TTL হেডার

প্রয়োজন

TTL (বা লাইভ টাইম) হল একটি পূর্ণসংখ্যা যা আপনার পুশ মেসেজটি ডেলিভারির আগে পুশ সার্ভিসে কত সেকেন্ড থাকতে চান তা নির্দিষ্ট করে। TTL মেয়াদ শেষ হলে, বার্তাটি পুশ পরিষেবা সারি থেকে সরানো হবে এবং এটি বিতরণ করা হবে না।

TTL: [Time to live in seconds]

আপনি যদি শূন্যের একটি TTL সেট করেন, পুশ পরিষেবা অবিলম্বে বার্তা দেওয়ার চেষ্টা করবে, কিন্তু যদি ডিভাইসে পৌঁছানো না যায়, তাহলে আপনার বার্তাটি পুশ পরিষেবা সারি থেকে অবিলম্বে বাদ দেওয়া হবে৷

টেকনিক্যালি একটি পুশ সার্ভিস চাইলে একটি পুশ মেসেজের TTL কমাতে পারে। আপনি একটি পুশ পরিষেবা থেকে প্রতিক্রিয়াতে TTL হেডার পরীক্ষা করে এটি ঘটেছে কিনা তা বলতে পারেন।

বিষয়

ঐচ্ছিক

বিষয়গুলি হল স্ট্রিং যা একটি মুলতুবি থাকা বার্তাগুলিকে একটি নতুন বার্তা দিয়ে প্রতিস্থাপন করতে ব্যবহার করা যেতে পারে যদি তাদের সাথে মিলে যাওয়া বিষয়ের নাম থাকে৷

এটি এমন পরিস্থিতিতে উপযোগী যেখানে একটি ডিভাইস অফলাইনে থাকাকালীন একাধিক বার্তা পাঠানো হয় এবং আপনি সত্যিই চান যে ডিভাইসটি চালু থাকা অবস্থায় একজন ব্যবহারকারী সর্বশেষ বার্তাটি দেখুক।

জরুরী

ঐচ্ছিক

জরুরীতা পুশ পরিষেবাকে নির্দেশ করে যে ব্যবহারকারীর কাছে একটি বার্তা কতটা গুরুত্বপূর্ণ। ব্যাটারি কম থাকা অবস্থায় শুধুমাত্র গুরুত্বপূর্ণ বার্তাগুলির জন্য জেগে থাকার মাধ্যমে ব্যবহারকারীর ডিভাইসের ব্যাটারি লাইফ সংরক্ষণে সাহায্য করার জন্য পুশ পরিষেবা দ্বারা এটি ব্যবহার করা যেতে পারে।

হেডার মান নীচে দেখানো হিসাবে সংজ্ঞায়িত করা হয়. ডিফল্ট মান normal

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

সবকিছু একসাথে

এই সমস্ত কীভাবে কাজ করে সে সম্পর্কে আপনার যদি আরও প্রশ্ন থাকে তবে আপনি সর্বদা দেখতে পারেন কিভাবে লাইব্রেরিগুলি ওয়েব-পুশ-লিবস org- এ পুশ বার্তাগুলিকে ট্রিগার করে।

একবার আপনার কাছে একটি এনক্রিপ্ট করা পেলোড এবং উপরের শিরোনামগুলি হয়ে গেলে, আপনাকে শুধুমাত্র একটি PushSubscriptionendpoint একটি POST অনুরোধ করতে হবে।

তাই এই পোস্ট অনুরোধের প্রতিক্রিয়া দিয়ে আমরা কি করব?

ধাক্কা সেবা থেকে প্রতিক্রিয়া

একবার আপনি একটি পুশ পরিষেবাতে একটি অনুরোধ করার পরে, আপনাকে প্রতিক্রিয়াটির স্ট্যাটাস কোড পরীক্ষা করতে হবে কারণ এটি আপনাকে বলবে যে অনুরোধটি সফল হয়েছে কিনা৷

স্ট্যাটাস কোড বর্ণনা
201 তৈরি হয়েছে। একটি পুশ বার্তা পাঠানোর অনুরোধটি গৃহীত হয়েছিল এবং গৃহীত হয়েছিল৷
429 অনেক অনুরোধ. মানে আপনার অ্যাপ্লিকেশন সার্ভার একটি পুশ পরিষেবার সাথে একটি হারের সীমাতে পৌঁছেছে। পুশ পরিষেবাটিতে একটি 'পুনরায় চেষ্টা-পরবর্তী' শিরোনাম অন্তর্ভুক্ত করা উচিত যাতে নির্দেশ করা যায় যে কতক্ষণ আগে আরেকটি অনুরোধ করা যেতে পারে।
400 অনুরোধ অগ্রহণযোগ্য. এটি সাধারণত আপনার শিরোনামগুলির একটি অবৈধ বা ভুলভাবে বিন্যাস করা হয় মানে।
404 পাওয়া যায়নি। এটি একটি ইঙ্গিত যে সদস্যতা মেয়াদ শেষ হয়ে গেছে এবং ব্যবহার করা যাবে না। এই ক্ষেত্রে আপনার 'পুশসাবস্ক্রিপশন' মুছে ফেলা উচিত এবং ক্লায়েন্টের ব্যবহারকারীর পুনরায় সদস্যতা নেওয়ার জন্য অপেক্ষা করা উচিত।
410 সর্বস্বান্ত. সদস্যতা আর বৈধ নয় এবং অ্যাপ্লিকেশন সার্ভার থেকে সরানো উচিত। এটি একটি `পুশসাবস্ক্রিপশন`-এ `আনসাবস্ক্রাইব()` কল করে পুনরুত্পাদন করা যেতে পারে।
413 পেলোডের আকার খুব বড়। সর্বনিম্ন আকারের পেলোড একটি পুশ পরিষেবাকে সমর্থন করতে হবে 4096 বাইট (বা 4kb)।

পরের দিকে কোথায় যেতে হবে

কোড ল্যাব