خلاصه اولیه : بسیاری از وباپلیکیشنهای مدرن از JSON Web Token(JWT) به جای احراز هویت بر پایه session سنتی استفاده میکنند. چندین چالش در استفاده از sessionهای سمت سرور در برنامههای امروزی پیدا شدهاند. در این پست، ما این چالشها را تشخیص داده، و نحوه کار JWT و sessionها را در عمل توضیح خواهیم داد.
Web Token JSON چه هستند؟
JSON Web Token یک استاندارد باز (RFC 7519) است که یک راه فشرده و خودمختار را برای انتقال اطلاعات میان اشخاص به صورت امن، به عنوان یک آبجکت JSON فراهم میکند. این اطلاعات میتوانند تایید شده، و به آنها اطمینان شود؛ زیرا به طور دیجیتال نشانهگذاری شدهاند. JWTها میتوانند با استفاده از یک کلید خصوصی (با الگوریتم HMAC) یا عمومی، و از طریق RSA نشانهگذاری شوند.
آناتومی JWT
JWTها اساسا از سه بخش جدا شده توسط یک نقطه تشکیل میشوند. این سه بخش header، payload و signature میباشند.
Web Token JSON
در احراز هویت، وقتی که کاربر با موفقیت با استفاده از مدارک خود وارد میشود، یک JSON Web Token برگردانده میشود، و باید به جای روش قدیمی ساخت یک session در سرور و برگرداندن یک کوکی، به صورت داخلی (معمولا در حافظه داخلی، اما از کوکیها هم میتوان استفاده کرد) ذخیره شود.
هر زمان که کاربری میخواهد به یک route محافظت شده دسترسی داشته باشد، باید JWT را (معمولا در header احراز هویت (Authorization) و با استفاده از طرح Bearer) ارسال کند. از این رو، محتویات header باید به این صورت باشند:
Authorization: Bearer <token>
از آنجایی که در این مکانیزم، کاربر هیچ وقت در حافظه سرور ذخیره نمیشود، این یک مکانیزم احراز هویت بدون state (stateless) میباشد. Route های حفاظت شده سرور به دنبال یک JWT معتبر در header احراز هویت میگردند، و اگر این route وجود داشته باشد، به کاربر اجازه داده میشود. از آنجایی که JWTها خود مختار هستند، تمام اطلاعات ضروری آنجا بوده، و باعث کاهش نیاز به رفت و برگشت به دیتابیس میشوند.
این باعث میشود که کاربر بتواند کاملا به APIهای داده که stateless هستند تکیه کند و حتی درخواستهایی به سرویس downstream ارسال کند. از آنجایی که اشتراک منبع میان ریشهای (CORS = Cross-Origin Resource Sharing) با توجه به این که از کوکیها استفاده نمیکند مشکلی ایجاد نخواهد کرد، مهم نیست که کدام دامنهها به API شما خدمت میکنند.
چرا باید از JSON Web Token استفاده کنیم؟
دلایل زیادی وجود دارند که از JSON Web Token استفاده کنید:
- به راحتی میتوان آنها را به صورت افقی مقیاسبندی کرد.
- نگهداری و خطایابی آنها سادهتر است.
- قابلیت ساخت سرویسهای RESTful را دارند.
- عملکرد انقضای داخلی دارند.
- نشانههای وب JSON خودمختار هستند.
نکاتی که در بالا اشاره شدند، در بخش بعدی به همراه جزئیات توضیح داده شدهاند.
JWT علیه Session ها
قبل از پدیدار شدن نشانههای وب JSON، باید به احراز هویت سمت سرور مسلط میشدیم. همانطور که همه ما میدانیم، پروتکل HTTP بدون state است، و این یعنی این که اگر ما یک کاربر را با استفاده از یک نام کاربری و رمز عبور احراز هویت کنیم و به درخواست بعدی برویم، برنامه ما نخواهد دانست که ما چه کسی هستیم. ما مجبور خواهیم بود که مجددا احراز هویت کنیم. پس نیاز بود که تضمین کنیم پس از وارد شدن یک کاربر، وضعیت احراز هویت کاربر همچنان میتواند بر روی درخواست HTTP بعدی تایید شود.
مدارک یک کاربر به عنوان یک درخواست POST به سرور ارسال میشوند. سرور، کاربر را احراز هویت میکند. اگر مدارک کاربر معتبر باشند، سرور با یک کوکی پاسخ میدهد، که بر روی مرورگر کاربر تنظیم شده است و شامل یک آیدی session برای تشخیص کاربر است. در این بخش، چند نکته را توضیح خواهم داد که به عنوان یک پایه برای مقایسه JWTها با session ها در عمل استفاده میشوند.
1. مقیاس پذیری
همینطور که برنامه شما بزرگتر میشود و مرکز کاربر شما افزایش مییابد، شما باید شروع به مقیاس بندی، به صورت افقی یا به صورت عمودی نمایید. دادههای session در حافظه بر روی سرور، به صورت فایل یا در یک دیتابیس ذخیره شدهاند. در یک سناریو مقیاسبندی افقی که در آن باید شروع به تکثیر سرورها نمایید، شما باید یک سیستم منبع session مرکزی جداگانه فراهم کنید که تمام سرورهای برنامه شما به آن دسترسی دارند. در غیر این صورت، شما به علت مشکل session - منبع نخواهید توانست که برنامه خود را مقیاسبندی کنید. یک راه دیگر برای رفع این چالش، این است که مقیاسبندی برنامه خود را در محیط ابری آسانتر کنید. این نوع راه حلها با برنامههای عظیم مدرن، خوب کنار نمیآیند. راهاندازی و نگهداری یک سیستم توزیع شده، شامل دانش فنی عمقی میشود و به طور متعاقب هزینههای بیشتری را به همراه دارد.
در این مورد استفاده از JWTها یکپارچه است؛ از آنجایی که احراز هویت بر پایه نشانه، بدون state است، نیازی به ذخیره اطلاعات کاربر در session نیست. برنامه ما میتواند به راحتی مقیاسبندی شود؛ زیرا میتوانیم از نشانهها برای دسترسی به منابع از سرورهای مختلف استفاده کنیم، بدون این که نگران باشیم که کاربر بر روی یک سرور مشخص وارد شده است، یا نه. همچنین با توجه به این که به یک سرور مختص ذخیره sessionها نیاز نخواهید داشت، از صرف هزینهها هم جلوگیری خواهید کرد. چرا؟ زیرا هیچ sessionای وجود ندارد!
نکته: اگر در حال ساخت برنامههای کوچکی هستید که نیازی به مقیاس بندی برای اجرا بر روی چند سرور ندارند و نیازی هم به اِیپیآیهای RESTful ندارند، sessionها قطعا برای شما به خوبی کار خواهند کرد. و اگر میتوانید از یک سرور مختص برای اجرای ابزاری مانند Redis برای مخزن session خود استفاده کنید، sessionها هم میتوانند به خوبی برای شما کار کنند.
2. امنیت
نشانهگذاری JWTها از پیش بر روی جلوگیری از مداخله بر روی سمت کاربر هدفگیری کردهاند، اما همچنین میتوانند رمزنگاری هم بشوند، تا تضمین شود که نشانهها امن هستند. حال JWTها اکثرا یا مستقیما در مخزن وب (مخزن local / session) ذخیره شدهاند، یا در کوکیها. JavaScript به مخزن وب بر روی دامنه مشابه دسترسی دارد. این به سادگی یعنی این که JWTهای شما ممکن است در مقابل XSS (Cross-site Scripting = اسکریپتنویسی میان وبسایتی) آسیبپذیر باشند. JavaScript مخرب میتواند بر روی یک صفحه وب پیادهسازی شود، تا خوانده شده و محتویات مخزن وب شما را در خطر بیندازند. در واقع، بسیاری از مردم مدافع این هستند که دادههای حساس شما، به علت حملات XSS نباید در مخزن وب ذخیره شوند. یک مثال معمولی، تضمین این است که JWTهای شما با هر داده حساس یا مطمئنی مانند شماره امنیت اجتماعی کاربر انکود نشوند.
در ابتدا اشاره کردم که JWTها میتوانند در کوکیها ذخیره شوند. در واقع، JWTها در بسیاری از موارد به عنوان کوکیها ذخیره شدهاند، و کوکیها در مقابل حملات CSRF (Cross-site Request Forgery = جعل درخواست میان وبسایتی) آسیبپذیر / حساس هستند. یکی از چندین روش برای جلوگیری از حملات SRF، این است که تضمین کنید کوکیهای شما فقط از طریق دامنه شما قابل دسترسی هستند. به عنوان یک توسعه دهنده، تضمین کنید که محافظتهای CSRF ضروری، بدون توجه به استفاده از JWTها و در جهت جلوگیری از این حملات، در جای خود قرار گرفتهاند.
حال، JWTها و آیدیهای session میتوانند در معرض حملات بازپخش کاهش یافته قرار بگیرند. این که کدام تکنیکهای بازپخش کاهش یافته برای سیستمهای مورد نظر مناسب هستند، کاملا به توسعه دهندگان بستگی دارد. یکی از راههای رفع این مشکل، این است که تضمین کنید JWTها تاریخ انقضای کوتاهی دارند. البته، این تکنیک به طور کامل مشکل را رفع نمیکند. گرچه دیگر روشهای جایگزین برای برطرف کردن این چالش، استفاده از JWTها به IPهای مشخص و استفاده از اثر انگشت مرورگر (Browser Fingerprint) هستند.
نکته: از HTTPS / SSL استفاده کنید، تا تضمین کنید که کوکیها و JWTهای شما به طور پیشفرض در طی انتقالات کلاینت و سرور رمزنگاری شدهاند.
3. سرویسهای RESTful اِیپیآی
یک الگوی رایج برای برنامههای مدرن، این است که دادههای JSON را از یک اِیپیآی JSON دریافت کنیم. امروزه اکثر برنامهها اِیپیآیهای RESTful برای توسعه دهندگان یا برنامههای دیگر دارند. Serve کردن دادهها از یک API ،چندین برتری متمایز دارد. یکی از آنها، قابلیت این است که دادهها در بیش از یک برنامه استفاده شوند. روش سنتی استفاده از sessionها و کوکیها برای هویت کاربر، در این مورد خوب کار نمیکند؛ زیرا state را به برنامه معرفی میکند.
یکی از فرضیههای اِیپیآی RESTful این است که بهتر است بدون state باشد، که یعنی وقتی که یک درخواست ارسال میشود، یک پاسخ در پارامترهایی خاص میتواند همیشه بدون اثرات جانبی پیشبینی شود. State احرای هویت یک کاربر، موجب چنین اثر جانبیای میشود، که این اصل را میشکند. بدون state نگه داشتن API و در نتیجه بدون اثر جانبی بودن به این معنی است که نگهداری و خطایابی بسیار سادهتر شده است.
یک چالش دیگر در اینجا این است که یکAPI از یک سرور serve شود، و از یک سرور دیگر توسط برنامه اصلی دریافت شود. برای ممکن کردن این مسئله، باید CORS را فعال کنیم. از آنجایی که کوکیها فقط میتوانند برای دامنهای که از آن منشا میگیرند استفاده شوند، کمک زیادی برای APIها بر روی دامنههای مختلف نسبت به برنامه نیستند. استفاده از JWTها در این مورد، تضمین میکند که اِیپیآی RESTful، بدون state است، و شما همچنین نیازی نیست که نگران این باشید که API یا برنامه از کجا serve میشود.
4. کارایی
یک تجزیه و تحلیل حیاتی برای این مورد، ضروری است. وقتی که از کلاینت به سرور یک درخواست را ارسال میکنید، اگر دادههای زیادی داخل JWT انکود شده باشند، میزان قابل توجهی داده اضافی به همراه هر درخواست HTTP ارسال میکند. گرچه با استفاده از sessionها، فقط مقدار کمی از آنها موجود است؛ زیرا آیدیهای session در واقع بسیار کوچک هستند. به این مثال نگاه کنید:
یک JWT، پنج claim دارد:
{
"sub": "1234567890",
"name": "Prosper Otemuyiwa",
"admin": true,
"role": "manager",
"company": "Auth0"
}
وقتی که JWT انکود میشود، حجم JWT چندین برابر حجم آیدی session میشود، و از این رو این JWT را میسازد و مقدار بیشتری داده از یک آیدی session با هر درخواست HTTP اضافه میکند. با استفاده از sessionها، همچنین جستجوی سمت سرور برای پیدا کردن و نابود کردن session در هنگام هر درخواست وجود دارد.
JWTها با نگه داشتن دادهها در سمت کاربر، تاخیر موجود را جبران میکنند مدل دادههای برنامه شما، در اینجا یک فاکتور قابل توجه است؛ زیرا با جلوگیری از فراخوانیهای بی وقفه و کوئریهای دیتابیس بر روی سرور ذخیره میشود. در اینجا، هدف ما این است که مراقب باشیم تا claimهای بیش از حدی در یک JWT ذخیره نکنیم، و از درخواستهای عظیم جلوگیری کنیم.
این مسئله که نشانهها ممکن است نیاز به دسترسی به دیتابیس بر روی backend را داشته باشند، ارزش اشاره را دارد. این مسئله، مورد به خصوصی برای نشانههای refresh است. این نشانهها ممکن است نیاز به دسترسی به یک دیتابیس بر روی سرور احراز هویت برای blacklist کردن داشته باشند.
نکته: توسعه دهندگان باید به تعادلی برسند، تا استفاده از JWTها را به صرفه نمایند.
5. سرویسهای Downstream
یک الگوی رایج دیگر که در وباپلیکیشنهای مدرن دیده میشود، این است که آنها معمولا به سرویسهای Downstream تکیه میکنند. برای مثال، یک فراخوانی به سرور اصلی برنامه که ممکن است قبل از این که درخواست resolve شود، نیاز به چند درخواست به یک سرور downstream داشته باشد. در اینجا مشکل این است که کوکیها به راحتی به سرورهای downstream جریان پیدا نمیکنند و نمیتوانند سرورهای مربوط به state احراز هویت کاربر را تشخیص دهند. از آنجایی که هر سرور طرح منحصر به فرد خود برای کوکیها را دارد، مقاومت زیادی در برقراری جریان وجود دارد و اتصال به آنها سخت است. باز هم JSON Web Token این مسئله را ساده میکنند.
احراز هویت با Auth0 با استفاده از JWTها
در Auth0، ما JWTها را به عنوان نتیجهای از روند احراز هویت خارج میکنیم. وقتی که کاربر با استفاده از Auth0 وارد میشود، یک JWT ساخته میشود، نشانهگذاری میشود، و به کاربر ارسال میشود. Auth0 نشانهگذاری JWT با الگوریتمهای HMAC و RSA را پشتیبانی میکند. کاربر انعطاف مورد نیاز برای انتخاب هر کام از این دو الگوریتم را از داشبورد دارد. این نشانه بعدا برای احراز هویت و مجوزدهی با APIها استفاده خواهد شد، که دسترسی به routeها و منابع محافظت شده آنها را فراهم میکند.
ما همچنین از JWTها برای اجرای احراز هویت و مجوزدهی در نسخه 2 اِیپیآی Auth0 استفاده میکنیم، و استفاده سنتی از کلیدهای API مات را جایگزین میکنیم. با توجه به احراز هویت، نشانههای وب JSON امنیت دانهای را میسر میسازند، که در واقع قابلیت مشخص کردن مجوزهایی خاص در نشانه، و ارتقای خطایابی میباشد.
نتیجه گیری
JSON Web Token سبک بوده، و میتوانند به سادگی در میان پلتفرمها و زبانها استفاده شوند. JWTها روش هوشمندانهای برای احراز هویت و مجوزدهی بدون sessionها هستند. چندین کتابخانه JWT برای نشانهگذاری و تایید نشانهها وجود دارند. همچنین دلایل زیادی هم برای استفاده از نشانهها وجود دارد، و Auth0 میتواند در پیادهسازی احراز هویت نشانه به روشی ساده و امن کمک کند. به شخصه فکر نمیکنم که یک روش در همه موارد مناسب است. همه چیز به ساختار برنامه شما و موارد استفاده از آن بستگی دارد.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید