توسعه فرانت‌اند

Authentication در React: JWT و Session

هویت و مجوز را از State رابط جدا کنید؛ حفاظت Cookie، خطر LocalStorage، اعتبارسنجی JWT، Refresh، خروج و کنترل دسترسی سرور را بشناسید.

1404/12/15 1 دقیقه مطالعه 1,445 بازدید

صفحه React نام کاربر را نشان می‌دهد و دکمه مدیریت را مخفی کرده است. آیا برنامه امن است؟ هنوز نمی‌دانیم؛ امنیت به این وابسته است که سرور هر درخواست را با هویت و اختیار معتبر بررسی کند. Authentication یعنی تشخیص هویت و Authorization یعنی تصمیم درباره اجازه انجام کار. State در React فقط وضعیت رابط را نگه می‌دارد. کاربر می‌تواند رابط را تغییر دهد یا مستقیم درخواست بفرستد. بنابراین انتخاب میان JWT و Session را نباید به چند خط ذخیره Token در مرورگر خلاصه کرد.

JWT و Session دو مفهوم کاملاً هم‌رده نیستند

Session معمولاً رابطه ورود کاربر با برنامه را مدل می‌کند و می‌تواند با شناسه تصادفی و داده سمت سرور پیاده شود. JWT قالبی برای انتقال Claimهاست و ممکن است در بعضی معماری‌ها بخشی از مدیریت نشست باشد. Cookie نیز روش نگهداری و ارسال داده در مرورگر است، نه رقیب هم‌معنای JWT. می‌توان JWT را در Cookie قرار داد یا شناسه Session را با Cookie فرستاد. تصمیم را با نوع برنامه و تهدیدهایش بگیرید، نه با شعار قدیمی یا مدرن بودن.

راهنمای مدیریت Session در OWASP بر چرخه عمر، حفاظت شناسه و کنترل نشست تأکید دارد. اگر این وظایف روشن نیستند، عوض کردن قالب Token مشکل را حل نمی‌کند. برای یک وب‌اپلیکیشن با سرور هم‌مبدأ، Session مبتنی بر Cookie می‌تواند انتخاب ساده‌تری باشد. برای معماری دیگر شاید نیاز متفاوت باشد. هیچ‌کدام بدون تنظیم و کنترل درست، امنیت آماده و تضمینی نمی‌دهند. مدل تهدید و قرارداد API را پیش از کدنویسی مشخص کنید.

مرورگر چه چیزی را باید بداند؟

رابط معمولاً به وضعیت‌هایی مانند در حال بررسی، واردشده و مهمان نیاز دارد. در آغاز، هنوز پاسخ سرور نرسیده است؛ این حالت را با مهمان قطعی یکی نکنید. اگر نام کاربر در حافظه محلی مانده، آن را شاهد معتبر ورود ندانید. رابط می‌تواند اطلاعات اولیه را برای تجربه بهتر نگه دارد، اما سرور باید اعتبار را تعیین کند. داده شخصی را بیش از نیاز به مرورگر نفرستید و از قرار دادن Secret در Bundle پرهیز کنید.

برای مثال، پس از بارگذاری، برنامه می‌تواند از یک مسیر مشخص مانند endpoint وضعیت کاربر بخواهد. قرارداد باید پاسخ مهمان، کاربر و خطای سرویس را جدا تعریف کند. این یک طرح مفهومی است، نه نمونه کامل API آماده اجرا. خطای شبکه به معنی خروج قطعی نیست و نمایش نام ذخیره‌شده به معنی ورود قطعی نیست. تصمیم درباره صفحه انتظار، تلاش دوباره و حفظ کار ناتمام باید با همین تفاوت‌ها طراحی شود.

ورود امن در سمت سرور

سرور باید داده ورود را دریافت و با روش مناسب بررسی کند. نگهداری رمز، محدودسازی تلاش و چرخه بازیابی نیاز به پیاده‌سازی معتبر دارند. برای محصول واقعی، از امکانات Framework یا سرویس هویت مناسب استفاده کنید و الگوریتم رمزنگاری شخصی نسازید. در صورت موفقیت، شناسه نشست باید مطابق قرارداد ایجاد یا تجدید شود تا Session Fixation کنترل شود. جزئیات امنیتی فقط با دکمه ورود و یک State در فرانت‌اند کامل نمی‌شوند.

