SECURITY

JWT چیست؟ ساختار، اعتبارسنجی درست و ۸ اشتباه امنیتی رایج

Header، Payload و Signature در JWT، تفاوت HS256 و RS256، فهرست اعتبارسنجی درست و دلیل خطرناک‌بودن alg=none و نگه‌داشتن توکن در localStorage.

JWT (JSON Web Token) ساده و پرکاربرد است و همین سادگی باعث می‌شود در عمل زیاد اشتباه پیاده‌سازی شود. این راهنما ساختار توکن را توضیح می‌دهد، فهرست درست اعتبارسنجی را ارائه می‌کند و هشت اشتباه امنیتی را که در بازبینی‌ها بیش از بقیه دیده می‌شود بررسی می‌کند.

ساختار یک JWT

یک JWT سه بخش دارد که با نقطه از هم جدا می‌شوند: header.payload.signature. دو بخش اول JSON هستند که با Base64URL کدگذاری شده‌اند (نه رمزنگاری)؛ بخش سوم امضای دیجیتال دو بخش اول است.

// Header
{ "alg": "RS256", "typ": "JWT", "kid": "2026-10" }

// Payload (claims)
{
  "iss": "https://auth.example.com",
  "sub": "user_421",
  "aud": "api.example.com",
  "iat": 1760000000,
  "exp": 1760000900,
  "scope": "orders:read"
}
JWTJWT Decoderتوکن خودتان را بچسبانید و Header، Payload، انقضا و امضا را ببینید؛ همه‌چیز داخل مرورگر انجام می‌شود.

ادعاهای (claims) استاندارد

claimمعنی
issصادرکننده؛ چه کسی توکن را ساخته است
subموضوع؛ معمولاً شناسه‌ی کاربر
audمخاطب؛ این توکن برای کدام سرویس است
expزمان انقضا (Unix timestamp به ثانیه)
nbfپیش از این زمان معتبر نیست
iatزمان صدور
jtiشناسه‌ی یکتای توکن؛ برای ابطال و جلوگیری از بازپخش

فیلدهای زمانی عدد Unix هستند؛ برای دیدن تاریخ خوانا از مبدل Timestamp استفاده کنید.

HS256 در برابر RS256 و ES256

HS256 الگوریتم متقارن است: یک کلید مخفی هم برای امضا و هم برای بررسی. ساده است و برای یک سرویس که هم توکن می‌سازد و هم بررسی می‌کند مناسب. مشکل وقتی پیش می‌آید که چند سرویس باید توکن را بررسی کنند: همه‌ی آن‌ها کلیدی دارند که می‌تواند توکن جعل کند.

RS256 (RSA) و ES256 (منحنی بیضوی) نامتقارن‌اند: فقط سرویس احراز هویت کلید خصوصی دارد و بقیه با کلید عمومی فقط می‌توانند بررسی کنند. ES256 امضاهای کوچک‌تر و بررسی سریع‌تری دارد. انتشار کلیدهای عمومی معمولاً از طریق یک آدرس JWKS انجام می‌شود و هدر kid مشخص می‌کند کدام کلید مناسب است، که چرخش کلید (key rotation) را بدون قطعی ممکن می‌کند.

فهرست درست اعتبارسنجی

پیش از اینکه به هر claim اعتماد کنید، به‌ترتیب این کارها را انجام دهید:

  1. الگوریتم را خودتان تعیین کنید، نه توکن. فهرست الگوریتم‌های مجاز را در کد بنویسید.
  2. امضا را با کلید درست بررسی کنید (انتخاب کلید از روی kid فقط از میان کلیدهای مورد اعتماد خودتان).
  3. iss و aud را مقایسه کنید با مقدار مورد انتظار؛ توکنی که برای سرویس دیگری صادر شده نباید اینجا پذیرفته شود.
  4. exp و nbf را بررسی کنید، با اختلاف زمانی (leeway) کوچک (مثلاً ۳۰ تا ۶۰ ثانیه) برای ساعت‌های ناهماهنگ.
  5. در صورت نیاز، jti را در فهرست ابطال‌شده‌ها جست‌وجو کنید.
// Node.js — کتابخانه‌ی jsonwebtoken
const payload = jwt.verify(token, publicKey, {
  algorithms: ['RS256'],          // فهرست مجاز، نه چیزی که توکن می‌گوید
  issuer: 'https://auth.example.com',
  audience: 'api.example.com',
  clockTolerance: 30,              // ثانیه
});

۸ اشتباه امنیتی رایج

۱. پذیرفتن alg: none

توکن با "alg": "none" یعنی «امضا ندارم». کتابخانه‌های قدیمی یا پیکربندی‌های اشتباه آن را معتبر می‌شمردند، که یعنی هر کسی می‌توانست توکن دلخواه بسازد. همیشه فهرست مجاز الگوریتم‌ها را صریح بدهید و none را هرگز در آن نگذارید.

۲. حمله‌ی algorithm confusion

اگر سرور الگوریتم را از هدر توکن بخواند، مهاجم می‌تواند توکنی با alg: HS256 بسازد و آن را با کلید عمومی سرور (که همه می‌دانند) امضا کند؛ اگر سرور همان کلید عمومی را به‌عنوان «کلید مخفی HMAC» به کار ببرد، امضا معتبر درمی‌آید. راه‌حل همان قاعده‌ی قبلی است: الگوریتم را سمت سرور قفل کنید.

