ما هو رمز الويب جيسون (JWT)؟
يستخدم رمز الويب جيسون (JWT) بشكل واسع في التطبيقات الحديثة والمعايير المفتوحة مثل OpenID Connect، لتسهيل المصادقة (Authentication) والتفويض (Authorization). بينما يعد RFC 7519 مرجعًا أساسيًا، إلا أنه قد يصعب فهمه للمبتدئين. في هذه المقالة، سنركز على المفاهيم الأساسية لـ JWT وسنعرضها بلغة بسيطة مع أمثلة.
لماذا نحتاج إلى JWT؟
في الوقت الحالي، من الشائع استخدام JSON لتبادل البيانات بين طرفين. فكر في كائن JSON يمثل مستخدمًا:
{
"sub": "foo",
"name": "John Doe"
}
sub
هو اختصار لـ “subject”، وهو ادعاء معياري (claim) في OpenID Connect لتمثيل معرف المستخدم (معرّف المستخدم).
كيف يمكننا ضمان سلامة هذا الكائن JSON؟ بعبارة أخرى، كيف يمكننا التأكد من أن البيانات لم يتم التلاعب بها أثناء النقل؟ الحل الشائع هو استخدام التوقيعات الرقمية. على سبيل المثال، يمكننا استخدام التشفير بالمفتاح العام : يقوم الخادم بتوقيع كائن JSON بمفتاحه الخاص، ويمكن للعميل التحقق من التوقيع باستخدام المفتاح العام للخادم.
باختصار، يوفر JWT نهجًا قياسيًا لتمثيل كائن JSON وتوقيعه.
يمكن أيضًا استخدام JWT لتشفير كائن JSON، ولكنه ليس محور هذه المقالة.
صيغة JWT
نظرًا لوجود العديد من الخوارزميات لإنشاء التوقيعات الرقمية، فمن الضروري تحديد الخوارزمية المستخدمة لتوقيع JWT. يتم تحقيق ذلك عبر إنشاء كائن JSON:
{
"alg": "HS256",
"typ": "JWT"
}
alg
هو اختصار لـ “algorithm”، وtyp
هو اختصار لـ “type”.
عادةً ما يتم تعيين typ
إلى JWT
بحروف كبيرة. في مثالنا، alg
هو HS256
، الذي يمثل
HMAC-SHA256 (سنوضحها لاحقًا)، ويشير إلى أننا نستخدم هذه الخوارزمية لإنشاء التوقيع.
الآن، لدينا جميع مكونات JWT:
- JSON الرأس: الخوارزمية والنوع
- JSON الحمولة: البيانات الفعلية
- التوقيع: التوقيع الذي يشمل الرأس والحمولة
ومع ذلك، بعض الأحرف مثل المسافات وانقطاعات الخط ليست صديقة للنقل عبر الشبكة. لذلك، يجب أن يتم Base64URL-تشفير الرأس والحمولة. عادةً ما يبدو JWT هكذا:
{{header}}.{{payload}}.{{signature}}
.
يعمل كفاصل.
دعونا نجمع كل شيء لإنشاء JWT:
الرأس
JSON: {"alg":"HS256","typ":"JWT"}
Base64URL تم ترميزه: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
الحمولة
JSON: {"sub":"foo","name":"John Doe"}
Base64URL تم ترميزه: eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ
التوقيع
في HMAC-SHA256، يتم إنشاء التوقيع باستخدام سر:
HMAC-SHA256(base64Url(header) + "." + base64Url(payload), secret)
على سبيل المثال، مع السر some-great-secret
، يكون التوقيع: XM-XSs2Lmp76IcTQ7tVdFcZzN4W_WcoKMNANp925Q9g
.
JWT
الـ JWT النهائي هو:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ.XM-XSs2Lmp76IcTQ7tVdFcZzN4W_WcoKMNANp925Q9g
يمكن لأي طرف يمتلك السر التحقق من صحة هذا الـ JWT.
اختيار خوارزمية التوقيع
كما ذكرنا سابقًا، هناك العديد من الخوارزميات لإنشاء التوقيعات الرقمية. لقد استخدمنا HS256
كمثال، ولكن قد لا تكون قوية بما فيه الكفاية لأن السر يجب أن يُشَارَك بين الأطراف (مثل العميل والخادم).
في السيناريوهات الواقعية، قد تتضمن العملاء تطبيقات عامة مثل تطبيقات React التي لا يمكنها الحفاظ على السر آمنًا. لذا، النهج المفضل يشمل استخدام التشفير بالمفتاح العام (أي التشفير غير المتماثل) لتوقيع JWT. لنبدأ بالخوارزمية الأكثر شيوعًا: RSA .
RSA
RSA هو خوارزمية غير متماثلة يستخدم زوج من المفاتيح: مفتاح عام ومفتاح خاص. يستخدم المفتاح العام للتحقق من التوقيع، بينما يُستخدَم المفتاح الخاص لتوقيع.
يبدو JSON الخاص بالرأس لـ RSA هكذا:
{
"alg": "RS256",
"typ": "JWT"
}
RS256
تعني RSA-SHA256، مما يعني أن التوقيع يتم إنشاؤه باستخدام خوارزمية RSA ودالة الهاش SHA256. يمكنك أيضًا استخدامRS384
وRS512
لإنشاء التوقيعات باستخدام دوال الهاش SHA384 و SHA512، على التوالي.
يتم إنشاء التوقيع باستخدام المفتاح الخاص:
RSA-SHA256(base64Url(header) + "." + base64Url(payload), privateKey)
مرة أخرى، يمكننا تجميع هذه الأجزاء لإنشاء JWT، ويبدو JWT النهائي هكذا:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ.{{signature}}
الآن يمكن للعميل التحقق من التوقيع دون معرفة المفتاح الخاص.
ECDSA
على الرغم من انتشار استخدام RSA، إلا أنه يعاني من أحجام توقيع أكبر، أحيانًا تتجاوز الحجم المجمع للرأس والحمولة. خوارزمية التوقيع الرقمي للمنحنيات البيضاوية (ECDSA) هي خوارزمية غير متماثلة أخرى يمكنها إنشاء توقيعات أصغر حجماً وأكثر أداءً.
لإنشاء مفتاح خاص لـ ECDSA، نحتاج إلى اختيار منحنى. هذا خارج نطاق هذه المقالة، ولكن يمكنك العثور على مزيد من المعلومات هنا .
يبدو JSON الخاص بالرأس لـ ECDSA هكذا:
{
"alg": "ES256",
"typ": "JWT"
}
ES256
تعني ECDSA-SHA256، مما يعني أن التوقيع يتم إنشاؤه باستخدام خوارزمية ECDSA ودالة الهاش SHA256. يمكنك أيضًا استخدامES384
وES512
لإنشاء التوقيعات باستخدام دوال الهاش SHA384 و SHA512، على التوالي.
يتم إنشاء التوقيع باستخدام المفتاح الخاص:
ECDSA-SHA256(base64Url(header) + "." + base64Url(payload), privateKey)
يحتفظ الـ JWT النهائي بنفس هيكل RSA ولكنه يأتي بتوقيع أقصر بشكل ملحوظ:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28iLCJuYW1lIjoiSm9obiBEb2UifQ.{{signature}}
التحقق من JWT
التحقق من JWT هو عملية مباشرة كعملية إنشاء JWT:
- قم بتقسيم JWT إلى ثلاثة أجزاء (الرأس، الحمولة، والتوقيع) باستخدام الفاصل
.
. - قم بفك تشفير الرأس والحمولة باستخدام Base64URL.
- تحقق من التوقيع باستخدام الخوارزمية المحددة في الرأس والمفتاح العام (للخوارزميات غير المتماثلة).
تتوفر العديد من المكتبات للمساعدة في التحقق من JWT، مثل jose لـ Node.js ومتصفحات الويب.