ادعاءات انتهاء الصلاحية وتاريخ الإصدار في JWT: كيف تُشغّل طوابع يونكس الزمنية مصادقة التوكن

مخطط حمولة JWT يُظهر مطالبات انتهاء الصلاحية exp وiat كطوابع زمنية Unix

في كل مرة يسجّل فيها مستخدم دخوله إلى تطبيق SaaS، يُولَّد رمز مصادقة ويُرسَل إلى المتصفح أو التطبيق المحمول. هذا الرمز يحمل في داخله ساعة صامتة. حقل انتهاء صلاحية JWT المخزَّن كطابع زمني Unix بسيط، يخبر كل خادم يقرأه متى يجب التوقف عن الوثوق بهذه البيانات. خطأ في هذا الطابع الزمني ولو بثوانٍ معدودة قد يُقفل المستخدمين مبكرًا، أو ما هو أسوأ، يمنحهم جلسة صالحة لفترة أطول بكثير مما تسمح به سياسة الأمان. فهم كيفية عمل claims في JSON web token - وتحديدًا exp و iat - من أهم الأشياء العملية التي يمكن لأي فريق SaaS فعلها لتعزيز المصادقة دون إضافة تعقيد للمستخدم.

أبرز النقاط:

  • الـ claims exp و iat في JWT JSON web token هي دائمًا طوابع زمنية Unix مقاسة بالثواني الكاملة، وليس بالميلي ثانية.
  • فارق التوقيت بين الخوادم (clock skew) قد يُبطل الرموز بصمت أو يمدد الجلسات خارج النطاق المسموح به - والحل المعتمد في الصناعة هو نافذة تسامح مدتها 60 ثانية.
  • ضبط انتهاء صلاحية الرمز لفترة طويلة جدًا يرفع مخاطر الاختراق، وضبطه لفترة قصيرة جدًا يُفسد تجربة المستخدم. معادلة واضحة تساعدك في اختيار القيمة الصحيحة.
  • يمكنك فك تشفير والتحقق من أي طابع زمني في JSON web token بالثواني باستخدام محوّل Unix epoch، دون الحاجة لأي مكتبة.

ما هو JWT ولماذا تُخزَّن الطوابع الزمنية بداخله

JSON web token هو بيانات اعتماد مضغوطة وآمنة للاستخدام في URL، مكوّنة من ثلاثة أجزاء مُشفَّرة بـ Base64 مفصولة بنقاط: header وpayload وsignature. الـ payload هو المكان الذي تعيش فيه البيانات الحساسة للوقت. ولأن JWT عديمة الحالة (stateless)، فإن الخادم لا يبحث عن جلسة في قاعدة البيانات للتحقق منها. بدلًا من ذلك، يقرأ الـ claims المضمّنة في الـ payload ويثق بالتوقيع التشفيري. هذا التصميم سريع وقابل للتوسع، لكنه يخلق قيدًا حقيقيًا: بمجرد إصدار الرمز، لا يستطيع الخادم "حذفه". الطريقة الوحيدة الموثوقة لتحديد مدة صلاحيته هي تضمين طابع زمني لانتهاء الصلاحية مباشرةً في الرمز نفسه.

لهذا السبب يُعدّ الوقت - وتحديدًا توقيت Unix epoch - محوريًا في أمان JWT. تُلزم مواصفة RFC 7519 التي تُعرّف JWT بأن تُعبَّر الـ claims المرتبطة بالوقت كقيم "NumericDate"، وهي ببساطة عدد الثواني منذ 1 يناير 1970 UTC. لا مناطق زمنية. لا تنسيق محلي. فقط عدد صحيح يمكن لأي خادم في أي مكان في العالم مقارنته بساعته الخاصة.

بنية JWT تُظهر header وpayload وsignature مع الـ claims: exp و iat

شرح الـ claims: exp و iat

