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

Matt Gaunt

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

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

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

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

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

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

যখন আমরা একজন ব্যবহারকারীকে সাবস্ক্রাইব করি, তখন আমরা একটি 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)।

আপনি HTTP স্ট্যাটাস কোড সম্পর্কে আরও তথ্যের জন্য ওয়েব পুশ স্ট্যান্ডার্ড (RFC8030) পড়তে পারেন।

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

কোড ল্যাব

,

Matt Gaunt

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

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

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

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

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

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

যখন আমরা একজন ব্যবহারকারীকে সাবস্ক্রাইব করি, তখন আমরা একটি 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();

আরো হেডার?

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

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

টিটিএল শিরোনাম

প্রয়োজন

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

TTL: [Time to live in seconds]

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

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

বিষয়

ঐচ্ছিক

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

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

জরুরী

ঐচ্ছিক

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

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

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

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

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

একবার আপনার কাছে একটি এনক্রিপ্ট করা পে -লোড এবং উপরের শিরোনামগুলি হয়ে গেলে, আপনাকে কেবল একটি PushSubscription endpoint একটি পোস্ট অনুরোধ করা দরকার।

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

পুশ পরিষেবা থেকে প্রতিক্রিয়া

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

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

এইচটিটিপি স্থিতি কোডগুলি সম্পর্কে আরও তথ্যের জন্য আপনি ওয়েব পুশ স্ট্যান্ডার্ড (আরএফসি 8030) পড়তে পারেন।

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

কোড ল্যাব

,

Matt Gaunt

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

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

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

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

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

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

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

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

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

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

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

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

অনুরোধে একটি শিরোনামে যুক্ত হওয়া "স্বাক্ষরিত তথ্য" হ'ল একটি জেএসএন ওয়েব টোকেন।

JSON ওয়েব টোকেন

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

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

Https://jwt.io/ এ প্রচুর লাইব্রেরি রয়েছে যা আপনার জন্য স্বাক্ষর সম্পাদন করতে পারে এবং আমি আপনাকে যেখানে করতে পারেন সেখানে করার পরামর্শ দিচ্ছি। সম্পূর্ণতার জন্য, আসুন কীভাবে ম্যানুয়ালি স্বাক্ষরিত জেডব্লিউটি তৈরি করতে হয় তা দেখুন।

ওয়েব পুশ এবং স্বাক্ষরিত জেডব্লিউটিএস

একটি স্বাক্ষরিত জেডাব্লুটিটি কেবল একটি স্ট্রিং, যদিও এটি বিন্দুগুলির সাথে যুক্ত তিনটি স্ট্রিং হিসাবে ভাবা যেতে পারে।

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

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

প্রথম স্ট্রিংটি নিজেই জেডব্লিউটি সম্পর্কে তথ্য, এটি নির্দেশ করে যে কোন অ্যালগরিদম স্বাক্ষর তৈরি করতে ব্যবহৃত হয়েছিল।

ওয়েব পুশের জন্য জেডব্লিউটি তথ্যটিতে অবশ্যই নিম্নলিখিত তথ্য থাকতে হবে:

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

দ্বিতীয় স্ট্রিংটি হ'ল জেডব্লিউটি ডেটা। এটি জেডব্লিউটি প্রেরক সম্পর্কে তথ্য সরবরাহ করে, কার জন্য এটি উদ্দেশ্যযুক্ত এবং এটি কতক্ষণ বৈধ।

ওয়েব পুশের জন্য, ডেটাটিতে এই ফর্ম্যাটটি থাকবে:

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

aud মানটি হ'ল "শ্রোতা", অর্থাত্ জেডব্লিউটি কে। ওয়েব পুশের জন্য শ্রোতাদের হ'ল পুশ পরিষেবা, তাই আমরা এটি পুশ পরিষেবার উত্সে সেট করি।

exp মানটি হ'ল জেডাব্লুটিটির মেয়াদোত্তীর্ণ, এটি স্নোপারদের যদি কোনও জেডব্লিউটি পুনরায় ব্যবহার করতে সক্ষম হতে বাধা দেয় তবে তারা যদি এটি বাধা দেয়। মেয়াদোত্তীর্ণতা সেকেন্ডের মধ্যে একটি টাইমস্ট্যাম্প এবং আর 24 ঘন্টা হতে হবে না।

নোড.জেএসে মেয়াদোত্তীর্ণতা ব্যবহার করে সেট করা হয়েছে:

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

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

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

জেডব্লিউটি তথ্যের মতোই, জেডব্লিউটি ডেটা একটি ইউআরএল নিরাপদ বেস 64 স্ট্রিং হিসাবে এনকোড করা হয়েছে।

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

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

// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');

// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;

// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
  kty: 'EC',
  crv: 'P-256',
  x: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(1, 33)),
  y: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(33, 65)),
  d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};

// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
  name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
  return crypto.subtle.sign({
    name: 'ECDSA',
    hash: {
      name: 'SHA-256',
    },
  }, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
  console.log('Signature: ', signature);
});

একটি পুশ পরিষেবা স্বাক্ষরটি ডিক্রিপ্ট করতে পাবলিক অ্যাপ্লিকেশন সার্ভার কী ব্যবহার করে একটি জেডব্লিউটি বৈধ করতে পারে এবং ডিক্রিপ্ট করা স্ট্রিংটি "স্বাক্ষরযুক্ত টোকেন" (অর্থাত্ জেডব্লিউটি -র প্রথম দুটি স্ট্রিং) এর সমান কিনা তা নিশ্চিত করতে পারে।

স্বাক্ষরিত জেডব্লিউটি (অর্থাত্ তিনটি স্ট্রিং ডট দ্বারা যোগদান করেছে), ওয়েব পুশ সার্ভিসে WebPush প্রস্তাবিত Authorization শিরোনাম হিসাবে প্রেরণ করা হয়েছে, এর মতো:

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

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

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

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

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

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

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

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

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

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

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

ইসিডিএইচ: উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান কী এক্সচেঞ্জ

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

ইসিডিএইচ দিয়ে উত্পন্ন কীগুলির দরকারী সম্পত্তি হ'ল অ্যালিস গোপন মান 'এক্স' তৈরি করতে তার ব্যক্তিগত কী এবং ববের পাবলিক কী ব্যবহার করতে পারে। ববও একই রকম করতে পারেন, তার ব্যক্তিগত কী এবং অ্যালিসের সর্বজনীন কীটি স্বাধীনভাবে একই মান 'এক্স' তৈরি করতে। এটি 'এক্স' কে একটি ভাগ করা গোপনীয় করে তোলে এবং অ্যালিস এবং ববকে কেবল তাদের সর্বজনীন কী ভাগ করে নিতে হয়েছিল। এখন বব এবং অ্যালিস তাদের মধ্যে এনক্রিপ্ট এবং ডিক্রিপ্ট করতে 'এক্স' ব্যবহার করতে পারে।

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

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

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

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

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

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

এইচকেডিএফ: এইচএমএসি ভিত্তিক কী ডেরাইভেশন ফাংশন

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

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

মূলত, এইচকেডিএফ এমন ইনপুট নেবে যা নির্দিষ্ট সুরক্ষিত নয় এবং এটিকে আরও সুরক্ষিত করে তোলে।

এই এনক্রিপশনটি সংজ্ঞায়িত করার জন্য আমাদের হ্যাশ অ্যালগরিদম হিসাবে SHA-256 এর ব্যবহার প্রয়োজন এবং ওয়েব পুশে এইচকেডিএফের ফলাফলের কীগুলি 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);
}

এই উদাহরণ কোডের জন্য ম্যাট স্কেলের নিবন্ধে টুপি টিপ।

এটি আলগাভাবে ইসিডিএইচ এবং এইচকেডিএফকে কভার করে।

ইসিডিএইচ পাবলিক কীগুলি ভাগ করে নেওয়ার এবং একটি ভাগ করা গোপন তৈরি করার একটি সুরক্ষিত উপায়। এইচকেডিএফ হ'ল অনিরাপদ উপাদান গ্রহণ এবং এটি সুরক্ষিত করার একটি উপায়।

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

ইনপুট

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

  1. পে -লোড নিজেই।
  2. PushSubscription থেকে auth গোপন।
  3. PushSubscription থেকে p256dh কী।

