CORE · مفاهیم احراز هویت

JWT — توکن وب جی‌سان

JSON Web Token

JWT یک قالب توکن امضاشده برای انتقال ادعاهای هویتی است؛ امنیت آن نه به خود قالب، بلکه تماماً به این بستگی دارد که سرور — و نه توکن — تصمیم بگیرد امضا با کدام الگوریتم و کدام کلید راستی‌آزمایی شود.

تیم فنی پی‌هانتر

JWT چیست و چگونه کار می‌کند؟

JSON Web Token یا JWT یک قالب استاندارد برای انتقال مجموعه‌ای از «ادعاها» (Claims) به‌صورت امضاشده است. توکن از سه بخش تشکیل می‌شود که با نقطه از هم جدا شده و هرکدام با Base64URL کدگذاری شده‌اند:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9   ← header
.eyJzdWIiOiIxMDQyIiwicm9sZSI6InVzZXIifQ ← payload
.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk ← signature

نکته‌ی اولی که باید روشن شود: Base64 رمزنگاری نیست. محتوای Payload برای هرکسی که توکن را دارد کاملاً خواناست. JWT محرمانگی نمی‌دهد، فقط یکپارچگی و اصالت می‌دهد — و آن هم فقط اگر راستی‌آزمایی امضا درست انجام شود. هرگز داده‌ی حساس را داخل Payload نگذارید.

هدر شامل فیلد alg است که الگوریتم امضا را اعلام می‌کند. و اینجا دقیقاً همان‌جایی است که بیشتر آسیب‌پذیری‌ها متولد می‌شوند: اگر پیاده‌سازی سرور برای انتخاب روش راستی‌آزمایی به alg داخل خودِ توکن نگاه کند، مهاجم — که کنترل کامل روی توکن دارد — تصمیم می‌گیرد امضا چگونه بررسی شود.

حملات رایج علیه JWT

سند مرجع در این حوزه RFC 8725 با عنوان «JSON Web Token Best Current Practices» است. حملاتی که مستقیماً در آن یا در رویه‌ی متعارف تست نفوذ مستندند:

  • alg: none — مهاجم الگوریتم را none می‌گذارد و امضا را حذف می‌کند. RFC 8725 تصریح می‌کند برخی کتابخانه‌ها توکن را «بدون بررسی هیچ امضایی» معتبر می‌شمردند. گونه‌های حساس به حروف مثل None و nOnE برای دور زدن فهرست‌های سیاه ناقص هم آزموده می‌شوند.
  • سردرگمی کلید / جایگزینی الگوریتم (RS256 → HS256) — مهاجم alg را از RS256 به HS256 تغییر می‌دهد و توکن را با کلید عمومی سرور به‌عنوان کلید مشترک HMAC امضا می‌کند. اگر کتابخانه بر پایه‌ی alg توکن تصمیم بگیرد و «کلید» را به‌صورت عمومی پاس بدهد، امضا با همان کلید عمومی — که مهاجم هم دارد — تأیید می‌شود. RFC 8725 دقیقاً همین سناریو را توصیف می‌کند.
  • کلید متقارن ضعیف — رمزهای HMAC قابل شکستن با دیکشنری. مقادیر پیش‌فرض فریم‌ورک‌ها و رشته‌های کوتاه، با hashcat -m 16500 در زمان کوتاهی شکسته می‌شوند.
  • تزریق هدرهای jku و x5u — این هدرها آدرس مجموعه‌کلید را مشخص می‌کنند. اگر سرور آدرس را از توکن بپذیرد، مهاجم آن را به میزبان خودش اشاره می‌دهد. این حمله با SSRF زنجیر می‌شود.
  • تزریق هدر jwk — جاسازی کلید عمومی مهاجم مستقیماً در هدر توکن و اعتماد سرور به آن.
  • تزریق kid — این فیلد شناسه‌ی کلید است و اغلب برای جست‌وجو در فایل‌سیستم یا پایگاه‌داده استفاده می‌شود؛ در نتیجه هم پیمایش مسیر و هم تزریق SQL از این نقطه گزارش شده‌اند.
  • نبود اعتبارسنجی aud، iss، exp و nbf — توکن معتبرِ منقضی‌شده، یا توکنی که برای سرویس دیگری صادر شده و اینجا هم پذیرفته می‌شود.
  • جایگزینی توکن بین زمینه‌ها — توکنی که برای سرویس A صادر شده، در سرویس B معتبر شمرده می‌شود. RFC 8725 برای همین، استفاده از تایپ صریح (typ) را توصیه می‌کند.

قاعده‌ی اصلی: برنامه الگوریتم را تعیین می‌کند، نه توکن

اگر قرار باشد از این صفحه فقط یک جمله را به تیم توسعه منتقل کنید، همین است. توصیه‌های RFC 8725 را می‌توان چنین خلاصه کرد:

  1. کتابخانه باید الگوریتم‌ها را به یک فهرست سفید محدود کند و راستی‌آزمایی کند که الگوریتم اعلام‌شده در هدر با عملیات رمزنگاری‌ای که واقعاً انجام می‌شود مطابقت دارد.
  2. فقط الگوریتم‌های به‌روز مجاز باشند.
  3. هر شکست در هر عملیات رمزنگاری باید به رد کل توکن منجر شود.
  4. هرگز از رمز عبور قابل حفظ توسط انسان به‌عنوان کلید MAC استفاده نشود؛ آنتروپی کافی الزامی است.
  5. ادعاهای aud و iss اعتبارسنجی شوند و تعلق کلید به صادرکننده‌ی ادعاشده تأیید شود. OWASP هم در A07:2025 همین دو ادعا را صراحتاً نام می‌برد.
  6. از تایپ صریح برای جلوگیری از سردرگمی توکن بین زمینه‌های مختلف استفاده شود.

در عمل یعنی: به‌جای verify(token, key) که الگوریتم را از توکن می‌خواند، باید verify(token, key, algorithms=["ES256"]) نوشته شود.

آیا JWT از نشست سمت سرور امن‌تر است؟

باور غلط رایج

«JWT از Session امن‌تر است.» این یک مصالحه‌ی متفاوت است، نه یک ارتقا. JWT برای مجوزدهی توزیع‌شده و بدون‌وضعیت طراحی شده و در همان جا ارزش دارد. اما ابطال آن دشوار است (توکن تا لحظه‌ی انقضا معتبر می‌ماند، حتی پس از خروج کاربر یا تغییر نقش)، اغلب در localStorage نگهداری می‌شود که هر XSS آن را می‌خواند، و کل سطح حمله‌ی راستی‌آزمایی امضا را اضافه می‌کند. برای یک برنامه‌ی یکپارچه‌ی تک‌سروری، نشست کدر (Opaque) سمت سرور معمولاً انتخاب امن‌تری است.

مقایسه‌ی صادقانه:

معیارنشست سمت سرورJWT
ابطال فوریساده — رکورد را حذف کنیددشوار؛ نیاز به فهرست سیاه که خودِ بدون‌وضعیت بودن را نقض می‌کند
مقیاس‌پذیری افقینیاز به ذخیره‌ساز مشترکبدون نیاز به ذخیره‌ساز مشترک
سطح حمله‌ی رمزنگاریناچیزالگوریتم، کلید، هدرها، ادعاها
محل نگهداری امنکوکی HttpOnlyکوکی HttpOnly — نه localStorage

توجه کنید که نگهداری توکن در کوکی، مسئله‌ی CSRF را برمی‌گرداند و باید با توکن هم‌زمان‌ساز یا هدر سفارشی پوشش داده شود. هیچ گزینه‌ای رایگان نیست؛ انتخاب باید آگاهانه باشد.

چگونه در تست نفوذ کشف می‌شود

  1. یافتن توکن: هدر Authorization: Bearer، کوکی‌ها، و به‌ویژه localStorage و sessionStorage در پنل Application مرورگر. حضور توکن در localStorage خودش یک یافته است.
  2. رمزگشایی و بازبینی ادعاها: آیا exp وجود دارد و کوتاه است؟ آیا aud و iss هستند؟ آیا ادعاهای نقش (role، scope، tenant) در توکن‌اند و سرور به آن‌ها اعتماد می‌کند؟
  3. آزمون امضا: امضا را حذف کنید؛ یک بایت از امضا را تغییر دهید؛ alg را روی none بگذارید؛ RS256 را به HS256 تبدیل کنید و با کلید عمومی سرور امضا کنید (کلید عمومی معمولاً در /.well-known/jwks.json در دسترس است).
  4. آزمون هدرها: jku، x5u، jwk و kid را دستکاری کنید و پاسخ سرور و ترافیک خروجی را رصد کنید.
  5. آزمون کلید ضعیف: برای توکن‌های HS256 با hashcat -m 16500 و یک فهرست رمز متعارف، شکستن کلید را بیازمایید.
  6. آزمون چرخه‌ی حیات: پس از خروج کاربر، همان توکن را دوباره بفرستید. اگر هنوز کار می‌کند — که در پیاده‌سازی‌های بدون‌وضعیت معمول است — این را در گزارش با اثر واقعی‌اش بنویسید.

