پروتکل فشار وب

ما دیده‌ایم که چگونه می‌توان از کتابخانه برای راه‌اندازی پیام‌های فشار استفاده کرد، اما این کتابخانه‌ها دقیقاً چه می‌کنند؟

خوب، آنها درخواست های شبکه را ارائه می کنند در حالی که مطمئن می شوند چنین درخواست هایی فرمت مناسبی دارند. مشخصاتی که این درخواست شبکه را تعریف می کند، پروتکل Web Push است.

نمودار ارسال پیام فشار از سرور شما به سرویس پوش

این بخش به تشریح این موضوع می‌پردازد که چگونه سرور می‌تواند خود را با کلیدهای سرور برنامه شناسایی کند و چگونه محموله رمزگذاری شده و داده‌های مرتبط ارسال می‌شود.

این یک جنبه زیبا از فشار وب نیست و من در رمزگذاری متخصص نیستم، اما بیایید هر بخش را بررسی کنیم زیرا دانستن اینکه این کتابخانه ها در زیر پوشش چه می کنند مفید است.

کلیدهای سرور برنامه

هنگامی که یک کاربر را مشترک می کنیم، یک applicationServerKey را ارسال می کنیم. این کلید به سرویس فشار داده می‌شود و برای بررسی اینکه برنامه‌ای که کاربر را مشترک کرده است، برنامه‌ای است که پیام‌های فشار را نیز راه‌اندازی می‌کند، استفاده می‌شود.

هنگامی که ما یک پیام فشار را راه اندازی می کنیم، مجموعه ای از هدرها وجود دارد که ارسال می کنیم که به سرویس فشار اجازه می دهد تا برنامه را تأیید اعتبار کند. (این توسط مشخصات VAPID تعریف شده است.)

همه اینها در واقع به چه معناست و دقیقاً چه اتفاقی می افتد؟ خوب این مراحل برای احراز هویت سرور برنامه انجام شده است:

  1. سرور برنامه برخی از اطلاعات JSON را با کلید برنامه خصوصی خود امضا می کند.
  2. این اطلاعات امضا شده به عنوان هدر در یک درخواست POST به سرویس فشار ارسال می شود.
  3. سرویس push از کلید عمومی ذخیره شده ای که از pushManager.subscribe() دریافت کرده استفاده می کند تا بررسی کند که اطلاعات دریافتی توسط کلید خصوصی مربوط به کلید عمومی امضا شده است. به یاد داشته باشید : کلید عمومی applicationServerKey است که در تماس اشتراک ارسال می شود.
  4. اگر اطلاعات امضا شده معتبر باشد، سرویس فشار پیام فشار را برای کاربر ارسال می کند.

نمونه ای از این جریان اطلاعات در زیر آمده است. (برای نشان دادن کلیدهای عمومی و خصوصی به افسانه پایین سمت چپ توجه کنید.)

تصویری از نحوه استفاده از کلید سرور برنامه خصوصی هنگام ارسال پیام

"اطلاعات امضا شده" اضافه شده به هدر در درخواست، یک توکن وب JSON است.

توکن وب JSON

توکن وب JSON (یا به اختصار JWT) راهی برای ارسال پیام به شخص ثالث است به طوری که گیرنده بتواند تأیید کند که چه کسی آن را ارسال کرده است.

هنگامی که شخص ثالثی پیامی را دریافت می کند، باید کلید عمومی فرستنده را دریافت کند و از آن برای تأیید اعتبار امضای JWT استفاده کند. اگر امضا معتبر است، JWT باید با کلید خصوصی منطبق امضا شده باشد، بنابراین باید از فرستنده مورد انتظار باشد.

مجموعه ای از کتابخانه ها در https://jwt.io/ وجود دارد که می توانند امضا را برای شما انجام دهند و من به شما توصیه می کنم تا جایی که می توانید این کار را انجام دهید. برای کامل بودن، بیایید نحوه ایجاد دستی JWT امضا شده را بررسی کنیم.

فشار وب و JWT های امضا شده

یک JWT امضا شده فقط یک رشته است، اگرچه می توان آن را به عنوان سه رشته در نظر گرفت که با نقطه به هم متصل شده اند.

تصویری از رشته ها در یک توکن وب JSON