تُعرّف مواصفة JWT عدة claims مسجّلة. اثنتان منها تهمّان أكثر من غيرهما في إدارة دورة حياة الرمز:

  • exp (وقت انتهاء الصلاحية): الطابع الزمني الذي بعده يجب رفض الرمز. يُلزَم الخوادم برفض أي رمز تكون فيه قيمة الوقت الحالي مساوية أو أكبر من هذه القيمة. هذه هي الآلية الأساسية وراء انتهاء صلاحية JWT.
  • iat (وقت الإصدار): الطابع الزمني الذي أُنشئ فيه الرمز. الـ iat claim غير مُطبَّق افتراضيًا، لكنه مفيد جدًا. يتيح لك حساب عمر الرمز، وتطبيق سياسة الحد الأقصى للعمر بشكل مستقل عن exp ، واكتشاف الرموز التي صدرت في وقت مشبوه بعيد في الماضي.

هناك claim ثالثة هي nbf (Not Before)، وتُحدد أبكر وقت يكون فيه الرمز صالحًا. تُستخدم بشكل أقل شيوعًا لكنها تتبع نفس تنسيق الطابع الزمني Unix.

القيم الثلاث كلها أعداد صحيحة. إذا كان نظامك يُولّدها بالميلي ثانية (خطأ شائع عند استخدام Date.now() في JavaScript)، فإن الرقم الناتج سيكون أكبر بحوالي 1000 مرة من المتوقع. خادم يفحص exp مقارنةً بثانية الـ epoch الحالية سيرى رمزًا يبدو أنه ينتهي في عام 33658 ولن يرفضه أبدًا. هذه مشكلة حقيقية في بيئات الإنتاج أثّرت على فرق SaaS متعددة.

مثال عملي: فك تشفير طوابع زمنية حقيقية من رمز

لنفترض أن خدمة المصادقة لديك تُصدر الـ payload التالية لـ JWT بعد تسجيل دخول مستخدم في الساعة 10:00 صباحًا UTC في 1 يوليو 2025:

{
  "sub": "user_8821",
  "iat": 1751364000,
  "exp": 1751367600,
  "role": "admin"
}

لنفهم ما تعنيه هذه الأرقام:

الـ Claim الطابع الزمني Unix التوقيت المقروء (UTC) المعنى
iat 1751364000 2025-07-01 10:00:00 صدر الرمز في هذه اللحظة
exp 1751367600 2025-07-01 11:00:00 ينتهي الرمز بعد ساعة بالضبط

الفرق هو 3600 ثانية بالضبط، أي ساعة واحدة. أي خادم يستقبل هذا الرمز بعد الساعة 11:00:00 UTC يجب أن يرفضه. للتحقق من ذلك يدويًا، تطرح iat من exp : 1751367600 - 1751364000 = 3600 ثانية. يمكنك التأكد من التوقيت المقروء لأي من هذه القيم فورًا باستخدام محوّل الطابع الزمني Unix، وهو بالضبط نوع الفحص السريع الذي يمنع مشكلة الميلي ثانية مقابل الثانية المذكورة سابقًا.

لمزيد من السياق حول كيفية تخزين هذه القيم في قواعد البيانات، راجع دليلنا حول الطوابع الزمنية Unix في قواعد البيانات، إذ ينطبق نفس تنسيق الأعداد الصحيحة سواء كنت تخزّن exp في رمز أو في جدول سجلات التدقيق.

فارق التوقيت: القاتل الصامت للرموز

إليك قيدًا حقيقيًا يُفاجئ كثيرًا من الفرق. في الأنظمة الموزّعة، خادم المصادقة وخادم API جهازان مختلفان. تتزامن ساعاتهما عبر NTP، لكنهما لا يكونان متطابقَين تمامًا أبدًا. فارق من 30 إلى 90 ثانية شائع في بيئات السحابة.

تخيّل هذا السيناريو: رمز بقيمة exp = 1751367600 يُتحقق منه بواسطة خادم API تُظهر ساعته 1751367610، أي 10 ثوانٍ فقط بعد انتهاء الصلاحية. يُرفض الرمز. يحصل المستخدم على خطأ 401. من منظوره، كان قد سجّل دخوله للتو وتعطّل التطبيق.