ابزار متعارف: افزونه‌ی JWT Editor در Burp Suite، ابزار jwt_tool و hashcat. توجه کنید که این آزمون‌ها بخش استانداردی از تست نفوذ API هستند و اسکنرهای عمومی معمولاً فقط حالت alg: none را پوشش می‌دهند.

نگاشت به OWASP و CWE

  • A07:2025 — Authentication Failures: خانه‌ی اصلی نقص‌های JWT. OWASP در همین دسته صراحتاً می‌گوید برای JWT باید ادعاهای aud و iss اعتبارسنجی شوند.
  • A01:2025 — Broken Access Control: وقتی دستکاری ادعای نقش داخل توکن به ارتقای سطح دسترسی منجر شود، یافته اینجا رده‌بندی می‌شود. OWASP در همین دسته توصیه می‌کند عمر JWT کوتاه نگه داشته شود.
  • CWE-287 (احراز هویت نادرست) و CWE-347 (راستی‌آزمایی نادرست امضای رمزنگاری) نزدیک‌ترین شناسه‌های CWE هستند؛ برای کلید ثابت جاسازی‌شده CWE-798.

پرسش‌های متداول

آیا JWT از نشست سمت سرور امن‌تر است؟

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

آیا محتوای JWT رمزنگاری شده است؟

در حالت متعارف (JWS) خیر. سه بخش توکن فقط با Base64URL کدگذاری شده‌اند و هرکس توکن را داشته باشد می‌تواند Payload را بخواند. امضا فقط جلوی تغییر محتوا را می‌گیرد، نه خواندن آن. برای محرمانگی باید از JWE استفاده کرد — یا ساده‌تر، داده‌ی حساس را اصلاً داخل توکن نگذارید.

حمله‌ی سردرگمی کلید در JWT چیست؟

مهاجم الگوریتم را از یک الگوریتم نامتقارن مثل RS256 به یک الگوریتم متقارن مثل HS256 تغییر می‌دهد و توکن را با کلید عمومی سرور به‌عنوان کلید مشترک HMAC امضا می‌کند. کلید عمومی معمولاً آزادانه در دسترس است. اگر کتابخانه الگوریتم را از خود توکن بخواند، امضا معتبر شمرده می‌شود. راه‌حل، تثبیت فهرست الگوریتم‌های مجاز در سمت سرور است.

JWT را کجا ذخیره کنیم؟

در کوکی با صفات HttpOnly، Secure و SameSite. نگهداری در localStorage رایج است اما توکن را برای هر XSS خواندنی می‌کند و هیچ سازوکار مرورگری از آن محافظت نمی‌کند. اگر کوکی را انتخاب کردید، دفاع CSRF را هم اضافه کنید.

پیشگیری / رفع

به ترتیب اثربخشی:

  1. الگوریتم را در سمت سرور تثبیت کنید. کتابخانه را طوری فراخوانی کنید که فهرست الگوریتم‌های مجاز صراحتاً پاس داده شود و مقدار alg داخل توکن هرگز مبنای تصمیم نباشد.
  2. ادعاها را کامل اعتبارسنجی کنید: exp، nbf، iss، aud و در OIDC مقدار nonce. هر شکست باید به رد کل توکن منجر شود.
  3. هدرهای jku، x5u، jwk و kid را از توکن نپذیرید. مجموعه‌کلید باید از پیکربندی سرور بیاید، نه از ورودی مهاجم؛ اگر kid لازم است، مقدار آن را با فهرست سفید تطبیق دهید.
  4. کلید قوی: برای HMAC کلید تصادفی با آنتروپی کافی و نه رمز عبور انسانی. جایی که هر دو سر را کنترل می‌کنید، EdDSA (Ed25519) یا ES256 بر RS256 ترجیح دارد.
  5. عمر کوتاه به‌علاوه‌ی سازوکار تجدید، و یک مسیر ابطال واقعی برای خروج، تغییر رمز و تغییر نقش.
  6. محل نگهداری: کوکی با صفات HttpOnly، Secure و SameSite — نه localStorage. اگر کوکی انتخاب شد، دفاع CSRF را هم اضافه کنید.
  7. و قبل از همه: اگر معماری شما یک برنامه‌ی یکپارچه است، بازبینی کنید که آیا اصلاً به JWT نیاز دارید یا نشست سمت سرور کار را ساده‌تر و امن‌تر می‌کند.

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

→ بازگشت به دانشنامه
PUT IT TO THE TEST

امنیت سامانه‌ی شما را
به مهاجمان واگذار نکنید

تیم پی‌هانتر با دیدِ یک مهاجم واقعی، این آسیب‌پذیری‌ها و ده‌ها مورد دیگر را روی دارایی‌های شما می‌سنجد.