رشته‌های اول و دوم (اطلاعات JWT و داده‌های JWT) قطعاتی از JSON هستند که با پایه ۶۴ کدگذاری شده‌اند، به این معنی که برای عموم قابل خواندن است.

رشته اول اطلاعاتی در مورد خود JWT است که نشان می دهد از کدام الگوریتم برای ایجاد امضا استفاده شده است.

اطلاعات JWT برای فشار وب باید حاوی اطلاعات زیر باشد:

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

رشته دوم JWT Data است. این اطلاعات درباره فرستنده JWT، برای چه کسی در نظر گرفته شده است و مدت اعتبار آن را ارائه می دهد.

برای فشار وب، داده ها این فرمت را دارند:

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

مقدار aud همان "مخاطب" است، یعنی JWT برای چه کسی است. برای فشار وب، مخاطب سرویس فشار است، بنابراین ما آن را روی مبدا سرویس فشار قرار می دهیم.

مقدار exp انقضای JWT است، این امر مانع از آن می شود که جاسوسان بتوانند در صورت رهگیری از JWT مجدداً از آن استفاده کنند. انقضا یک مهر زمانی بر حسب ثانیه است و نباید دیگر 24 ساعت باشد.

در Node.js انقضا با استفاده از:

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

12 ساعت به جای 24 ساعت طول می کشد تا از هر گونه مشکلی در مورد اختلاف ساعت بین برنامه ارسال و سرویس فشار جلوگیری شود.

در نهایت، مقدار sub باید یک URL یا یک آدرس ایمیل mailto باشد. این به این دلیل است که اگر یک سرویس فشار نیاز به تماس با فرستنده داشت، بتواند اطلاعات تماس را از JWT پیدا کند. (به همین دلیل است که کتابخانه web-push به یک آدرس ایمیل نیاز داشت).

درست مانند اطلاعات JWT، داده های JWT به عنوان یک رشته URL ایمن base64 کدگذاری می شود.

رشته سوم، امضا، نتیجه گرفتن دو رشته اول (اطلاعات JWT و JWT Data)، پیوستن آنها با یک کاراکتر نقطه است که ما آن را "نشانه بدون علامت" می نامیم، و آن را امضا می کنیم.

فرآیند امضا نیاز به رمزگذاری "توکن بدون امضا" با استفاده از ES256 دارد. طبق مشخصات JWT ، ES256 مخفف عبارت «ECDSA با استفاده از منحنی P-256 و الگوریتم هش SHA-256» است. با استفاده از رمزنگاری وب، می توانید امضا را مانند زیر ایجاد کنید:

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

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

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

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

یک سرویس فشار می‌تواند یک JWT را با استفاده از کلید سرور برنامه عمومی برای رمزگشایی امضا تأیید کند و مطمئن شود که رشته رمزگشایی شده همان «توکن بدون علامت» (یعنی دو رشته اول در JWT) است.

JWT امضا شده (یعنی هر سه رشته که با نقطه به هم وصل شده اند)، به عنوان هدر Authorization با WebPush از پیش اضافه شده به سرویس فشار وب ارسال می شود، مانند:

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

پروتکل Web Push همچنین بیان می‌کند که کلید سرور برنامه عمومی باید در هدر Crypto-Key به‌عنوان یک رشته کدگذاری شده با URL ایمن base64 با p256ecdsa= ارسال شود.

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

رمزگذاری بار

در ادامه بیایید ببینیم چگونه می‌توانیم یک محموله را با یک پیام فشار ارسال کنیم تا زمانی که برنامه وب ما یک پیام فشار دریافت می‌کند، بتواند به داده‌هایی که دریافت می‌کند دسترسی داشته باشد.

یک سوال متداول که از هر کسی که از سرویس‌های فشار دیگری استفاده کرده است مطرح می‌شود این است که چرا بار فشار وب نیاز به رمزگذاری دارد؟ با برنامه های بومی، پیام های فشاری می توانند داده ها را به صورت متن ساده ارسال کنند.

