من فهم التوقيع الكريبتوغرافي لحد استغلال الـ algorithm confusion وسرقة الـ secret — الدليل الكامل لاختراق الـ JSON Web Tokens.
الفرق بين الـ Stateful والـ Stateless — وليه الـ JWT هو الـ attack surface الأساسية في كل API حديثة.
الـ server بيعمل session في الـ DB أو Redis وبيديك session ID في الـ cookie. كل request — database lookup.
الـ server بيعمل JWT مكتوب فيه كل المعلومات. مفيش DB lookup — بيتحقق من الـ signature بس.
السؤال اللي لازم تسأله: لو الـ server مش بيعمل database lookup — إزاي بيتأكد إن الـ token ما اتغيرش؟ الإجابة هي الـ Cryptographic Signature.
والسؤال التاني: لو الـ attacker قدر يغير الـ token أو يعمل token جديد بدون الـ secret key — إيه اللي هيحصل؟ هيقدر يتحول لأي user في السيستم — حتى الـ admin.
الـ JWT مش بس token — هو التعاقد المكتوب بين الـ client والـ server. لو قدرت تزور التوقيع، الـ contract بقى بتاعتك.
الأكتر شيوعاً في APIs:
بعض التطبيقات تحط الـ JWT في Cookie:
Anti-pattern خطير — بيظهر في الـ logs:
قبل ما تهاجم أي حاجة لازم تفهم الـ structure. كل جزء ليه دور مختلف — وكل جزء له attack vector مختلف.
الـ Header: بيقول "أنا JWT وبيتحقق مني باستخدام HS256". الـ alg field هو أول حاجة الـ server بيشوفها — وهو الـ attack surface الأولى.
الـ Payload: بيحتوي على الـ Claims — الادعاءات عن الـ user. في Claims مقننة زي exp (expiration)، sub (user ID)، وفي Claims خاصة بالتطبيق زي role أو isAdmin.
الـ Signature: هو الحارس. بدونه — الـ payload مجرد JSON ممكن يغيره أي حد. معاه — الـ server يتأكد إن الـ token ما اتلعبش فيه.
| Claim | الاسم | الوظيفة | أهميته للـ Attacker |
|---|---|---|---|
| sub | Subject | الـ user ID | CRITICAL — تغييره = ATO مباشر |
| exp | Expiration | متى الـ token بينتهي | HIGH — رفعه = token لا ينتهي |
| aud | Audience | الـ service اللي الـ token معمول ليه | HIGH — cross-service replay |
| iss | Issuer | مين أصدر الـ token | MEDIUM |
| jti | JWT ID | Unique ID للـ token | MEDIUM — replay attacks |
| iat | Issued At | إمتى اتعمل | LOW |
أهم مفهوم في الـ JWT — الـ payload مكشوف دايماً ومقروء من أي حد.
كتير من الـ developers بيفتكروا إن الـ JWT payload "متشفر" لأنه شايف حروف عشوائية. ده خطأ فادح.
Encoding = تحويل format بدون مفتاح.
Encryption = تشفير يحتاج مفتاح
للفك.
ده معناه إن أي حد يقدر يقرأ الـ payload — حتى لو الـ signature صح. المعلومات زي الـ userId والـ role والـ email — كلها plain text لأي حد يعترض الـ token.
لكن قراءة الـ payload مختلفة عن تعديله. لو غيرت الـ payload — الـ signature هتبقى مش بتطابق. الـ server هيرفض. ده الأمان الحقيقي.
فهم الـ signature هو مفتاح فهم كل الـ attacks. كل attack بتستغل weakness في كيفية التحقق من التوقيع.
الـ signature عبارة عن hash function بياخد input وبيطلع output ثابت. الـ input هو الـ header والـ payload مع بعض، والـ function هي الـ HMAC-SHA256.
الفكرة الأساسية: لو غيرت أي byte في الـ input — الـ output (الـ signature) هيتغير تماماً. ده بيعني إن أي تعديل على الـ payload هيكشفه الـ server فوراً لما يتحقق.
الـ SECRET_KEY هو الفرق بين تطبيق آمن وتطبيق قابل للاختراق. بدون الـ secret — المهاجم يقدر يشوف الـ payload بس مش يقدر يعمل signature صح.
لو المهاجم عنده الـ secret — خلاص. هيقدر يعمل أي token عايزه بأي claims. لو الـ secret ضعيف — يقدر يخمنه بـ hashcat. لو التحقق مش موجود أصلاً — مش محتاج الـ secret خالص.
الـ test الأساسي لكل JWT: شيل آخر حرف من الـ signature وبعت الـ request. لو رجع 200 — الـ signature مش validated. لو رجع 401 — الـ signature validated وعندنا attack vectors تانية.
لو الـ signature مش validated — تقدر تغير أي claim في الـ payload وبعته وهيشتغل. ده الـ ATO أبسط طريقة.
نوع الـ algorithm بيحدد الـ attack vectors المتاحة لك. لازم تعرف الفرق قبل ما تبدأ.
الـ server يستخدم نفس الـ SECRET_KEY عشان يوقع ويتحقق. ده معناه:
الـ server يوقع بالـ private key ويعطي الـ public key لأي حد عشان يتحقق. ده معناه:
أول لحظة تشوف JWT — افتح jwt.io وشوف الـ alg field في الـ header. ده بيحدد بالظبط إيه الـ attacks المتاحة.
لو HS256: جرب الـ algorithm confusion (RS256 → HS256)، وجرب تعمل brute force على الـ secret بالـ hashcat. لو RS256: فكر في الـ jku injection، الـ jwk injection، والـ kid path traversal.
| Algorithm | النوع | الـ Attack Vectors | الأولوية |
|---|---|---|---|
| HS256/384/512 | Symmetric (HMAC) | alg:none، Algorithm Confusion، Weak Secret Brute Force | HIGHEST |
| RS256/384/512 | Asymmetric (RSA) | Algorithm Confusion، jku injection، jwk injection، kid injection | HIGH |
| ES256/384/512 | Asymmetric (ECDSA) | jku injection، jwk injection، kid injection | HIGH |
| none | No Signature | مباشرة قابل للتزوير | CRITICAL |
الـ JWT spec بيسمح بـ "none" كـ algorithm — معناه مفيش signature. لو الـ server قبله — الـ game over.
الـ JWT spec الأصلي بيتيح الـ none algorithm للحالات اللي مفيهاش حاجة تتحقق منها. بس لو الـ server قبل الـ none algorithm من الـ client — أي حد يقدر يعمل token من غير signature.
الـ attack بسيط جداً: خد الـ JWT الأصلي، غير الـ alg في الـ header لـ "none"، غير الـ payload زي ما تحب، واشيل الـ signature — بس خلي الـ dot الأخير موجود!
بعض الـ implementations بيبلوكوا الـ "none" string. بس كتير من الـ libraries بتعمل case-insensitive comparison أو مش بتتحقق من كل الـ variations:
الأذكى والأكثر تأثيراً. بتخلي الـ server يتحقق من الـ signature بالـ public key كـ HMAC secret.
الـ server عنده كود زي ده: jwt.verify(token, key). لو الـ server عامل RS256 — الـ key المفروض يكون الـ public key. بس لو المهاجم غير الـ alg لـ HS256 — الـ library هتستخدم الـ public key كـ HMAC secret. والـ attacker عنده الـ public key — هو public!
الـ scenario: التطبيق بيستخدم RS256. الـ public key متاح من endpoint زي /.well-known/jwks.json أو /api/public-key. المهاجم بيعمل الآتي:
يجيب الـ public key (هو public — مش سر). يبني JWT جديد بالـ header {"alg":"HS256"}. يوقع الـ token بـ الـ HMAC-SHA256 باستخدام الـ public key كـ secret. الـ server بيستلم الـ token، بيشوف alg:HS256، بيعمل verify بالـ public key كـ HMAC secret — ويتطابق!
الـ HS256 signature يمكن كسره offline. كل اللي محتاجه JWT واحد صح وwordlist.
الـ JWT signature بيمكن التحقق منه offline. يعني مش محتاج تبعت requests للـ server عشان تعرف لو الـ secret صح. ده معناه:
تاخد JWT واحد صح من الـ traffic، وتجرب millions من الـ secrets في الثانية بالـ hashcat. الـ server مش بيعرف وما فيش rate limiting هنا.
الـ JWT header بيحتوي على parameters بيستخدمها الـ server لإيجاد الـ key — وكلها attack vectors.
الـ JWT header مش بس بيحتوي على alg. في parameters تانية زي kid (Key ID) وjku (JWK Set URL) وjwk (JSON Web Key). كلها بيستخدمها الـ server عشان يجيب الـ key اللي هيتحقق بيه من الـ signature.
الـ vulnerability: لو الـ server بيثق في القيم دي من الـ token من غير validation — المهاجم يقدر يوجهه لـ key بتاعه هو.
الـ kid (Key ID) parameter بيقول للـ server أي key يستخدم. لو الـ server بيجيب الـ key من الـ filesystem بناءً على الـ kid — Path Traversal ممكن.
لو الـ server بيعمل SQL query بالـ kid عشان يجيب الـ key من DB — SQL Injection ممكن عشان يرجع key معروف.
الـ jku parameter بيقول للـ server يجيب الـ public key من URL معين. لو الـ server فتح الـ URL من غير whitelist — المهاجم بيستضيف الـ key بتاعه على server تاني.
الـ jwk parameter بيسمح بتضمين الـ public key في الـ header نفسه. لو الـ server قبل الـ key من الـ header من غير whitelist — المهاجم يضمن الـ key بتاعه.
لما تكسر الـ signature validation — الـ claims بقت ملعب بتاعك. كل claim فيه impact مختلف.
الفرق بين الاتنين هو كل الفرق. كتير من الـ developers بيخلطوا بينهم — وده بيلغي الـ security كلها.
لما بتعمل code review أو لما بيكون عندك source code — دور على jwt.decode( بدل jwt.verify(. في JavaScript: decode مش آمن بدون verify. في Python: options={"verify_signature": False} خطر.
من اللحظة اللي تشوف فيها JWT في الـ Burp لحد ما تكتب الـ report — كل خطوة بالترتيب.
افتح Burp Suite وابحث عن الـ pattern eyJ في الـ headers والـ cookies. دي الـ signature بتاعة كل JWT. في Burp → Target → Site Map → Search لـ "eyJ". كمان ابحث في الـ Response headers لأن بعض التطبيقات بتعمل token rotation.
افتح jwt.io أو استخدم الـ Burp JWT Editor extension. وثق في الـ notes: الـ algorithm، كل الـ claims في الـ header والـ payload، وخصوصاً الـ role وuser ID claims. ابحث عن sequential IDs في الـ sub/userId.
في Burp Repeater: شيل آخر حرف من الـ signature وبعت الـ request. لو 200 → signature not validated → الـ payload مفتوح للتعديل المباشر. لو 401 → validated → انتقل للـ attacks التانية.
غير الـ header لـ {"alg":"none"}، غير الـ payload، واشيل الـ signature مع الاحتفاظ بالـ trailing dot. جرب كل الـ variations: "None"، "NONE"، "nOnE". استخدم Burp JWT Editor → Attack → None Algorithm.
جيب الـ public key من /.well-known/jwks.json أو الـ endpoint المعروفة. حول الـ alg لـ HS256. وقع الـ token بالـ HMAC-SHA256 باستخدام الـ public key كـ secret. استخدم jwt_tool.py -X k أو Burp JWT Editor.
استخدم hashcat -a 0 -m 16500 مع أشهر الـ wordlists. ابدأ بالـ jwt.secrets.list ثم rockyou. لو اتكسر الـ secret — اعمل token بأي claims عايزها.
لو في kid: جرب path traversal ../../../../dev/null وSQL injection. لو في jku: حاول توجهه لـ server بتاعك. لو في jwk: استخدم Burp JWT Editor → Embedded JWK attack.
بعد ما قدرت تزور الـ token: غير الـ role لـ admin، غير الـ sub لـ ID تاني، ارفع الـ exp. وثق الـ PoC بالـ screenshots وكتب الـ report مع الـ impact الكامل.
من لحظة ما تشوف JWT لحد ما تكتب الـ report — كل decision له tree.
PHASE 1 — SIGNATURE VALIDATION TEST
PHASE 2 — NONE ALGORITHM ATTACK
PHASE 3 — ALGORITHM BRANCH
IF HS256 BRANCH
IF RS256/ES256 BRANCH
PHASE 4 — HEADER PARAMETERS (any algorithm)
| Attack | الـ Condition | الـ Tool | الـ Impact |
|---|---|---|---|
| Signature Not Validated | اي algorithm | Burp Repeater | AUTH BYPASS |
| alg:none | Library vulnerable | Burp JWT Editor | AUTH BYPASS |
| Weak Secret | HS256 | Hashcat -m 16500 | TOKEN FORGE |
| Algorithm Confusion | RS256/ES256 | jwt_tool.py -X k | TOKEN FORGE |
| kid Path Traversal | kid in header | Manual + jwt_tool | TOKEN FORGE |
| kid SQL Injection | kid → DB | Manual | TOKEN FORGE |
| jku Injection | jku in header | Burp JWT Editor | TOKEN FORGE |
| jwk Injection | jwk accepted | Burp JWT Editor | TOKEN FORGE |
| Claim: role → admin | بعد forging | jwt.io | PRIV ESC |
| Claim: exp → 9999999999 | بعد forging | jwt.io | PERSISTENCE |
| Claim: sub → 1 (admin) | بعد forging | jwt.io | ATO |
6 أسئلة عن decision-making حقيقي — مش عن تعريفات.