افزایش امنیت در احراز هویت
کلمه PKCE مخفف Proof Key for Code Exchange("پیکسی") یک افزونه امنیتی برای پروتکل OAuth 2.0 است که بهطور خاص برای افزایش امنیت در سناریوهای کلاینتهای عمومی (Public Clients) مانند برنامههای موبایل، برنامههای تکصفحهای (Single Page Applications) یا برنامههای دسکتاپ طراحی شده است. این مکانیزم برای جلوگیری از حملات تزریق کد احراز هویت (Authorization Code Interception Attack) استفاده میشود.
چالش موجود چی هستش؟
- جریان Authorization Code Grant یکی از رایجترین جریانهای OAuth 2.0 است که برای برنامههایی که میتوانند راز کلاینت (Client Secret) را بهصورت امن ذخیره کنند (مانند برنامههای سمت سرور) طراحی شده است.
- مراحل کلی این جریان:کلاینت (برنامه که درخواست دسترسی به اطلاعات کاربر رو داره) کاربر را به سرور احراز هویت (Authorization Server) هدایت میکند تا اجازه دسترسی بدهد.
سرور احراز هویت یک کد احراز هویت (Authorization Code) تولید میکند و آن را از طریق پارامتر redirect_uri به کلاینت بازمیگرداند.
کلاینت این کد را به همراه شناسه کلاینت (Client ID) و راز کلاینت (Client Secret) به سرور احراز هویت ارسال میکند تا توکن دسترسی (Access Token) دریافت کند.
- کلاینتهای عمومی (مانند اپلیکیشنهای موبایل یا SPAها) نمیتوانند راز کلاینت را بهصورت امن ذخیره کنند، زیرا کد منبع آنها (مانند جاوااسکریپت در مرورگر) در دسترس است یا میتوان آن را از دستگاه استخراج کرد.
- بدون راز کلاینت، هر برنامه مخربی که بتواند کد احراز هویت (code) را رهگیری کند (مثلاً از طریق redirect_uri جعلی یا شنود)، میتواند آن را به سرور احراز هویت ارسال کرده و توکن دسترسی دریافت کند.
- این آسیبپذیری به حمله تزریق کد احراز هویت معروف است.
یک لایه امنیتی اضافی اضافه میکند تا اطمینان حاصل شود که تنها کلاینتی که درخواست اولیه را شروع کرده است، میتواند کد احراز هویت را برای دریافت توکن دسترسی استفاده کند. این کار با استفاده از یک جفت کلید موقت (Code Verifier و Code Challenge) انجام میشود که در ادامه توضیح میدهیم.
یک رشته تصادفی با امنیت بالا (High-Entropy) است که توسط کلاینت تولید میشود.
طول آن باید بین 43 تا 128 کاراکتر باشد و میتواند شامل حروف، اعداد و برخی کاراکترهای خاص (مانند -, ., _, ~) باشد.
مثال: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
- شماره دو (Code Challenge)
یک تبدیل رمزنگاریشده از Code Verifier است که به سرور احراز هویت ارسال میشود.
دو روش برای تولید Code Challenge وجود دارد:
Plain: Code Challenge = Code Verifier (بدون تغییر).
S256: Code Challenge = Base64URL(SHA256(Code Verifier)).
- شماره سه (Code Challenge Method)
مشخص میکند که Code Challenge چگونه تولید شده است (plain یا S256).
سرور احراز هویت از این اطلاعات برای اعتبارسنجی استفاده میکند.
روش S256 به دلیل امنیت بالاتر ترجیح داده میشود.
- مقدارCode Verifier فقط در کلاینت ذخیره میشود و هرگز به سرور ارسال نمیشود (جلوگیری از افشا).
- مقدار Code Challenge به سرور ارسال میشود و بهعنوان یک "چکپوینت" عمل میکند تا سرور بتواند تأیید کند که کلاینتی که کد احراز هویت را ارائه میدهد، همان کلاینتی است که درخواست اولیه را شروع کرده است.
مرحله 1: تولید Code Verifier و Code Challenge
- کلاینت یک Code Verifier تصادفی تولید میکند.
- کلاینت از Code Verifier یک Code Challenge تولید میکند:اگر روش plain باشد: Code Challenge = Code Verifier.
اگر روش S256 باشد: Code Challenge = Base64URL(SHA256(Code Verifier)).
کلاینت Code Verifier را بهصورت محلی ذخیره میکند.
- کلاینت کاربر را به سرور احراز هویت هدایت میکند و پارامترهای زیر را در URL درخواست اضافه میکند:
- response_type=code (مشخصکننده جریان کد احراز هویت).
client_id (شناسه کلاینت).
redirect_uri (آدرس بازگشت).
scope (دامنه دسترسیهای موردنظر).
state (برای جلوگیری از حملات CSRF).
code_challenge (Code Challenge تولیدشده).
code_challenge_method (معمولاً S256 یا plain).
GET https://authorization-server.com/auth? response_type=code&client_id=your_client_id&redirect_uri=https://yourapp.com/callback&scope=user_profile&state=xyz123&code_challenge=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk&code_challenge_method=S256
- کاربر در سرور احراز هویت لاگین میکند و دسترسی را تأیید میکند.
- سرور احراز هویت یک کد احراز هویت تولید میکند و آن را همراه با state به redirect_uri کلاینت بازمیگرداند
https://yourapp.com/callback?code=abc123&state=xyz123
- سرور احراز هویت Code Challenge و Code Challenge Method را برای استفاده در مرحله بعدی ذخیره میکند.
- کلاینت کد احراز هویت را به همراه Code Verifier به سرور احراز هویت ارسال میکند:
POST https://authorization-server.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=abc123&redirect_uri=https://yourapp.com/callback&client_id=your_client_id&code_verifier=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
مقدارCode Verifier را با استفاده ازCode Challenge Method بررسی میکند:
برایS256: محاسبه میکند Base64URL(SHA256(code_verifier)) و آن را باCode Challenge ذخیرهشده مقایسه میکند
. برایplain: مستقیماً Code Verifier را با Code Challenge مقایسه میکند.
اگر تطابق داشته باشد، سرور توکن دسترسی (و احتمالاً توکن تازهسازی) را صادر میکند
از حملات تزریق کد احراز هویت جلوگیری میکند، زیرا:
- رهگیری کد احراز هویت بیفایده است: حتی اگر یک مهاجم کد احراز هویت را از redirect_uri بدزدد، بدون دانستن Code Verifier نمیتواند توکن دسترسی دریافت کند.
- مقدار Code Verifier مخفی است: این مقدار فقط در کلاینت ذخیره میشود و هرگز در شبکه ارسال نمیشود (بهجز در مرحله تبادل توکن با اتصال امن HTTPS).
- یکبارمصرف بودن: Code Verifier و Code Challenge برای هر درخواست احراز هویت جدید تولید میشوند، بنابراین نمیتوان آنها را دوباره استفاده کرد.
- روش S256: استفاده از هش SHA256 و رمزگذاری Base64URL باعث میشود که استخراج Code Verifier از Code Challenge عملاً غیرممکن باشد.