۳. کلید مخفی ضعیف در HS256

امضای HS256 را می‌توان آفلاین حدس زد (brute-force)؛ اگر کلید کوتاه یا قابل‌حدس مثل secret باشد، در چند ثانیه شکسته می‌شود. کلید باید رشته‌ای تصادفی دست‌کم ۳۲ بایتی باشد.

openssl rand -base64 48

۴. گذاشتن داده‌ی حساس در Payload

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

۵. عمر طولانی و نبود راه ابطال

توکنی که یک سال معتبر است، یعنی توکن دزدیده‌شده یک سال کار می‌کند. الگوی رایج: access token با عمر کوتاه (۵ تا ۱۵ دقیقه) + refresh token قابل‌ابطال که در سرور ثبت می‌شود.

۶. نگه‌داری توکن در localStorage

هر اسکریپتی که روی صفحه اجرا شود (یک XSS یا یک وابستگی آلوده) می‌تواند localStorage را بخواند. کوکی با HttpOnly از دسترس جاوااسکریپت خارج است؛ با Secure و SameSite=Lax یا Strict ترکیبش کنید و برای درخواست‌های تغییر وضعیت، حفاظت CSRF را فراموش نکنید.

۷. بررسی نکردن iss و aud

اگر چند سرویس از یک سرور احراز هویت استفاده می‌کنند، توکنِ سرویس A می‌تواند به سرویس B هم ارائه شود؛ مگر اینکه aud مقایسه شود. این یک خطای «فراموش‌شده» است، نه حمله‌ی پیچیده.

۸. استفاده از JWT برای همه‌چیز

اگر اپلیکیشن شما یک سرور و یک پایگاه‌داده دارد، سشن سمت‌سرور ساده‌تر، قابل‌ابطال‌تر و امن‌تر از JWT است. JWT زمانی می‌ارزد که واقعاً چند سرویس مستقل و بدون وضعیت مشترک دارید.

warn بررسی امضا در مرورگر (مثل ابزار آنلاین) فقط برای عیب‌یابی است. تصمیم‌های امنیتی را همیشه در سرور بگیرید.

عیب‌یابی عملی توکن

وقتی API پاسخ 401 می‌دهد، چند سؤال به‌ترتیب جواب می‌دهند چرا:

  • توکن منقضی شده؟ (exp را با زمان فعلی مقایسه کنید.)
  • ساعت سرور و کلاینت ناهماهنگ است؟ (nbf یا iat در آینده است.)
  • aud یا iss با تنظیمات API یکی است؟
  • الگوریتم و kid همان چیزی است که سرور انتظار دارد؟
  • توکن در هدر Authorization: Bearer … بدون کاراکتر اضافی (مثل خط جدید) ارسال شده؟
#Hash Generatorبرای بررسی HMAC و مقایسه‌ی هش‌ها (مثلاً هنگام عیب‌یابی امضای webhook).

جمع‌بندی

امنیت JWT از امضا و اعتبارسنجی درست می‌آید، نه از پنهان‌بودن محتوا: الگوریتم را سمت سرور قفل کنید، iss، aud و exp را بررسی کنید، عمر توکن را کوتاه نگه دارید و برای نگه‌داری آن در مرورگر از کوکی HttpOnly استفاده کنید. اگر API شما پشت Nginx است، راهنمای پیکربندی reverse proxy نحوه‌ی عبور درست هدر Authorization و X-Forwarded-Proto را نشان می‌دهد.

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

آیا JWT رمزنگاری‌شده است؟
نه. JWT معمولی (JWS) فقط امضا شده است؛ هر کسی می‌تواند Header و Payload را بخواند. اگر محتوا باید محرمانه بماند، از JWE استفاده کنید یا اصلاً داده‌ی حساس را در توکن نگذارید.
HS256 بهتر است یا RS256؟
HS256 یک کلید مشترک دارد: هر سرویسی که بتواند توکن را بررسی کند، می‌تواند توکن جعلی هم بسازد. RS256 و ES256 کلید خصوصی (برای امضا) و عمومی (برای بررسی) دارند و برای معماری‌هایی که چند سرویس توکن را بررسی می‌کنند مناسب‌ترند.
توکن را کجا نگه دارم؟
هیچ جایی بی‌ریسک نیست. localStorage با XSS قابل‌سرقت است؛ کوکی HttpOnly از XSS امن‌تر است ولی به محافظت در برابر CSRF (SameSite و توکن CSRF) نیاز دارد. برای وب‌اپلیکیشن‌ها معمولاً کوکی HttpOnly + SameSite انتخاب بهتری است.
چطور JWT را باطل (revoke) کنیم؟
JWT به‌خودی‌خود بی‌حالت است و تا انقضا معتبر می‌ماند. راه‌حل‌ها: عمر کوتاه برای access token به‌همراه refresh token قابل‌ابطال، یا نگه‌داشتن فهرست jti ابطال‌شده در حافظه‌ی سریع (مثل Redis).