پیام ورود ناموفق نباید بی‌دلیل وجود حساب را افشا کند. از طرف دیگر، کاربر باید مسیر قابل فهم برای اصلاح یا بازیابی داشته باشد. در ثبت‌نام، تأیید راه تماس و شرایط فعال شدن حساب را روشن کنید. اطلاعات حساس را در URL قرار ندهید، چون ممکن است در تاریخچه، گزارش و ابزار تحلیل ثبت شود. برای سرویس بیرونی هویت، Callback، مقصد بازگشت و کنترل‌های پروتکل را از کتابخانه و مستندات معتبر بگیرید، نه از نمونه ناقص اینترنتی.

Cookie و کنترل‌های لازم

برای Cookie حساس، ویژگی‌های Secure، HttpOnly و سیاست مناسب SameSite مطرح‌اند. Secure به ارسال روی ارتباط امن مربوط است و HttpOnly خواندن مستقیم توسط JavaScript صفحه را محدود می‌کند. این ویژگی‌ها همه حمله‌ها را حذف نمی‌کنند. Cookie می‌تواند خودکار در درخواست ارسال شود و کنترل CSRF باید با معماری و نوع عملیات هماهنگ باشد. تنظیم دامنه، مسیر و مدت نیز باید محدود و آگاهانه باشد. از نسخه کپی‌شده بدون فهم محیط استفاده نکنید.

هم‌مبدأ بودن، ارتباط امن و سیاست CORS نیز مفاهیم متفاوت‌اند. CORS تصمیم مجوز کاربر برای انجام عملیات نیست و جای کنترل دسترسی را نمی‌گیرد. اگر فرانت‌اند و API مبدأ متفاوت دارند، قرارداد Credentials و Cookie را دقیق بررسی کنید. یک تنظیم توسعه که برای localhost کار می‌کند، الزاماً برای دامنه تولید مناسب نیست. در مستند پروژه، مبدأها و مسئولیت هر کنترل را روشن بنویسید تا تغییر زیرساخت به حذف ناخواسته حفاظت منجر نشود.

چرا LocalStorage پاسخ پیش‌فرض خوبی نیست؟

راهنمای امنیت HTML5 در OWASP درباره نگهداری شناسه نشست در LocalStorage هشدار می‌دهد، چون JavaScript به آن دسترسی دارد. XSS می‌تواند داده قابل دسترسی صفحه را بخواند. SessionStorage نیز از این جهت حفاظت معادل HttpOnly ایجاد نمی‌کند. رمزگذاری Token با کلیدی که در همان کد مرورگر در دسترس است، مسئله را خودکار حل نمی‌کند. باید تهدید و نیاز معماری را بررسی کنید، نه اینکه محل ذخیره را فقط بر اساس راحتی انتخاب کنید.

این توضیح به معنی امن بودن کامل Cookie در برابر XSS نیست. کد مهاجم ممکن است از زمینه صفحه عملیات انجام دهد، حتی اگر مقدار Cookie را مستقیم نخواند. جلوگیری از XSS، کنترل درخواست و حفاظت نشست باید کنار هم باشند. داده غیرقابل اعتماد را به HTML خام تبدیل نکنید و سیاست‌های مناسب محصول را رعایت کنید. نتیجه امنیتی را با یک ویژگی یا محل ذخیره توضیح ندهید؛ زنجیره کنترل‌ها و محدودیت‌ها اهمیت دارند.

JWT به معنی رمزگذاری محتوا نیست

بسیاری از JWTهای رایج امضاشده‌اند و Payload آن‌ها قابل خواندن است. امضا برای بررسی تمامیت و صادرکننده به کار می‌رود و با محرمانه بودن محتوا یکسان نیست. اطلاعات حساس را صرفاً به دلیل وجود امضا داخل Token نگذارید. سرور باید الگوریتم، امضا، زمان‌ها و Claimهای مرتبط مانند صادرکننده و مخاطب را طبق قرارداد بررسی کند. Decode کردن Token در مرورگر، اعتبارسنجی امنیتی نیست و اجازه دسترسی به سرور ایجاد نمی‌کند.

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