بخشی از زیبایی وب فشار در این است که از آنجایی که همه سرویس‌های پوش از یک API (پروتکل فشار وب) استفاده می‌کنند، توسعه‌دهندگان نیازی به اهمیت ندارند که سرویس پوش کیست. ما می توانیم درخواستی را با فرمت مناسب ارائه دهیم و انتظار داشته باشیم که یک پیام فشار ارسال شود. نقطه ضعف این است که توسعه دهندگان می توانند پیام هایی را به یک سرویس فشاری که قابل اعتماد نیستند ارسال کنند. با رمزگذاری محموله، یک سرویس فشار نمی تواند داده های ارسال شده را بخواند. فقط مرورگر می تواند اطلاعات را رمزگشایی کند. این از داده های کاربر محافظت می کند.

رمزگذاری محموله در مشخصات رمزگذاری پیام تعریف شده است.

قبل از اینکه به مراحل خاص برای رمزگذاری محموله پیام‌های فشاری نگاه کنیم، باید تکنیک‌هایی را که در طول فرآیند رمزگذاری استفاده می‌شوند، پوشش دهیم. (نکته کلاه عظیم به Mat Scales برای مقاله عالی او در مورد رمزگذاری فشاری.)

ECDH و HKDF

هر دو ECDH و HKDF در سراسر فرآیند رمزگذاری استفاده می شوند و مزایایی را برای رمزگذاری اطلاعات ارائه می دهند.

ECDH: مبادله کلید دیفی-هلمن منحنی بیضوی

تصور کنید دو نفر دارید که می خواهند اطلاعاتی را به اشتراک بگذارند، آلیس و باب. هر دو آلیس و باب کلیدهای عمومی و خصوصی خود را دارند. آلیس و باب کلیدهای عمومی خود را با یکدیگر به اشتراک می گذارند.

ویژگی مفید کلیدهای تولید شده با ECDH این است که آلیس می تواند از کلید خصوصی خود و کلید عمومی باب برای ایجاد مقدار مخفی "X" استفاده کند. باب می‌تواند همین کار را انجام دهد، کلید خصوصی خود و کلید عمومی آلیس را برای ایجاد مستقل همان مقدار "X" استفاده کند. این باعث می شود «X» یک راز مشترک باشد و آلیس و باب فقط باید کلید عمومی خود را به اشتراک بگذارند. اکنون باب و آلیس می توانند از X برای رمزگذاری و رمزگشایی پیام های بین خود استفاده کنند.

ECDH، تا جایی که من می‌دانم، ویژگی‌های منحنی‌ها را تعریف می‌کند که به این «ویژگی» ایجاد یک راز مشترک «X» اجازه می‌دهد.

این یک توضیح سطح بالایی از ECDH است، اگر می‌خواهید بیشتر بدانید ، توصیه می‌کنم این ویدیو را ببینید .

از نظر کد؛ اکثر زبان ها / پلتفرم ها دارای کتابخانه هایی هستند تا تولید این کلیدها را آسان کنند.

در node کارهای زیر را انجام می دهیم:

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

نکته کلاه به مقاله Mat Scale برای این کد مثال .

این به طور آزاد ECDH و HKDF را پوشش می دهد.

ECDH راهی امن برای اشتراک گذاری کلیدهای عمومی و ایجاد یک راز مشترک. HKDF راهی برای گرفتن مواد ناامن و ایمن کردن آن است.

این در هنگام رمزگذاری محموله ما استفاده خواهد شد. بعد بیایید ببینیم چه چیزی به عنوان ورودی می گیریم و چگونه رمزگذاری می شود.

ورودی ها

هنگامی که می خواهیم یک پیام فشار برای کاربر با بار ارسال کنیم، به سه ورودی نیاز داریم:

  1. خود محموله.
  2. راز auth از PushSubscription .
  3. کلید p256dh از PushSubscription .

ما مقادیر auth و p256dh را دیده‌ایم که از PushSubscription بازیابی می‌شوند، اما برای یادآوری سریع، با توجه به اشتراک، به این مقادیر نیاز داریم:

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

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

auth اعتبار باید به عنوان یک راز تلقی شود و خارج از برنامه شما به اشتراک گذاشته نشود.

کلید p256dh یک کلید عمومی است که گاهی اوقات به آن کلید عمومی مشتری نیز گفته می شود. در اینجا ما به p256dh به عنوان کلید عمومی اشتراک اشاره خواهیم کرد. کلید عمومی اشتراک توسط مرورگر تولید می شود. مرورگر کلید خصوصی را مخفی نگه می دارد و از آن برای رمزگشایی محموله استفاده می کند.