الحل المعتمد هو إضافة هامش تسامح لفارق التوقيت إلى منطق التحقق. معظم مكتبات JWT تدعم معامل leeway. قيمة 60 ثانية مقبولة على نطاق واسع ومُشار إليها في مواصفة OpenID Connect Core. اضبطها، ووثّقها، وتأكد من أن كل خدمة في بنيتك تستخدم نفس القيمة.

قاعدة عملية: أضف هامش تسامح مدته 60 ثانية عند التحقق من exp . لا تُضف أي هامش تسامح لفحوصات iat ، لأن ذلك سيسمح بقبول رموز صادرة في المستقبل، وهو مؤشر خطر على هجمات إعادة التشغيل (replay attacks).

اختيار مدة انتهاء الصلاحية المناسبة لمنتج SaaS الخاص بك

تعتمد القيمة الصحيحة لانتهاء صلاحية الرمز على حساسية تطبيقك وسلوك الجلسة المتوقع. إليك إطارًا عمليًا:

  • التطبيقات عالية الحساسية (البنوك، الرعاية الصحية، لوحات تحكم المشرفين): من 15 إلى 30 دقيقة لرموز الوصول. استخدم refresh tokens لتجديد الجلسات بصمت.
  • تطبيقات SaaS العادية (إدارة المشاريع، CRM، التحليلات): من 1 إلى 8 ساعات. طابق طول جلسة العمل النموذجية.
  • الـ APIs منخفضة المخاطر أو للقراءة فقط: حتى 24 ساعة، لكن اقرنها بتدوير الرمز عند الإجراءات الحساسة.
  • رموز الخدمات من آلة إلى آلة (machine-to-machine): يمكن أن تكون أطول (أيام أو أسابيع)، لكن يجب تحديد نطاقها بدقة وتدويرها بجدول زمني.

معادلة مفيدة لحساب قيمة exp عند وقت الإصدار:

// Node.js example
const iat = Math.floor(Date.now() / 1000); // current time in seconds
const ttl = 3600; // time-to-live: 1 hour in seconds
const exp = iat + ttl;

const payload = {
  sub: userId,
  iat: iat,
  exp: exp
};

لاحظ القسمة الصريحة على 1000 لتحويل الميلي ثانية إلى ثوانٍ. هذا السطر الواحد يمنع أكثر أخطاء الطوابع الزمنية شيوعًا في تطبيقات JavaScript.

خطوات عملية لتطبيق انتهاء صلاحية JWT بشكل آمن

إليك قائمة تحقق ملموسة يمكنك تطبيقها على أي نظام مصادقة SaaS اليوم:

  1. أدرج دائمًا كلًا من iat و exp . الـ iat claim اختيارية تقنيًا، لكنها تمنحك سجل تدقيق وتُمكّن تطبيق سياسة الحد الأقصى للعمر بشكل مستقل عن انتهاء الصلاحية.
  2. تحقق من تنسيق الطابع الزمني قبل النشر. افك تشفير الرمز وتأكد أن exp عدد صحيح من 10 أرقام. القيمة المكوّنة من 13 رقمًا تعني أن الميلي ثانية تسلّلت إلى الكود.
  3. اضبط هامش تسامح لفارق التوقيت مدته 60 ثانية في middleware التحقق لديك. طبّقه باتساق على كل خدمة تستهلك رموز المصادقة.
  4. استخدم رموز وصول قصيرة الأمد مع refresh tokens. رمز وصول مدته 15 دقيقة مقرون بـ refresh token مدته 7 أيام أكثر أمانًا بكثير من رمز وصول واحد مدته 7 أيام، لأن الـ refresh token يمكن إلغاؤه من جانب الخادم.
  5. سجّل قيمة iat مع كل طلب مصادَق. يتيح لك ذلك اكتشاف الشذوذات، كاستخدام نفس الرمز من منطقتين جغرافيتين مختلفتين في آنٍ واحد.
  6. اختبر انتهاء الصلاحية في بيئة التطوير بتسريع الوقت. محاكاة القفز بالساعة إلى ما بعد قيمة exp وتأكد أن تطبيقك يتعامل مع الخطأ 401 بشكل سلس بدلًا من التعطل أو الدوران في حلقة.

