آن پروژه‌ای که با یک توکن ساده حل شد

چند سال پیش، برای یک پروژهٔ API، از سشن‌های سنتی استفاده می‌کردیم. مشکل این بود که هر درخواست باید به دیتابیس مراجعه می‌کرد تا سشن را بررسی کند. با رشد تعداد درخواست‌ها، این رویکرد به گلوگاه تبدیل شد. آن پروژه را با JWT بازنویسی کردیم و مشکلات حل شد: سرعت بیشتر، مقیاس‌پذیری بهتر و تجربه کاربری روان‌تر. در این مقاله، همان چیزی را که در پروژه‌های خودم یاد گرفته‌ام، با شما در میان می‌گذارم. اگر با مفاهیم پایه آشنا نیستید، پیشنهاد می‌کنم ابتدا احراز هویت چیست و چه انواعی دارد را بخوانید.

JWT دقیقاً چیست؟

JWT یا JSON Web Token، یک استاندارد باز (RFC 7519) برای انتقال امن اطلاعات بین دو طرف است. این اطلاعات به‌صورت یک رشتهٔ فشرده و امضاشده ذخیره می‌شوند. JWT معمولاً در احراز هویت APIها استفاده می‌شود، اما کاربردهای دیگری مثل انتقال اطلاعات بین سرویس‌ها هم دارد.

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

ساختار JWT

هر JWT از سه بخش تشکیل می‌شود که با نقطه از هم جدا می‌شوند:

  • Header: شامل نوع توکن و الگوریتم امضا. مثلاً {"alg": "HS256", "typ": "JWT"}.
  • Payload: شامل داده‌های توکن مثل شناسه کاربر، نقش، زمان انقضا. این بخش معمولاً Claims نامیده می‌شود.
  • Signature: امضای دیجیتال برای تایید صحت توکن. اگر کسی Payload را تغییر دهد، امضا نامعتبر می‌شود.

این سه بخش با Base64Url کدگذاری و با نقطه به هم متصل می‌شوند. نتیجه یک رشته مثل این است:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

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

مکانیزم کار JWT

فرآیند احراز هویت با JWT در چند مرحله انجام می‌شود:

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

این چرخه در هر درخواست تکرار می‌شود. برخلاف سشن‌های سنتی، سرور نیازی به مراجعه به دیتابیس برای بررسی توکن ندارد. همین ویژگی، JWT را برای معماری‌های مقیاس‌پذیر مناسب می‌کند.

مزایای JWT

JWT چند مزیت مهم دارد که آن را برای برخی سناریوها بهتر از سشن‌های سنتی می‌کند:

  • Stateless: سرور نیازی به ذخیره سشن ندارد. همین ویژگی، مقیاس‌پذیری را افزایش می‌دهد.
  • مقیاس‌پذیری: می‌توان چندین سرور را برای پاسخ به درخواست‌ها استفاده کرد، بدون نیاز به اشتراک سشن.
  • مناسب برای API: JWT استاندارد رایج در احراز هویت APIها است.
  • مناسب برای موبایل: چون Stateless است، برای اپلیکیشن‌های موبایل مناسب است.
  • انتقال اطلاعات: می‌توان اطلاعات اضافی مثل نقش کاربر را در توکن ذخیره کرد.
  • امضای دیجیتال: امضا، صحت توکن را تضمین می‌کند.
  • استاندارد باز: JWT یک استاندارد بین‌المللی است و پیاده‌سازی‌های متعددی دارد.
  • پشتیبانی از چند زبان: تقریباً همه زبان‌های برنامه‌نویسی از JWT پشتیبانی می‌کنند.
  • اندازه کوچک: نسبت به سایر روش‌ها، حجم کمتری دارد.
  • پشتیبانی از Cross-Domain: می‌توان از JWT در دامنه‌های مختلف استفاده کرد.

این مزایا، JWT را برای APIها، اپلیکیشن‌های موبایل و معماری‌های میکروسرویس مناسب می‌کند.

معایب و محدودیت‌های JWT