این سه مقدار، auth ، p256dh و payload به عنوان ورودی مورد نیاز هستند و نتیجه فرآیند رمزگذاری، محموله رمزگذاری شده، یک مقدار نمک و یک کلید عمومی است که فقط برای رمزگذاری داده ها استفاده می شود.

نمک

نمک باید 16 بایت داده تصادفی باشد. در NodeJS، برای ایجاد نمک، کارهای زیر را انجام می دهیم:

const salt = crypto.randomBytes(16);

کلیدهای عمومی / خصوصی

کلیدهای عمومی و خصوصی باید با استفاده از یک منحنی بیضوی P-256 تولید شوند که در Node این کار را انجام می دهیم:

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) استفاده می شود.

کلید تصادفی شبه

کلید تصادفی شبه (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 و به دنبال آن داده‌های رمزگذاری شده انتظار داشته باشند.

کلید تصادفی شبه ما به سادگی از طریق HKDF از طریق 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) استفاده کنیم.

کلید رمزگذاری محتوا و غیره

Nonce مقداری است که از حملات تکراری جلوگیری می کند زیرا فقط یک بار باید استفاده شود.

کلید رمزگذاری محتوا (CEK) کلیدی است که در نهایت برای رمزگذاری بار ما استفاده می شود.

ابتدا باید بایت های داده را برای nonce و 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);

این کلید رمزگذاری nonce و محتوا را به ما می دهد.

رمزگذاری را انجام دهید

اکنون که کلید رمزگذاری محتوای خود را داریم، می‌توانیم محموله را رمزگذاری کنیم.

ما یک رمز AES128 با استفاده از کلید رمزگذاری محتوا به عنوان کلید ایجاد می کنیم و nonce یک بردار اولیه است.

در Node این کار به این صورت انجام می شود:

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

قبل از اینکه محموله خود را رمزگذاری کنیم، باید مشخص کنیم که چه مقدار لایه را می‌خواهیم به جلوی محموله اضافه کنیم. دلیل اینکه ما می‌خواهیم بالشتک اضافه کنیم این است که از خطر استراق سمع‌ها جلوگیری می‌کند تا بتوانند «انواع» پیام‌ها را بر اساس اندازه بار تعیین کنند.

برای نشان دادن طول هر بالشتک اضافی باید دو بایت بالشتک اضافه کنید.

به عنوان مثال، اگر هیچ padding اضافه نکنید، دو بایت با مقدار 0 خواهید داشت، یعنی هیچ بالشتکی وجود ندارد، پس از این دو بایت، شما در حال خواندن payload خواهید بود. اگر 5 بایت padding اضافه کنید، دو بایت اول دارای ارزش 5 خواهند بود، بنابراین مصرف کننده پنج بایت اضافی را می خواند و سپس شروع به خواندن محموله می کند.

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

سپس padding و payload خود را از طریق این رمز اجرا می کنیم.

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 بایتی باید با URL پایه 64 کدگذاری شده باشد و به هدر Encryption اضافه شود، مانند:

Encryption: salt=[URL Safe Base64 Encoded Salt]

هدر Crypto-Key

دیدیم که هدر 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-Type» و «Content-Encoding» مقادیر ثابتی هستند. این در زیر نشان داده شده است.

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

با تنظیم این هدرها، باید پیلود رمزگذاری شده را به عنوان متن درخواست خود ارسال کنیم. توجه داشته باشید که Content-Type روی application/octet-stream تنظیم شده است. این به این دلیل است که محموله رمزگذاری شده باید به صورت جریانی از بایت ها ارسال شود.

در NodeJS این کار را به این صورت انجام می دهیم:

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

هدر بیشتر؟

ما سرصفحه‌های مورد استفاده برای کلیدهای JWT / Application Server (یعنی نحوه شناسایی برنامه با سرویس فشار) را پوشش داده‌ایم و سرصفحه‌های مورد استفاده برای ارسال یک محموله رمزگذاری شده را پوشش داده‌ایم.