Refresh، خروج و ابطال

انقضای کوتاه Access Token می‌تواند بخشی از طراحی باشد، ولی Refresh Token خود نیاز به حفاظت و چرخه عمر دارد. تجدید، چرخش و تشخیص استفاده دوباره باید با سرویس هویت هماهنگ شوند. فرانت‌اند نباید برای هر پاسخ ناموفق، حلقه بی‌پایان تجدید بسازد. درخواست‌های هم‌زمان نیز ممکن است با هم تجدید بخواهند و باید مدیریت شوند. قرارداد خطا و پایان نشست را مشخص کنید تا کاربر در صفحه انتظار نامعلوم نماند.

خروج فقط پاک کردن State رابط نیست. نشست یا مجوز مربوط باید در سمت معتبر مطابق معماری پایان یابد. پاک کردن یک Token از یک تب، الزاماً اعتبار نسخه دزدیده‌شده را لغو نمی‌کند. برای دستگاه‌های دیگر و خروج از همه نشست‌ها، سیاست جدا لازم است. پس از خروج، داده خصوصی Cacheشده و نمایش صفحه قبلی را نیز در نظر بگیرید. کار ناتمام را اگر حفظ می‌کنید، شرایط حریم خصوصی و حساب بعدی روی همان دستگاه را بررسی کنید.

مجوز هر منبع و هر عملیات

کاربر واردشده الزاماً اجازه خواندن همه رکوردها را ندارد. سرور باید بررسی کند رکورد به چه کسی تعلق دارد و عملیات مجاز چیست. تغییر شناسه در URL نباید راه دسترسی به داده دیگری شود. نقش کلی مدیر یا کاربر، همیشه برای همه قواعد کافی نیست؛ مالکیت، سازمان و وضعیت رکورد ممکن است اهمیت داشته باشند. کنترل را نزدیک منبع معتبر اجرا کنید و به مخفی بودن لینک در منو اعتماد نکنید.

در مدیریت فرم React نیز اعتبارسنجی مرورگر از تصمیم سرور جداست. همین مرز در Authentication باقی می‌ماند. حالت نمایشی Route Guard برای تجربه کاربر مفید است، اما امنیت API را تأمین نمی‌کند. اگر مسیر خصوصی در رابط بسته شد ولی endpoint عمومی ماند، مسئله حل نشده است. طراحی را با سناریوهای واقعی کاربر و داده توضیح دهید، نه فقط با یک Component محافظ درخت React.

حالت خطا و تجربه کاربر

پیام 401، 403 و خطای شبکه معنی یکسان ندارند و قرارداد محصول باید روشن باشد. کاربر مهمان شاید نیاز به ورود داشته باشد؛ کاربر فاقد اجازه شاید باید درخواست دسترسی بدهد. اختلال سرویس نباید به خروج اجباری و پاک شدن فرم منجر شود، مگر دلیل مشخص وجود داشته باشد. متن رابط کاربری را با وضعیت واقعی هماهنگ کنید. برای بازگشت پس از ورود، مقصد را کنترل کنید تا مسیر بازگشت به Redirect ناامن تبدیل نشود.

این مقاله عمداً نمونه ورود ناقص با Token ثابت ارائه نمی‌دهد. چنین کدی ممکن است ظاهر یک راه‌حل آماده بسازد، در حالی که سرور، ابطال و حفاظت لازم ندارد. برای پیاده‌سازی، مسیر کامل کتابخانه یا سرویس منتخب را با نسخه و محیط مشخص دنبال کنید. نتیجه حرفه‌ای آن است که هویت، اختیار، چرخه نشست و تجربه خطا با هم تعریف شوند. React باید وضعیت را درست نمایش دهد؛ تصمیم معتبر امنیتی باید در جای درست گرفته شود.

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!