JWT مزایای زیادی دارد اما محدودیت‌هایی هم دارد که باید در نظر بگیرید:

  • عدم امکان ابطال سریع: چون توکن در سرور ذخیره نمی‌شود، ابطال آن سخت است. اگر توکن لو برود، تا زمان انقضا معتبر می‌ماند.
  • حساسیت کلید امضا: اگر کلید امضا لو برود، مهاجم می‌تواند توکن جعلی بسازد.
  • خطر XSS: اگر توکن در localStorage ذخیره شود، در معرض XSS قرار می‌گیرد. راهنمای کامل در حملات XSS چیست و چگونه دفع می‌شود آمده است.
  • حجم بیشتر از سشن ID: توکن JWT معمولاً بزرگ‌تر از سشن ID است، بنابراین در هر درخواست حجم بیشتری منتقل می‌شود.
  • عدم انعطاف: اگر اطلاعات کاربر تغییر کند، توکن قبلی معتبر می‌ماند مگر اینکه ابطال شود.
  • پیچیدگی پیاده‌سازی: پیاده‌سازی درست JWT نیازمند دانش فنی است.
  • مسائل حریم خصوصی: اطلاعات Payload قابل خواندن است، پس نباید اطلاعات حساس در آن ذخیره شود.

این محدودیت‌ها باعث می‌شود که JWT برای همه سناریوها مناسب نباشد. برای برخی سناریوها، سشن‌های سنتی هنوز بهتر هستند.

JWT در برابر سشن سنتی

مقایسه JWT با سشن‌های سنتی:

  • ذخیره‌سازی: JWT در کلاینت، سشن در سرور.
  • مقیاس‌پذیری: JWT مقیاس‌پذیرتر.
  • ابطال: سشن سنتی امکان ابطال آنی دارد، JWT خیر.
  • حجم: JWT بزرگ‌تر، سشن ID کوچک‌تر.
  • مناسب برای: JWT برای API، سشن برای وب سنتی.
  • Stateful: سشن Stateful، JWT Stateless.
  • پیچیدگی: سشن ساده‌تر، JWT پیچیده‌تر.
  • امنیت: هرکدام در سناریوی خودشان امن هستند.

انتخاب بین این دو، بستگی به نوع پروژه و نیازها دارد.

اشتباهات رایج در استفاده از JWT

چند اشتباه رایج در پیاده‌سازی JWT:

  • ذخیره اطلاعات حساس: چون Payload قابل خواندن است، هرگز رمز عبور یا اطلاعات حساس در آن ذخیره نکنید.
  • ذخیره در localStorage: در معرض XSS است. راه‌حل بهتر، استفاده از HttpOnly Cookies است.
  • استفاده از الگوریتم none: هیچ‌وقت از الگوریتم none استفاده نکنید، چون امضا ندارد.
  • عدم بررسی امضا: همیشه امضای توکن را در سرور بررسی کنید.
  • عدم تنظیم زمان انقضا: توکن‌ها باید زمان انقضا داشته باشند.
  • عدم استفاده از HTTPS: JWT بدون HTTPS قابل رهگیری است. راهنما در حمله MITM چیست آمده است.
  • استفاده از کلید ضعیف: کلید امضا باید قوی و تصادفی باشد.
  • عدم ابطال توکن قدیمی: توکن‌های قدیمی باید ابطال شوند.
  • استفاده از JWT برای سشن‌های طولانی: JWT برای سشن‌های کوتاه‌مدت مناسب است.
  • ذخیره کلید در کد: کلید امضا باید در متغیرهای محیطی نگه‌داری شود.

این اشتباهات در پروژه‌های واقعی مشاهده شده‌اند و می‌توانند به نقض امنیت منجر شوند.

بهترین روش‌های استفاده از JWT

روش‌های صحیح استفاده از JWT:

  • زمان انقضای کوتاه: توکن‌ها باید عمر کوتاه داشته باشند، مثلاً ۱۵ دقیقه.
  • Refresh Token: برای تمدید، از Refresh Token استفاده کنید که عمر طولانی‌تری دارد.
  • ذخیره در HttpOnly Cookies: توکن‌ها را در HttpOnly Cookies ذخیره کنید تا در معرض XSS نباشند.
  • HTTPS اجباری: تمام ارتباطات با JWT باید از HTTPS استفاده کنند.
  • امضای قوی: از الگوریتم‌های قوی مثل RS256 یا ES256 استفاده کنید.
  • کلید امن: کلید امضا را در متغیرهای محیطی نگه‌داری کنید.
  • بررسی امضا: همیشه امضای توکن را در سرور بررسی کنید.
  • Blacklist: برای ابطال توکن‌های قدیمی، از Blacklist استفاده کنید.
  • Claims کامل: در Payload، اطلاعات ضروری مثل شناسه کاربر، نقش و زمان انقضا را قرار دهید.
  • پایش مستمر: استفاده از توکن‌ها را پایش کنید تا فعالیت مشکوک را شناسایی کنید.