هدرهای دیگری وجود دارد که سرویس‌ها را برای تغییر رفتار پیام‌های ارسالی استفاده می‌کنند. برخی از این سرصفحه ها مورد نیاز هستند، در حالی که برخی دیگر اختیاری هستند.

هدر TTL

مورد نیاز

TTL (یا زمان زنده بودن) یک عدد صحیح است که تعداد ثانیه هایی را که می خواهید پیام فشار شما قبل از تحویل در سرویس فشار پخش شود را مشخص می کند. هنگامی که TTL منقضی می شود، پیام از صف سرویس فشار حذف می شود و تحویل داده نمی شود.

TTL: [Time to live in seconds]

اگر TTL را صفر تنظیم کنید، سرویس فشار سعی می کند پیام را فوراً تحویل دهد، اما اگر دسترسی به دستگاه امکان پذیر نباشد، پیام شما بلافاصله از صف سرویس فشار حذف می شود.

از نظر فنی، یک سرویس فشار در صورت تمایل می تواند TTL پیام فشار را کاهش دهد. شما می توانید با بررسی هدر TTL در پاسخ یک سرویس فشار متوجه شوید که این اتفاق افتاده است یا خیر.

موضوع

اختیاری

موضوعات رشته‌هایی هستند که می‌توانند برای جایگزینی پیام‌های معلق با یک پیام جدید در صورت داشتن نام موضوع منطبق استفاده شوند.

این در سناریوهایی مفید است که چندین پیام در حالی که دستگاهی آفلاین است ارسال می‌شود، و شما واقعاً می‌خواهید که کاربر فقط وقتی دستگاه روشن است آخرین پیام را ببیند.

فوریت

اختیاری

فوریت به سرویس فشار نشان می دهد که یک پیام چقدر برای کاربر مهم است. این می تواند توسط سرویس فشار برای کمک به حفظ عمر باتری دستگاه کاربر با بیدار شدن برای پیام های مهم زمانی که باتری کم است استفاده کند.

مقدار هدر مطابق شکل زیر تعریف می شود. مقدار پیش فرض normal است.

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

همه چیز با هم

اگر سؤالات بیشتری در مورد نحوه کارکرد اینها دارید، همیشه می‌توانید ببینید که چگونه کتابخانه‌ها پیام‌های فشاری را در web-push-libs org راه‌اندازی می‌کنند.

هنگامی که یک بار رمزگذاری شده و هدرهای بالا دارید، فقط باید یک درخواست POST به endpoint در یک PushSubscription ارسال کنید.

پس با پاسخ به این درخواست POST چه کنیم؟

پاسخ از سرویس فشار

هنگامی که درخواستی را به یک سرویس فشار دادید، باید کد وضعیت پاسخ را بررسی کنید زیرا به شما می گوید که آیا درخواست موفقیت آمیز بوده است یا خیر.

کد وضعیت توضیحات
201 ایجاد شد. درخواست ارسال پیام فشار دریافت و پذیرفته شد.
429 درخواست های خیلی زیاد به این معنی که سرور برنامه شما با یک سرویس فشار به محدودیت نرخ رسیده است. سرویس فشار باید شامل یک سرصفحه "Retry-After" باشد تا نشان دهد چه مدت قبل از درخواست دیگری می توان انجام داد.
400 درخواست نامعتبر این به طور کلی به این معنی است که یکی از سرصفحه‌های شما نامعتبر است یا قالب بندی نامناسبی دارد.
404 یافت نشد. این نشان می دهد که اشتراک منقضی شده است و نمی توان از آن استفاده کرد. در این صورت باید «PushSubscription» را حذف کنید و منتظر بمانید تا مشتری مجدداً کاربر را اشتراک کند.
410 رفته اشتراک دیگر معتبر نیست و باید از سرور برنامه حذف شود. این را می توان با فراخوانی 'unsubscribe()' در 'PushSubscription' بازتولید کرد.
413 اندازه بار بسیار بزرگ است. حداقل اندازه باری که یک سرویس فشار باید پشتیبانی کند 4096 بایت (یا 4 کیلوبایت) است.

همچنین می توانید استاندارد Web Push (RFC8030) را برای اطلاعات بیشتر در مورد کدهای وضعیت HTTP مطالعه کنید.

بعد کجا بریم

آزمایشگاه های کد