إذا أردت تحويل قيمة exp خام من رمز إلى تاريخ مقروء، أو التحقق من الطابع الزمني الذي تحتاج ضبطه لمدة معينة، فإن محوّل الـ epoch في صفحتنا الرئيسية يتعامل مع الاتجاهين فورًا دون الحاجة لأي كود.

الخلاصة

انتهاء صلاحية JWT ليس مفهومًا معقدًا، لكنه مجال تُحدث فيه الأخطاء الصغيرة ثغرات أمنية كبيرة. الـ claims exp و iat مجرد أعداد صحيحة - طوابع زمنية Unix تُحسب بالثواني من نقطة الـ epoch. هذه البساطة هي قوتها: كل خادم وكل لغة وكل منصة يمكنها مقارنة رقمين دون أي غموض. المهارة الحقيقية تكمن في تطبيقها بشكل صحيح: اختيار مدة انتهاء صلاحية الرمز المناسبة، والتعامل مع فارق التوقيت بنافذة تسامح، وإيقاف مشكلة الميلي ثانية مقابل الثانية قبل وصولها إلى الإنتاج. ابنِ هذه العادات في pipeline المصادقة لديك وسيكون تطبيق JSON web token لديك آمنًا وقابلًا للصيانة في آنٍ واحد.

أداة تحويل الطابع الزمني Unix للتحقق من انتهاء صلاحية JWT

تحقق من طوابع JWT الزمنية فورًا - دون أي كود

الصق أي طابع زمني Unix من الـ payload في JWT وحوّله إلى تاريخ مقروء بنقرة واحدة. اكتشف أخطاء الميلي ثانية مقابل الثانية قبل أن تصل إلى بيئة الإنتاج.

جرّب أداتنا المجانية ←

exp (وقت انتهاء الصلاحية) هو الطابع الزمني Unix الذي بعده يصبح الرمز غير صالح ويجب رفضه. iat (وقت الإصدار) هو الطابع الزمني الذي أُنشئ فيه الرمز. كلاهما أعداد صحيحة بالثواني. الـ iat claim اختيارية لكنها مفيدة للتدقيق وتطبيق سياسات الحد الأقصى لعمر الرمز.

الطوابع الزمنية Unix أعداد صحيحة مستقلة عن المنطقة الزمنية، مما يجعلها غير قابلة للتأويل المختلف عبر جميع الخوادم والمناطق. سلاسل التاريخ المنسّقة مثل "2025-07-01T10:00:00" يمكن أن تُفسَّر بشكل خاطئ بناءً على إعدادات اللغة أو المنطقة الزمنية. كذلك مقارنة عددين أسرع وأقل عرضة للخطأ من تحليل النصوص في أنظمة المصادقة عالية الأداء.

الرمز طويل الأمد يظل صالحًا حتى لو تعرّض حساب المستخدم للاختراق أو تغيّرت صلاحياته. ولأن JWT عديمة الحالة، لا يستطيع الخادم إلغاءها في منتصف مدة صلاحيتها. إذا سرق مهاجم رمزًا بصلاحية 30 يومًا، فلديه وصول كامل لمدة 30 يومًا. نوافذ انتهاء الصلاحية القصيرة مقرونة بـ refresh tokens تقلّص هذه النافذة الخطرة بشكل كبير.

في JavaScript، استخدم دائمًا Math.floor(Date.now() / 1000) عند توليد قيم iat أو exp . الـ Date.now() الخام يُعيد الميلي ثانية. القسمة على 1000 تحوّلها إلى العدد الصحيح المعتمد على الثواني الذي تتطلبه مواصفة JWT. تحقق من ذلك بالتأكد أن طابعك الزمني مكوّن من 10 أرقام وليس 13 رقمًا.

فارق التوقيت هو الفرق الزمني الصغير بين ساعات النظام في خادمَين ضمن نظام موزّع. حتى فارق 30 ثانية قد يتسبب في رفض رمز صالح إذا كانت ساعة الخادم المتحقق متقدمة قليلًا. الحل المعتمد هو ضبط هامش تسامح مدته 60 ثانية في مكتبة التحقق من JWT لامتصاص الانجراف الطبيعي لـ NTP.