## JWT در وردپرس

در سایت‌های وردپرسی، JWT می‌تواند برای احراز هویت APIها استفاده شود. راهکارهای رایج:

  • افزونه‌های JWT: افزونه‌هایی مثل JWT Authentication for WP REST API امکان استفاده از JWT در REST API وردپرس را فراهم می‌کنند.
  • Headless WordPress: در معماری Headless، JWT برای احراز هویت بین فرانت‌اند و بک‌اند استفاده می‌شود.
  • اپلیکیشن‌های موبایل: برای اپلیکیشن‌های موبایل که از API وردپرس استفاده می‌کنند.
  • سرویس‌های جانبی: برای ارتباط بین وردپرس و سرویس‌های خارجی.

برای امن‌سازی JWT در وردپرس، از افزونه‌های معتبر استفاده کنید و HTTPS را اجباری کنید. راهنماهای امنیتی در چگونه امنیت وب‌سایت را افزایش دهیم و راهنمای امنیت وردپرس برای مبتدیان آمده است.

مقایسه JWT با OAuth

JWT و OAuth دو مفهوم متفاوت هستند که اغلب با هم اشتباه گرفته می‌شوند:

  • OAuth: استانداردی برای مجوزدهی. کاربر به سایت ثالث اجازه دسترسی به منابع خود را می‌دهد. راهنما در OAuth چیست و چگونه کار می‌کند آمده است.
  • JWT: استانداردی برای انتقال امن اطلاعات. می‌تواند در OAuth به‌عنوان توکن دسترسی استفاده شود.
  • ترکیب: در OAuth 2.0، از JWT به‌عنوان Access Token یا ID Token استفاده می‌شود.

در عمل، این دو مکمل یکدیگرند، نه جایگزین.

پرسش‌های پرتکرار دربارهٔ JWT

آیا JWT برای همه سناریوها مناسب است؟ خیر. JWT برای APIها و اپلیکیشن‌های مدرن مناسب است، اما برای سایت‌های سنتی، سشن‌های معمولی کافی هستند.

آیا JWT امن است؟ اگر درست پیاده‌سازی شود، بله. اما اشتباه در پیاده‌سازی می‌تواند به آسیب‌پذیری منجر شود.

آیا JWT رمزنگاری‌شده است؟ خیر، فقط امضاشده است. اطلاعات Payload قابل خواندن است.

چطور می‌توانم JWT را ابطال کنم؟ به‌طور سنتی سخت است، اما با Blacklist یا زمان انقضای کوتاه می‌توانید آن را مدیریت کنید.

آیا JWT جایگزین سشن می‌شود؟ در برخی سناریوها بله، اما در سناریوهای دیگر نه. هرکدام برای موارد خاصی مناسب هستند.

آیا باید JWT در localStorage ذخیره شود؟ خیر. بهتر است در HttpOnly Cookies ذخیره شود تا از XSS محافظت شود.

مستندات ویکی‌پدیا در مورد JSON Web Token: منبع معتبر برای اطلاعات فنی.

سخن آخر: انتخاب آگاهانه

JWT یکی از ابزارهای مهم در احراز هویت مدرن است. با درک مکانیزم، مزایا و معایب آن، می‌توانید درست تصمیم بگیرید که آیا JWT برای پروژه شما مناسب است یا نه. در اکثر موارد، JWT برای APIها و اپلیکیشن‌های موبایل انتخاب خوبی است، در حالی که سشن‌های سنتی برای سایت‌های وب معمولی همچنان کاربردی هستند. در هر صورت، پیاده‌سازی صحیح JWT نیازمند رعایت اصول امنیتی است که در این مقاله مرور کردیم. اگر تجربه‌ای از استفاده از JWT دارید یا سؤالی دربارهٔ آن دارید، در دیدگاه‌ها بنویسید؛ تجربهٔ شما به دیگران کمک می‌کند تا انتخاب بهتری داشته باشند.