آن چه در این مقاله خواهید خواند:
احراز هویت (JWT, JWS, JWE) چیست؟
احراز هویت کاربران در دنیای دیجیتال بسیار پر اهمیت است. JWT, JWS, JWE 3 ابزار معروف برای تعیین و تایید هویت کاربران در فضای مجازی و وبسایتها هستند. فرآیند احراز هویت کاربر یا همان اعتبار سنجی کاربر، معمولا به 2 شکل انجام میشود. یا از طریق ارسال کوکی به ازای هر درخواست و یا ارسال یک توکن امضا شده در مقابل هر درخواست ارائه شده از سمت کاربر. من ارسلان میربزرگی، در این مقاله در مورد احراز هویت از طریق JWT یا JSON Web Token، JWS یا JSON Web Signature و همچنین JWE یا JSON Web Encryption با شما صحبت خواهم کرد.

روش های اعتبارسنجی کاربر با JWT
گفتیم که فرآیند احراز هویت کاربر یا همان اعتبار سنجی کاربر، معمولا به 2 شکل انجام میشود. شکل اول یا Cookie-Based Authentication، روش پرطرفدارتری است. در این روش پس از هر درخواست کاربر، یک کوکی به منظور احراز هویت کاربر به سرور ارسال میشود و مجددا نیز یک کوکی از سمت سرور برای تایید هویت کاربر، از سرور به سمت کلاینت فرستاده میشود. در شکل دوم یا Token-Based Authentication، پس از هر درخواست کاربر، یک توکن امضا شده به منظور احراز هویت کاربر به سرور ارسال میشود.
مزیت های روش Token-Based Authentication
این روش شاید کم طرفدارتر باشد اما نسبت به روش مبتنی بر کوکی، دارای مزیتهایی است که در ادامه به آنها اشاره کردهایم.
دامنههای متقابل Cross-domain / CORS : CORS و کوکیها، سازگاری زیادی با هم ندارند. علت این امر نیز این است که صدور هر کوکی به دومین صدور آن وابسته است و همچنین استفاده از آن در مابقی دومینها، قابل قبول نیست. در صورتی که در توکنها، صدور اولیه به صدور دومین وابسته نیست و اصالت توکن بر اساس متدهای رمزنگاری تایید میشود.
مقیاس پذیری سمت سرور و بدون حالت بودن :
موجودیت توکن خود شمول (self-contained) است به این معنی که شما در زمانی که با توکنها کار میکنید، نیازی به ذخیره کردن اطلاعات در سشن در سمت سرور ندارید و همه این اطلاعات در کوکی های سمت کلاینت و یا local storage ذخیره میشود.
توزیع کردن برنامه توسط CDN :
زمانی که شما با توکن کار میکنید، میتوانید همه فایلهای برنامه مانند فایلهای جاوا اسکریپت، تصاویر و …. را توسط CDN توزیع کنید. در این وضعیت، کدهای سمت سرور، صرفا یک API معمولی هستند.
عدم در هم تنیدگی کدها در سمت کلاینت و سرور:
اگر از توکن استفاده کنیم، این توکن قابلیت این را دارد که در هر برنامهای و در هر مکان صادر شود و نیازی به ایجاد هیچ وابستگی بین کدهایی که در سمت سرور هستند با کدهای سمت کلاینت نیست.
سازگاری بهتر توکن با سیستم عامل های تلفن همراه :
اگر بخواهید برنامههای مربوط به سیستم عاملهای مختلف تلفن همراه را توسعه دهید، بهتر از توکن به جای کوکی استفاده کنید. زیرا کوکیها در هنگام کار با APIهایی که در سمت سرور هستند، عملکرد مطلوبی ندارند و در این حالت، توکنها بهتر عمل میکنند.
باگ CSRF:
به دلیل استفاده شما از توکن به جای کوکی، حملات CSRF دیگر اتفاق نخواهند افتاد. چون عملا دیگر کوکی به طرف سرور ارسال نخواهد شد که امکان هرگونه سوء استفاده از کوکی احراز هویت شده برای صدور درخواستهای مشابه با درخواست فردی که به سایت لاگین کرده، وجود داشته باشد.
عملکرد بهتر:
توکنها به دلیل ماهیت خود شمولی که دارند، نیاز زیادی به رفت و برگشت به بانک اطلاعاتی ندارند از این رو، سرعت عملیات با استفاده از آنها افزایش مییابد.
فراهم کردن امکان نوشتن آزمونهای یکپارچگی سادهتر:
وقتی که از توکنها استفاده میکنید، برای اجرای هیچ یک از آزمونهای یکپارچگی در برنامه شما، نیازی نیست که از صفحه لاگین عبور کنند و بسیار آسان تر از حالات دیگر اجرا خواهند شد.
استاندارد بودن :
استفاده از JWT برای شما، به منزله امکان کار کردن با انواع مختلف کتابخانهها و پلتفرم ها خواهد بود.