আমরা auth এবং p256dh মানগুলি PushSubscription থেকে পুনরুদ্ধার করা দেখেছি তবে দ্রুত অনুস্মারকটির জন্য, একটি সাবস্ক্রিপশন দেওয়া আমাদের এই মানগুলির প্রয়োজন হবে:

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

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

auth মানটি একটি গোপন হিসাবে বিবেচনা করা উচিত এবং আপনার আবেদনের বাইরে ভাগ করা উচিত নয়।

p256dh কীটি একটি পাবলিক কী, এটি কখনও কখনও ক্লায়েন্ট পাবলিক কী হিসাবে উল্লেখ করা হয়। এখানে আমরা p256dh কে সাবস্ক্রিপশন পাবলিক কী হিসাবে উল্লেখ করব। সাবস্ক্রিপশন পাবলিক কী ব্রাউজার দ্বারা উত্পাদিত হয়। ব্রাউজারটি প্রাইভেট কীটি গোপন রাখবে এবং এটি পে -লোড ডিক্রিপ্ট করার জন্য ব্যবহার করবে।

এই তিনটি মান, auth , p256dh এবং payload ইনপুট হিসাবে প্রয়োজন এবং এনক্রিপশন প্রক্রিয়াটির ফলাফলটি হ'ল এনক্রিপ্ট করা পেডলোড, একটি লবণের মান এবং কেবল ডেটা এনক্রিপ্ট করার জন্য ব্যবহৃত একটি পাবলিক কী।

লবণ

লবণটি এলোমেলো ডেটা 16 বাইট হতে হবে। নোডজেগুলিতে, আমরা একটি লবণ তৈরি করতে নিম্নলিখিতগুলি করব:

const salt = crypto.randomBytes(16);

সরকারী / ব্যক্তিগত কী

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

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

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

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

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

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

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

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

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

সিউডো এলোমেলো কী

সিউডো র‌্যান্ডম কী (পিআরকে) হ'ল পুশ সাবস্ক্রিপশন এর আথ সিক্রেট এবং আমরা সবেমাত্র তৈরি করা ভাগ করা গোপনীয়তার সংমিশ্রণ।

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

আপনি কী ভাবছেন যে Content-Encoding: auth\0 স্ট্রিং এর জন্য। সংক্ষেপে, এটির কোনও স্পষ্ট উদ্দেশ্য নেই, যদিও ব্রাউজারগুলি আগত বার্তা ডিক্রিপ্ট করতে পারে এবং প্রত্যাশিত সামগ্রী-এনকোডিংয়ের সন্ধান করতে পারে। \0 বাফারের শেষ থেকে 0 এর মান সহ একটি বাইট যুক্ত করে। ব্রাউজারগুলি বার্তাটি ডিক্রিপ্ট করে প্রত্যাশিত যে বিষয়বস্তু এনকোডিংয়ের জন্য এতগুলি বাইট আশা করবে, মান 0 সহ একটি বাইট অনুসরণ করবে, তারপরে এনক্রিপ্ট করা ডেটা অনুসরণ করবে।

আমাদের সিউডো এলোমেলো কীটি কেবল এইচকেডিএফ (যেমন এটি ক্রিপ্টোগ্রাফিকভাবে আরও শক্তিশালী করে তোলে) এর মাধ্যমে অ্যাথ, শেয়ারড সিক্রেট এবং এনকোডিং তথ্যের একটি অংশ চালাচ্ছে।

প্রসঙ্গ

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

const keyLabel = new Buffer('P-256\0', 'utf8');

// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');

const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;

const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;

const contextBuffer = Buffer.concat([
  keyLabel,
  subscriptionPubKeyLength.buffer,
  subscriptionPubKey,
  localPublicKeyLength.buffer,
  localPublicKey,
]);

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

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

সামগ্রী এনক্রিপশন কী এবং ননস

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

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

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

const nonceEncBuffer = new Buffer('Content-Encoding: nonce\0', 'utf8');
const nonceInfo = Buffer.concat([nonceEncBuffer, contextBuffer]);

const cekEncBuffer = new Buffer('Content-Encoding: aesgcm\0');
const cekInfo = Buffer.concat([cekEncBuffer, contextBuffer]);

এই তথ্যটি এইচকেডিএফের মাধ্যমে নুন এবং PRK এর সাথে ননসিনফো এবং সেকিনফোর সাথে চালিত হয়:

// 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 বাইট লবণ বেস 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();

আরও শিরোনাম?

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

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

টিটিএল শিরোনাম

প্রয়োজন

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

TTL: [Time to live in seconds]

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

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

বিষয়

ঐচ্ছিক

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

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

জরুরী

ঐচ্ছিক

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

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

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

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

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

একবার আপনার কাছে একটি এনক্রিপ্ট করা পে -লোড এবং উপরের শিরোনামগুলি হয়ে গেলে, আপনাকে কেবল একটি PushSubscription endpoint একটি পোস্ট অনুরোধ করা দরকার।

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

পুশ পরিষেবা থেকে প্রতিক্রিয়া

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

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

এইচটিটিপি স্থিতি কোডগুলি সম্পর্কে আরও তথ্যের জন্য আপনি ওয়েব পুশ স্ট্যান্ডার্ড (আরএফসি 8030) পড়তে পারেন।

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

কোড ল্যাব

,

Matt Gaunt

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

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

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

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

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

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

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

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

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

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

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

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

অনুরোধে একটি শিরোনামে যুক্ত হওয়া "স্বাক্ষরিত তথ্য" হ'ল একটি জেএসএন ওয়েব টোকেন।

JSON ওয়েব টোকেন

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

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

Https://jwt.io/ এ প্রচুর লাইব্রেরি রয়েছে যা আপনার জন্য স্বাক্ষর সম্পাদন করতে পারে এবং আমি আপনাকে যেখানে করতে পারেন সেখানে করার পরামর্শ দিচ্ছি। সম্পূর্ণতার জন্য, আসুন কীভাবে ম্যানুয়ালি স্বাক্ষরিত জেডব্লিউটি তৈরি করতে হয় তা দেখুন।

ওয়েব পুশ এবং স্বাক্ষরিত জেডব্লিউটিএস

একটি স্বাক্ষরিত জেডাব্লুটিটি কেবল একটি স্ট্রিং, যদিও এটি বিন্দুগুলির সাথে যুক্ত তিনটি স্ট্রিং হিসাবে ভাবা যেতে পারে।

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

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

প্রথম স্ট্রিংটি নিজেই জেডব্লিউটি সম্পর্কে তথ্য, এটি নির্দেশ করে যে কোন অ্যালগরিদম স্বাক্ষর তৈরি করতে ব্যবহৃত হয়েছিল।

ওয়েব পুশের জন্য জেডব্লিউটি তথ্যটিতে অবশ্যই নিম্নলিখিত তথ্য থাকতে হবে:

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

দ্বিতীয় স্ট্রিংটি হ'ল জেডব্লিউটি ডেটা। এটি জেডব্লিউটি প্রেরক সম্পর্কে তথ্য সরবরাহ করে, কার জন্য এটি উদ্দেশ্যযুক্ত এবং এটি কতক্ষণ বৈধ।

ওয়েব পুশের জন্য, ডেটাটিতে এই ফর্ম্যাটটি থাকবে:

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

aud মানটি হ'ল "শ্রোতা", অর্থাত্ জেডব্লিউটি কে। ওয়েব পুশের জন্য শ্রোতাদের হ'ল পুশ পরিষেবা, তাই আমরা এটি পুশ পরিষেবার উত্সে সেট করি।

exp মানটি হ'ল জেডাব্লুটিটির মেয়াদোত্তীর্ণ, এটি স্নোপারদের যদি কোনও জেডব্লিউটি পুনরায় ব্যবহার করতে সক্ষম হতে বাধা দেয় তবে তারা যদি এটি বাধা দেয়। মেয়াদোত্তীর্ণতা সেকেন্ডের মধ্যে একটি টাইমস্ট্যাম্প এবং আর 24 ঘন্টা হতে হবে না।

নোড.জেএসে মেয়াদোত্তীর্ণতা ব্যবহার করে সেট করা হয়েছে:

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

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

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

জেডব্লিউটি তথ্যের মতোই, জেডব্লিউটি ডেটা একটি ইউআরএল নিরাপদ বেস 64 স্ট্রিং হিসাবে এনকোড করা হয়েছে।

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

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

// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');

// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;

// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
  kty: 'EC',
  crv: 'P-256',
  x: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(1, 33)),
  y: window.uint8ArrayToBase64Url(
    applicationServerKeys.publicKey.subarray(33, 65)),
  d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};

// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
  name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
  return crypto.subtle.sign({
    name: 'ECDSA',
    hash: {
      name: 'SHA-256',
    },
  }, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
  console.log('Signature: ', signature);
});

একটি পুশ পরিষেবা স্বাক্ষরটি ডিক্রিপ্ট করতে পাবলিক অ্যাপ্লিকেশন সার্ভার কী ব্যবহার করে একটি জেডব্লিউটি বৈধ করতে পারে এবং ডিক্রিপ্ট করা স্ট্রিংটি "স্বাক্ষরযুক্ত টোকেন" (অর্থাত্ জেডব্লিউটি -র প্রথম দুটি স্ট্রিং) এর সমান কিনা তা নিশ্চিত করতে পারে।

স্বাক্ষরিত জেডব্লিউটি (অর্থাত্ তিনটি স্ট্রিং ডট দ্বারা যোগদান করেছে), ওয়েব পুশ সার্ভিসে WebPush প্রস্তাবিত Authorization শিরোনাম হিসাবে প্রেরণ করা হয়েছে, এর মতো:

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

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

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

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

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

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

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

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

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

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

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

ইসিডিএইচ: উপবৃত্তাকার বক্ররেখা ডিফি-হেলম্যান কী এক্সচেঞ্জ

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

ইসিডিএইচ দিয়ে উত্পন্ন কীগুলির দরকারী সম্পত্তি হ'ল অ্যালিস গোপন মান 'এক্স' তৈরি করতে তার ব্যক্তিগত কী এবং ববের পাবলিক কী ব্যবহার করতে পারে। ববও একই রকম করতে পারেন, তার ব্যক্তিগত কী এবং অ্যালিসের সর্বজনীন কীটি স্বাধীনভাবে একই মান 'এক্স' তৈরি করতে। এটি 'এক্স' কে একটি ভাগ করা গোপনীয় করে তোলে এবং অ্যালিস এবং ববকে কেবল তাদের সর্বজনীন কী ভাগ করে নিতে হয়েছিল। এখন বব এবং অ্যালিস তাদের মধ্যে এনক্রিপ্ট এবং ডিক্রিপ্ট করতে 'এক্স' ব্যবহার করতে পারে।

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

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

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

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

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

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

এইচকেডিএফ: এইচএমএসি ভিত্তিক কী ডেরাইভেশন ফাংশন

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

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

মূলত, এইচকেডিএফ এমন ইনপুট নেবে যা নির্দিষ্ট সুরক্ষিত নয় এবং এটিকে আরও সুরক্ষিত করে তোলে।

এই এনক্রিপশনটি সংজ্ঞায়িত করার জন্য আমাদের হ্যাশ অ্যালগরিদম হিসাবে SHA-256 এর ব্যবহার প্রয়োজন এবং ওয়েব পুশে এইচকেডিএফের ফলাফলের কীগুলি 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);
}

এই উদাহরণ কোডের জন্য ম্যাট স্কেলের নিবন্ধে টুপি টিপ।

এটি আলগাভাবে ইসিডিএইচ এবং এইচকেডিএফকে কভার করে।

ইসিডিএইচ পাবলিক কীগুলি ভাগ করে নেওয়ার এবং একটি ভাগ করা গোপন তৈরি করার একটি সুরক্ষিত উপায়। এইচকেডিএফ হ'ল অনিরাপদ উপাদান গ্রহণ এবং এটি সুরক্ষিত করার একটি উপায়।

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

ইনপুট

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

  1. পে -লোড নিজেই।
  2. PushSubscription থেকে auth গোপন।
  3. PushSubscription থেকে p256dh কী।

আমরা auth এবং p256dh মানগুলি PushSubscription থেকে পুনরুদ্ধার করা দেখেছি তবে দ্রুত অনুস্মারকটির জন্য, একটি সাবস্ক্রিপশন দেওয়া আমাদের এই মানগুলির প্রয়োজন হবে:

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

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

auth মানটি একটি গোপন হিসাবে বিবেচনা করা উচিত এবং আপনার আবেদনের বাইরে ভাগ করা উচিত নয়।

p256dh কীটি একটি পাবলিক কী, এটি কখনও কখনও ক্লায়েন্ট পাবলিক কী হিসাবে উল্লেখ করা হয়। এখানে আমরা p256dh কে সাবস্ক্রিপশন পাবলিক কী হিসাবে উল্লেখ করব। সাবস্ক্রিপশন পাবলিক কী ব্রাউজার দ্বারা উত্পাদিত হয়। ব্রাউজারটি প্রাইভেট কীটি গোপন রাখবে এবং এটি পে -লোড ডিক্রিপ্ট করার জন্য ব্যবহার করবে।

এই তিনটি মান, auth , p256dh এবং payload ইনপুট হিসাবে প্রয়োজন এবং এনক্রিপশন প্রক্রিয়াটির ফলাফলটি হ'ল এনক্রিপ্ট করা পেডলোড, একটি লবণের মান এবং কেবল ডেটা এনক্রিপ্ট করার জন্য ব্যবহৃত একটি পাবলিক কী।

লবণ

লবণটি এলোমেলো ডেটা 16 বাইট হতে হবে। নোডজেগুলিতে, আমরা একটি লবণ তৈরি করতে নিম্নলিখিতগুলি করব:

const salt = crypto.randomBytes(16);

সরকারী / ব্যক্তিগত কী

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

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

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

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

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

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

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

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

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

সিউডো এলোমেলো কী

সিউডো র‌্যান্ডম কী (পিআরকে) হ'ল পুশ সাবস্ক্রিপশন এর আথ সিক্রেট এবং আমরা সবেমাত্র তৈরি করা ভাগ করা গোপনীয়তার সংমিশ্রণ।

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

আপনি কী ভাবছেন যে Content-Encoding: auth\0 স্ট্রিং এর জন্য। সংক্ষেপে, এটির কোনও স্পষ্ট উদ্দেশ্য নেই, যদিও ব্রাউজারগুলি আগত বার্তা ডিক্রিপ্ট করতে পারে এবং প্রত্যাশিত সামগ্রী-এনকোডিংয়ের সন্ধান করতে পারে। \0 বাফারের শেষ থেকে 0 এর মান সহ একটি বাইট যুক্ত করে। ব্রাউজারগুলি বার্তাটি ডিক্রিপ্ট করে প্রত্যাশিত যে বিষয়বস্তু এনকোডিংয়ের জন্য এতগুলি বাইট আশা করবে, মান 0 সহ একটি বাইট অনুসরণ করবে, তারপরে এনক্রিপ্ট করা ডেটা অনুসরণ করবে।

আমাদের সিউডো এলোমেলো কীটি কেবল এইচকেডিএফ (যেমন এটি ক্রিপ্টোগ্রাফিকভাবে আরও শক্তিশালী করে তোলে) এর মাধ্যমে অ্যাথ, শেয়ারড সিক্রেট এবং এনকোডিং তথ্যের একটি অংশ চালাচ্ছে।

প্রসঙ্গ

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

const keyLabel = new Buffer('P-256\0', 'utf8');

// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');

const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;

const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;

const contextBuffer = Buffer.concat([
  keyLabel,
  subscriptionPubKeyLength.buffer,
  subscriptionPubKey,
  localPublicKeyLength.buffer,
  localPublicKey,
]);

The final context buffer is a label, the number of bytes in the subscription public key, followed by the key itself, then the number of bytes local public key, followed by the key itself.

With this context value we can use it in the creation of a nonce and a content encryption key (CEK).

Content encryption key and nonce

A nonce is a value that prevents replay attacks as it should only be used once.

The content encryption key (CEK) is the key that will ultimately be used to encrypt our payload.

First we need to create the bytes of data for the nonce and CEK, which is simply a content encoding string followed by the context buffer we just calculated:

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

This information is run through HKDF combining the salt and PRK with the nonceInfo and 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);

This gives us our nonce and content encryption key.

Perform the encryption

Now that we have our content encryption key, we can encrypt the payload.