JWT چیست؟
اگر بخواهیم JWT را به شکل کلی توضیح دهیم، باید بگوییم که JWT ، نوعی استاندارد وب است. این استاندارد یک راه فشرده و خود شمول را برای انتقال اطلاعات به صورت امن و بین مقاصد متفاوت و با استفاده از یک شی JSON معرفی میکند. اطلاعات انتقالی، قابل اطمینان میباشند زیرا تمامی آنها، امضای دیجیتال دارند. تمامی JWT ها، به وسیله یک جفت کلید عمومی و خصوصی که به وسیله الگوریتم RSA ایجاد میشوند و یک کلید خصوصی که به وسیله الگوریتم HMAC ایجاد میشود، میتوانند امضا شوند. اگر بخواهیم در مورد واژههای خود شمول و فشرده، بیشتر توضیح دهیم، می توانیم اینگونه بگوییم که :
خود شمول به این معنا است که در این توکن، payload یا بار مفید شامل همه اطلاعاتی است برای اعتبارسنجی و احراز هویت یک کاربر مورد نیاز است. در این حالت صرفا یک مرتبه از بانک اطلاعاتی، کوئری گرفته میشود.
فشرده بودن بودن نیز به این معنی است که شی JSON در JWT، دارای اندازه کوچکی است. در نتیجه به آسانی از طریق یک HTTP Header یا پارامترهای POST و یا به وسیله یک URL میتواند ارسال شود و فرایند ارسال آن نیز به دلیل کوچک بودن اندازه شی JSON، سریعتر خواهد بود.
در فارسی به روش JWT، جوت نیز گفته میشود. تمامی جوتها از سه قسمت تشکیل شدهاند که هر کدام از این قسمتها به وسیله یک نقطه از بقیه جدا شدهاند. مثل xxxxx.yyyyy.zzzzz . این سه قسمت عبارتند از قسمت header، قسمت payload و قسمت signature که هر کدام توسط الگوریتم Base64، اینکد خواهند شد. در زیر میتوانید نمونه ای از یک توکن JWT را مشاهده کنید.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I
kpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o
قسمت Header
این بخش، خود شامل دو قسمتی است که بر اساس آن نوع توکن و نوع الگوریتمی که توکن استفاده کرده، مشخص میشود. در اینجا نوع توکن ما JWT است. الگوریتمهایی که توکن استفاده کرده نیز HMAC SHA256 و RSA است. در زیر، اطلاعات دیکد شده از قسمت هدر را مشاهده میکنید.
{ "alg": "RS256", "typ": "JWT" }
قسمت payload
این قسمت، شامل claims میباشد. claims، اطلاعاتی هستند که در رابطه با موجودیت مورد نظر ما، به طور مثال کاربر و همچنین مقداری متا دیتای اضافی است. claims سه نوع است که شامل :
نوع اول Reserved claims:
این نوع شامل مقداری اطلاعات مفید و همچنین اطلاعات از پیش مشخص شده و غیر اجباری است. اطلاعاتی مانند exp یا تاریخ انقضا، iss یا صادر کننده، sub یا subject (عنوان) و aud یا audience (مخاطب).
نوع دوم Public claims :
این نوع شامل اطلاعاتی است که قبلا به وسیله IANA JSON Web Token Registry ثبت شده و فضاهای نامگذاری آنها با یکدیگر،تداخلی ندارند.
نوع سوم Private claims:
این نوع شامل درخواست سفارشی است که برای انتقال دادن دادهها بین مقصدهای گوناگون استفاده میشود.
در زیر، اطلاعات اینکد شده از قسمت payload را مشاهده میکنید.
{ "exp": 1520507268, "sub": 582946, "name": "ErFUN KH", "admin": true }
در این نمونه، 2 دیتایی که در اول کد آورده شدهاند، از نوع Reserved claims هستند. همچنین 2 دیتای آخر نیز از نوع Private claims میباشند.
payload از نوع Reserved claims شامل موارد زیر است :
بخش iss یا issuer :
در این بخش، باید صادر کننده توکن مشخص شود. به طور مثال میتوانید مقدار “hesabfun.com” را برای آن تعیین کنید.
بخش sub یا subject :
در این بخش، موضوع اصلی توکن مشخص میشود. به طور مثال، موضوع توکن ما مربوط به شناسایی کاربران است. میتوانیم به این منظور، از قرار دادن آیدی کاربران در توکن استفاده کنیم. از این طریق متوجه خواهیم شد که توکن ارسالی از سمت کدام کاربر است.
بخش aud یا audience :
در این بخش، مکان استفاده توکن مشخص میشود. بخش audience ، بیشتر در موقعی که چند سرور داشته باشیم و همگی یک کلید خصوصی برای امضای دیجیتال داشته باشند، استفاده میشود. به طور مثال میتوانید مقدار “https://blog.hesabfun.com” را برای آن تعیین کنید.
بخش exp یا expiration:
در این بخش، زمان اعتبار توکن مشخص میشود. این زمان باید به شکل Unix time مشخص شود.
بخش nbf یا not before:
در این بخش، این موضوع که از چه زمانی پردازش توکن امکان خواهد بود، مشخص میشود.
بخش iat یا issuedAt:
در این بخش، باید تاریخ ساخت توکن به شکل Unix time آورده شود.
بخش jti یاid JWT :
در این بخش، برای هر توکن، یک آیدی انحصاری مشخص میشود. این مورد هم در سیستم مدیریت توکن پیام رسان تلگرام و هم در لیست مایکروسافت وجود دارد. بهتر است در این حالت، کل آیدی در داخل دیتابیس آپلود نشود و صرفا sub و exp مورد استفاده قرار گیرد.
بخش signature
تا به اینجای کار، مشاهده کردید که تمامی دیتاهایی که دارای الگوریتم 64 بودند، اینکد شدند و به آسانی در هر سیستمی نیز دیکد میشوند و هر شخص قادر به ساخت توکن و ارسال آن البته بدون امضا خواهد بود.