We create an AES128 cipher using the content encryption key as the key and the nonce is an initialization vector.

In Node this is done like so:

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

Before we encrypt our payload, we need to define how much padding we wish to add to the front of the payload. The reason we'd want to add padding is that it prevents the risk of eavesdroppers being able to determine "types" of messages based on the payload size.

You must add two bytes of padding to indicate the length of any additional padding.

For example, if you added no padding, you'd have two bytes with value 0, ie no padding exists, after these two bytes you'll be reading the payload. If you added 5 bytes of padding, the first two bytes will have a value of 5, so the consumer will then read an additional five bytes and then start reading the payload.

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

We then run our padding and payload through this cipher.

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

We now have our encrypted payload. ইয়া!

All that remains is to determine how this payload is sent to the push service.

Encrypted payload headers & body

To send this encrypted payload to the push service we need to define a few different headers in our POST request.

Encryption header

The 'Encryption' header must contain the salt used for encrypting the payload.

The 16 byte salt should be base64 URL safe encoded and added to the Encryption header, like so:

Encryption: salt=[URL Safe Base64 Encoded Salt]

Crypto-Key header

We saw that the Crypto-Key header is used under the 'Application Server Keys' section to contain the public application server key.

This header is also used to share the local public key used to encrypt the payload.

The resulting header looks like this:

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

Content type, length & encoding headers

The Content-Length header is the number of bytes in the encrypted payload. 'Content-Type' and 'Content-Encoding' headers are fixed values. এটি নীচে দেখানো হয়েছে।

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

With these headers set, we need to send the encrypted payload as the body of our request. Notice that the Content-Type is set to application/octet-stream . This is because the encrypted payload must be sent as a stream of bytes.

In NodeJS we would do this like so:

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

More headers?

We've covered the headers used for JWT / Application Server Keys (ie how to identify the application with the push service) and we've covered the headers used to send an encrypted payload.

There are additional headers that push services use to alter the behavior of sent messages. Some of these headers are required, while others are optional.

TTL header

প্রয়োজন

TTL (or time to live) is an integer specifying the number of seconds you want your push message to live on the push service before it's delivered. When the TTL expires, the message will be removed from the push service queue and it won't be delivered.

TTL: [Time to live in seconds]

If you set a TTL of zero, the push service will attempt to deliver the message immediately, but if the device can't be reached, your message will be immediately dropped from the push service queue.

Technically a push service can reduce the TTL of a push message if it wants. You can tell if this has happened by examining the TTL header in the response from a push service.

বিষয়

ঐচ্ছিক

Topics are strings that can be used to replace a pending messages with a new message if they have matching topic names.

This is useful in scenarios where multiple messages are sent while a device is offline, and you really only want a user to see the latest message when the device is turned on.

জরুরী

ঐচ্ছিক

Urgency indicates to the push service how important a message is to the user. This can be used by the push service to help conserve the battery life of a user's device by only waking up for important messages when battery is low.

The header value is defined as shown below. The default value is normal .

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

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

If you have further questions about how this all works you can always see how libraries trigger push messages on the web-push-libs org .

Once you have an encrypted payload, and the headers above, you just need to make a POST request to the endpoint in a PushSubscription .

So what do we do with the response to this POST request?

Response from push service

Once you've made a request to a push service, you need to check the status code of the response as that'll tell you whether the request was successful or not.

স্ট্যাটাস কোড বর্ণনা
201 তৈরি হয়েছে। The request to send a push message was received and accepted.
429 অনেক অনুরোধ. Meaning your application server has reached a rate limit with a push service. The push service should include a 'Retry-After' header to indicate how long before another request can be made.
400 Invalid request. This generally means one of your headers is invalid or improperly formatted.
404 পাওয়া যায়নি। This is an indication that the subscription is expired and can't be used. In this case you should delete the `PushSubscription` and wait for the client to resubscribe the user.
410 চলে গেছে। The subscription is no longer valid and should be removed from application server. This can be reproduced by calling `unsubscribe()` on a `PushSubscription`.
413 Payload size too large. The minimum size payload a push service must support is 4096 bytes (or 4kb).

You can also read the Web Push standard (RFC8030) for more information about the HTTP status codes.

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

কোড ল্যাব