همانطوری که در عکس مشاهده میکنید، قسمت Header و قسمت payload با یکدیگر جمع شده و به وسیله یک کلید خصوصی که صرفا در سرور قرار دارد، رمزگذاری میشود. کلی این بخش برای بخش امضا یا signature مورد استفاده قرار میگیرد. اطلاعات موجود در بخش payload، نباید تغییر داده شوند چرا که در صورت هر گونه تغییر، دیگر امضا برای سرور اعتباری ندارد و از آنجایی که کاربر به کلید خصوصی دسترسی ندارد، قادر به تولید توکن نخواهد بود. دقت داشته باشید که اطلاعات حیاتی مانند رمز و … نباید در داخل توکن گذاشته شود زیرا به راحتی قابل خوانده شدن است اما در صورتی که ضرورتی به این کار بود، لازم است تا حتما از JWT استفاده شود.
طریقه استفاده از JWT در برنامهها
وقتی که کاربر، توسط یک لاگین موفق به سیستم وارد شود، سرور یک توکن امن را با فرمت JWT صادر کرده و به سمت کاربر ارسال میکند. این توکن لزوما باید به شکل محلی و در سمت کلاینت ذخیره شود که معمولا این کار در local storage انجام میشود. توصیه میشود از کوکی استفاده نکنید زیرا در ادامه توضیح میدهیم که کوکیها از لحاظ امنیتی مشکل دارند. پس از ذخیره این توکن، دیگر هیچ سشنی در سرور برای هر کاربر ساخته نمی شود و هیچ کوکی نیز از سرور به سمت کاربر فرستاده نخواهد شد.
در این حالت اگر کاربر بخواهد به یک محتوا یا صفحه محفوظی دسترسی پیدا کند، لازم است تا توکن خود را به سمت سرور بفرستد. این کار به وسیله یک header سفارشی Authorization، به همراه Bearer schema انجام میشود که مشابه نمونه زیر است :
Authorization: Bearer
این شیوه احراز هویت، بدون حالت stateless میباشد. علت این موضوع نیز این است که وضعیت کاربر هرگز در سمت سرور ذخیره نمیشود. در واقع API مربوط به سمت سرور، در مرحله اول هدر Authorization فوق را در درخواست دریافتی جستجو میکند. اگر این جستجو موفقیت آمیز بود و همچنین صحت آن مورد تایید واقع شد، به کاربر اجازه دسترسی به منبع محافظت شده داده میشود. همانطور که در بخشهای قبلی نیز توضیح دادیم، به علت خود شمول بودن توکنها، همه اطلاعات مورد نیاز برای صدور اجازه دسترسی به کاربر، در توکن قرار دارد و نیازی به مراجعه رفت و برگشتی به بانک اطلاعاتی نیست و همین موضوع، سرعت کار را بالا میبرد.

محل ذخیره JWT
معمولا JWT ها در local storage هر مرورگر ذخیره میشوند. این مورد در اغلب موارد کارآمد است اما باید در نظر داشته باشید که local storage در واقع نوعی sandbox میباشد که به دومین جاری برنامه محدود است و مثلا از طریق زیر دامنههای آن قابل دسترسی نمیباشد. در این صورت بهتر است تا JWT ها را در کوکیها ذخیره کنید. علت این کار این است که کوکیها در سمت کلاینت هم ذخیره میشوند و محدودیت ناشی از ذخیره JWT ها در local storage هر مرورگر را ندارند. البته باید این موضوع را در نظر بگیرید که حجم کوکیها، حداکثر میتواند 4 کیلوبایت باشد و اگر claims ذخیره شده در یک JWT افزایش یابد و انکد شود، این حجم بیشتر از 4 کیلوبایت خواهد شد. البته میتوانید JWT ها در session storage هر مرورگر نیز ذخیره کنید. تنها مشکلی که در این مورد وجود دارد، این است که به محض بسته شدن مرورگر، این توکنها پاک خواهند شد. اگر شما از local storage برای ذخیره JWT ها استفاده کنید، دیگر حملات Cross Site Request Forgery، تاثیری ندارند. در صورتی که اگر کوکیها به منظور محل ذخیره توکنها استفاده کنید، این حملات امکان پذیر خواهند بود. راه حل این موضوع این است که تاریخ انقضا توکنها را تا حد امکان، نزدیک در نظر بگیرید که در صورت افشا شدن، سریع تر قابل استفاده شود.

انقضا و صدور دوباره توکنها
اگر توکنها بدون حالت باشند، می توانند فقط بر اساس چک کردن امضای هر پیام، فعالیت کنند. یعنی این فعالیت ممکن است تا ابد ادامه پیدا کند. این موضوع معمولا یک مشکل تلقی میشود و برای حل آن لازم است تا یک تاریخ انقضا و یا exp برای توکن مشخص شود. اگر برنامه شما حساس باشد، این تاریخ انقضا میتواند 15 دقیقه یا کمتر باشد. و در صورتی که برنامه حساسیت کمتری داشته باشد، این زمان تا چند ماه نیز قابل افزایش است. شاید برایتان سوال باشد که اگر بخواهیم توکنی را در لحظه غیرفعال کنیم، چه کاری باید انجام دهیم؟ در پاسخ باید بگویم که یکی از راههایی که میتوانید این کار را انجام دهید این است که رکوردهای تمام توکنها را در یک بانک اطلاعاتی ذخیره کنید. میتوانید برای این کار از یک فیلد jti استفاده کنید. در این حالت، id ها در بانک اطلاعاتی ذخیره خواهند شد. شما قادر خواهید بود تا بین کاربران و اطلاعات آنها و توکنهای صادر شده، ارتباط برقرار کنید. با این کار، برنامه علاوه بر اینکه امضا توکن را بررسی میکند، قادر خواهد بود تا به لیست این id ها نیز مراجعه کند و احراز هویت اضافه تری برای ابطال سریع تر توکن ها پیاده سازی کند.
چطور میتوانیم بیشترین میزان امنیت JWTها را تامین کنیم؟
برای این کار لازم است تا :
- همه توکنهای خودتان را به وسیله یک کلید قوی امضا کنید. دقت داشته باشید که این کلید باید فقط بر روی سرور ذخیره شده باشد نه جای دیگری. در این حالت، زمانی که سرور، یک توکن را از سمت کاربر دریافت میکند، اعتبار امضای پیام رسیده به سرور، توسط سرور و بر اساس کلید قوی موجود بر روی آن سنجیده میشود.
- در صورتی که اطلاعات حساس و خاصی را در توکنها ذخیره کرده اید، باید JWE یا JSON Web Encryption را مورد استفاده قرار دهید. علت این کار این است که JWT ها فقط امضای دیجیتالی دارند و رمزنگاری نشده اند.
- برای امنیت بیشتر، لازم است تا توکنها را صرفا از طریق پروتکل HTTPS، ارسال کنید.
- اگر برای ذخیره سازی توکنها، از کوکیها استفاده میکنید. باید برای در امان ماندن از شر حملات Cross-Site Scripting XSS ، صرفا از Secure استفاده کنید.
- تاریخ انقضای توکنهای صادر شده را به شکل اصولی و منطقی در نظر بگیرید.

و در آخر
وبسایت میربزرگی قصد دارد تا با ارائه مقاله ها و تجربههای کاربردی شما را در زمینه یادگیری و رفع اشکالاتتان کمک کند. در صورت وجود هرگونه سوالی به من ایمیل بزنید.
خیلی عالی
خیلی ممنون
لطفا ایمیلتون روچکمیکنین؟
بله حتما