فاز 13 · درس 18

احراز هویت MCP در پروداکشن: ثبت و توکن‌های مقید به صادرکننده

نوع
ساختنی
زمان
حدود 90 دقیقه
زبان‌ها
Python (stdlib)
پیش‌نیاز
فاز 13 · درس 16 (ماشین وضعیت OAuth 2.1)، فاز 13 · درس 17 (گیت‌وی‌ها)
یک سیستم قفل و کلید دیجیتال متقارن روی یک مدار فیبر نوری امن در حال بررسی و اعتبارسنجی توکن‌ها
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. دامنه‌ی درس: اعمال قوانین عملیاتی پس از درس 16
  4. مفاهیم پایه
  5. متادیتای سرور احراز هویت OAuth در RFC 8414
  6. یادآوری RFC 9728 — متادیتای منبع محافظت‌شده
  7. سند متادیتای شناسه‌ی کلاینت (پیش‌فرض پیشنهادی)
  8. روش RFC 7591: ثبت‌نام منسوخ‌شده برای حفظ سازگاری
  9. یادآوری RFC 8707 — شناساگرهای منبع
  10. یادآوری RFC 7636 — مکانیزم PKCE
  11. پروفایل احراز هویت MCP در نسخه‌ی 2026-07-28
  12. چک‌لیست قابلیت‌های استقرار
  13. الگوی بازخوانی JWKS (چرخش در سرور احراز هویت، بازخوانی در سرور منبع)
  14. روتین اعتبارسنجی
  15. توکن‌های کدر نیاز به درون‌نگری دارند، نه حدس و گمان
  16. ابطال توکن یک قرارداد مبتنی بر تازگی داده است
  17. قطعی وابستگی‌ها نیاز به تصمیم‌گیری صریح دارد
  18. بررسی گام‌به‌گام بازپخش مخاطب (محدودسازی اختیارات توکن دسترسی)
  19. حملات اشتباه‌گیری (راهکار دفاعی سمت کلاینت که از عهده‌ی سرور خارج است)
  20. حالت‌های شکست
  21. استفاده از ابزارهای واقعی
  22. خروجی این درس
  23. تمرین‌ها
  24. اصطلاحات کلیدی
  25. برای مطالعه‌ی بیشتر

پیاده‌سازی اولیه‌ی جریان احراز هویت با پروتکل‌های استانداردی مثل OAuth 2.1 روی محیط لوکال یا شبیه‌سازهای درون‌حافظه‌ای کار ساده‌ای به نظر می‌رسد؛ اما انتقال همین منطق به محیط‌های عملیاتی و پروداکشن چالش‌های جدی به همراه دارد. در یک زیرساخت واقعی ممکن است صدها سرویس با ابزارهای مختلف در حال ارتباط باشند و عدم تفکیک دقیق دامنه‌ی دسترسی توکن‌ها می‌تواند آسیب‌پذیری‌های خطرناکی مثل بازپخش توکن (Token Replay) ایجاد کند.

در نسخه‌ی به‌روز مشخصات MCP، تمرکز ویژه‌ای روی بسترهای ارتباطی بی‌وضعیت (Stateless) و ثبت‌نام امن گذاشته شده است. روش‌هایی مانند سند متادیتای شناسه‌ی کلاینت (CIMD) جایگزین رویکردهای قدیمی شده‌اند و تک‌تک درخواست‌های ارسال‌شده به ابزارها باید به‌صورت مستقل، بدون وابستگی به نشست‌های قبلی، اعتبارسنجی شوند.

در این درس یاد می‌گیرید چگونه کلاینت‌ها و سرورهای منبع را در برابر سناریوهایی نظیر چرخش کلیدهای امضا (Key Rotation)، حملات اشتباه‌گیری (Mix-up Attacks) و خطای وابستگی‌های خارجی مقاوم‌سازی کنید و کدی پایدار و ایمن در بستر پروداکشن تحویل دهید.

در درس 16 ماشین وضعیت (State Machine) پروتکل OAuth 2.1 را پیاده کردیم. این درس مرزهای عملیاتی آن را برای مشخصات MCP نسخه‌ی 2026-07-28 مقاوم‌سازی می‌کند: اولویت با سند متادیتای شناسه‌ی کلاینت (Client ID Metadata Document)، ثبت پویای منسوخ‌شده صرفاً برای سازگاری، اعتبارسنجی صادرکننده در پاسخ احراز هویت، کلیدگذاری مدارک کلاینت بر اساس صادرکننده، بازخوانی JWKS، و الصاق توکن به مخاطب در تک‌تک درخواست‌های بی‌وضعیت (Stateless). نکته‌ی مشخصات (2026-07-28): ثبت پویای کلاینت (DCR) به نفع «سند متادیتای شناسه‌ی کلاینت» منسوخ شده است. DCR صرفاً به‌عنوان مکانیزمی برای سازگاری باقی می‌ماند. هر زمان از آن استفاده شود، کلاینت مقدار درست application_type را اعلام می‌کند. کلاینت وجود مقدار iss بر اساس RFC 9207 را اعتبارسنجی می‌کند و هرگز اطلاعات هویتی را میان صادرکنندگان سرور احراز هویت بازاستفاده نمی‌کند.

اهداف یادگیری

  • کشف سرور احراز هویت از طریق متادیتای RFC 8414 و اعتبارسنجی قرارداد آن.
  • ثبت‌نام از طریق سند متادیتای شناسه‌ی کلاینت و تفکیک DCR منسوخ‌شده به‌عنوان گزینه‌ی جایگزین (Fallback).
  • اعتبارسنجی مقدار iss مطابق RFC 9207، کلیدگذاری ثبت‌ها بر اساس صادرکننده‌ی سرور احراز هویت، و کلیدگذاری توکن‌های وابسته به منبع بر اساس ترکیب صادرکننده و منبع.
  • کش کردن و بازخوانی زمان‌بندی‌شده‌ی کلیدهای JWKS تا اعتبارسنجی امضا پس از جابه‌جایی کلیدها دوام بیاورد.
  • الصاق توکن‌ها به یک منبع MCP با استفاده از شناساگرهای منبع RFC 8707 و رد بازاستفاده‌ی نایب فریب‌خورده (Confused Deputy).
  • انتخاب بین اعتبارسنجی JWT یا درون‌نگری توکن (Token Introspection)، تعیین تازگی ابطال، و مدیریت امن خطاهای ناشی از در دسترس نبودن وابستگی‌های هویتی.
  • تفکیک سرور احراز هویت، سرور منبع و کلاینت تا هر کدام فقط بررسی‌های مربوط به دامنه‌ی خود را اجرا کنند.
  • ممیزی سرور احراز هویت بر اساس چک‌لیست استقرار و رد ثبت‌نام ناامن یا بازاستفاده از توکن.

صورت مسئله

شبیه‌ساز درس 16 پروتکل OAuth 2.1 را در حافظه اجرا می‌کرد. محیط عملیاتی سه شکاف اجرایی دارد که شبیه‌ساز درون‌حافظه‌ای متوجه آن‌ها نمی‌شود.

شکاف اول ثبت‌نام و جداسازی اطلاعات هویتی است. یک سازمان واقعی ممکن است صدها سرور MCP و هزاران کلاینت MCP را اجرا کند. بازنگری 2026-07-28 سند متادیتای شناسه‌ی کلاینت را ترجیح می‌دهد: کلاینت از یک آدرس HTTPS با مسیری که در کنترل خودش است به‌عنوان شناسه استفاده می‌کند و سرور احراز هویت متادیتا را واکشی می‌نماید. ثبت پویای RFC 7591 فقط به‌عنوان یک مسیر سازگاری منسوخ‌شده باقی می‌ماند. در صورتی که DCR اجتناب‌ناپذیر باشد، درخواست باید application_type صحیح را اعلام کند. کلاینت ثبت‌ها را بر اساس صادرکننده‌ی سرور احراز هویت ذخیره می‌کند و توکن‌های دسترسی را تحت جفت (issuer, resource) نگه می‌دارد. تغییر صادرکننده به معنای ثبت‌نام جدید است و منبع متفاوت به معنای توکنی جداگانه و مقید به همان مخاطب است.

شکاف دوم چرخش کلید (Key Rotation) است. اعتبارسنجی JWT به کلیدهای امضای سرور احراز هویت وابسته است که به‌صورت مجموعه‌کلید وب جیسون (JWKS) منتشر می‌شوند. سرور احراز هویت این کلیدها را طبق زمان‌بندی منظمی می‌چرخاند (اغلب ساعتی، و در زمان مدیریت حوادث حتی سریع‌تر). سرور MCP که JWKS را فقط یک بار هنگام راه‌اندازی واکشی می‌کند، تا پنجره‌ی چرخش بدون مشکل اعتبارسنجی را انجام می‌دهد — سپس همه‌ی درخواست‌ها تا راه‌اندازی مجدد رد می‌شوند. محیط عملیاتی، JWKS را به‌عنوان مقداری کش‌شده همراه با یک تسک بازخوانی پیاده می‌کند که کش را پیش از انقضای کلیدهای قبلی به‌روزرسانی کند؛ به‌علاوه‌ی واکشی جایگزین در زمان خالی بودن کش (Cache Miss) برای حالتی که توکنی با کلید تازه‌تر از نسخه‌ی کش‌شده برسد.

شکاف سوم محدودسازی مخاطب (Audience Binding) است. درس 16 شناساگرهای منبع RFC 8707 را معرفی کرد. در محیط عملیاتی، این شناساگر در تک‌تک درخواست‌ها به یک بررسی الزامی کلیم تبدیل می‌شود. سرور MCP مقدار token.aud را با آدرس منبع استاندارد خود مقایسه می‌کند و موارد عدم تطابق را با کد وضعیت HTTP 401 رد می‌کند. این تنها راه دفاعی در برابر سرور MCP بالادستی (یا کلاینت مخربی است که توکن مختص سرور دیگری را در دست دارد) تا نتواند آن توکن را روی سرور دیگری در همان شبکه‌ی اعتماد بازپخش کند.

این درس هر شکاف را روی بخش مشخصی از سطح حمله پیاده می‌کند. سند متادیتا یک اندپوینت (Endpoint) پروتکل HTTP است. بازخوانی کش JWKS یک کار زمان‌بندی‌شده به‌همراه یک کش کلید-مقدار است. اعتبارسنجی JWT روتینی است که سرور منبع پیش از ارسال هر فراخوانی ابزار (Tool call) اجرا می‌کند. این سه نقش را مجزا نگه دارید تا هرکدام تنها بررسی‌های تحت اختیار خود را اجرا کنند: سرور احراز هویت صادر می‌کند و کلیدها را می‌چرخاند، سرور منبع کش می‌کند و می‌سنجد، و کلاینت کشف می‌کند و ثبت‌نام را انجام می‌دهد.

دامنه‌ی درس: اعمال قوانین عملیاتی پس از درس 16

درس 16: امنیت MCP با OAuth 2.1 ماشین وضعیت کد احراز هویت، PKCE، کشف منبع محافظت‌شده، شناساگرهای منبع و تصمیم‌گیری‌های دامنه (Scope) را مدیریت می‌کند. این درس جریان OAuth دومی تعریف نمی‌کند؛ بلکه بعد از شکل‌گیری این قراردادها وارد عمل می‌شود و بررسی می‌کند که یک سرور منبع مستقرشده چگونه در شرایط چرخش کلید، اعتبارسنجی توکن‌های کدر (Opaque Token)، ابطال، قطعی وابستگی‌ها، فرایند استقرار و مدیریت حوادث، همچنان این قوانین را اعمال می‌کند.

مرز عملیاتی دقیق‌تر و معطوف به اجرا است:

  • مسیر JWT در هر درخواست صادرکننده، الگوریتم، کلید امضا، مخاطب، کلیم‌های زمانی و دامنه‌های الصاق‌شده را بررسی می‌کند و هم‌زمان بازخوانی JWKS را با ایمنی انجام می‌دهد.
  • مسیر توکن کدر اندپوینت درون‌نگری احرازشده‌ی صادرکننده را فراخوانی می‌کند و فعال بودن، مخاطب یا منبع، انقضا، فاعل (Subject) و دامنه‌های بازگشتی را می‌سنجد.
  • سیاست ابطال مشخص می‌کند که یک مدرک هویتی ظرف چه مدتی باید از کار بیفتد و کدام کش می‌تواند این فرایند را به تأخیر بیندازد.
  • سیاست خطا تعیین می‌کند که هنگام در دسترس نبودن زیرساخت کشف، JWKS، درون‌نگری یا ابطال چه تصمیمی گرفته شود.
  • شواهد (Evidence) ثبت می‌کنند که کدام متادیتای صادرکننده، مجموعه‌کلید، پاسخ درون‌نگری، کلیم‌های توکن، نسخه‌ی سیاست و دلیل رد درخواست مبنای نتیجه بوده‌اند، بدون آنکه خود توکن ذخیره شود.

این تفکیک باعث ترکیب‌پذیری درس‌ها می‌شود. درس 16 درستی جریان را اثبات می‌کند؛ درس 18 اثبات می‌کند که یک توکن پس از رسیدن به مسیر واقعی درخواست MCP، همچنان قابل اعتماد است یا رد می‌شود.

مفاهیم پایه

متادیتای سرور احراز هویت OAuth در RFC 8414

سندی در مسیر /.well-known/oauth-authorization-server همه‌ی نیازهای کلاینت را مشخص می‌کند:

{
  "issuer": "https://auth.example.com",
  "authorization_endpoint": "https://auth.example.com/authorize",
  "token_endpoint": "https://auth.example.com/token",
  "jwks_uri": "https://auth.example.com/.well-known/jwks.json",
  "client_id_metadata_document_supported": true,
  "registration_endpoint": "https://auth.example.com/register",
  "authorization_response_iss_parameter_supported": true,
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["mcp:tools.read", "mcp:tools.invoke"],
  "token_endpoint_auth_methods_supported": ["none", "private_key_jwt"]
}

کلاینتی که آدرس منبع MCP را دارد، فرایند کشف را زنجیره‌ای انجام می‌دهد: سند oauth-protected-resource از RFC 9728 (سند سرور منبع) صادرکننده را مشخص می‌کند، و سپس oauth-authorization-server (همین سند) تمامی اندپوینت‌ها را معرفی می‌نماید. کلاینت هرگز آدرس احراز هویت را هاردکد نمی‌کند.

اگر شناسه‌ی منبع شامل مسیر (Path) باشد، بخش well-known پیش از آن مسیر اضافه می‌شود. مثلاً برای https://mcp.example.com/team/server، متادیتای منبع محافظت‌شده در آدرس https://mcp.example.com/.well-known/oauth-protected-resource/team/server برطرف می‌شود. قرار دادن /.well-known/... پس از مسیر منبع غلط است.

قراردادی که پیش از اعتماد به یک ارائه‌دهنده‌ی هویت (IdP) برای MCP بررسی می‌کنید:

  • پارامتر code_challenge_methods_supported باید شامل S256 باشد (PKCE مطابق RFC 7636). مشخصات صریح است: اگر این فیلد نباشد، سرور احراز هویت از PKCE پشتیبانی نمی‌کند و کلاینت باید فرایند را متوقف کند.
  • پارامتر grant_types_supported شامل authorization_code بوده و password و implicit را رد کند.
  • حداقل یک مسیر ثبت‌نام در دسترس باشد: client_id_metadata_document_supported: true (CIMD، روش ارجح)، کلاینت از پیش ثبت‌شده، یا registration_endpoint (مسیر سازگاری منسوخ‌شده‌ی RFC 7591).
  • اگر authorization_response_iss_parameter_supported برابر true باشد، کلاینت مقدار iss بازگشتی مطابق RFC 9207 را الزامی می‌داند و آن را دقیقاً با صادرکننده‌ای که قبل از تغییر مسیر ذخیره کرده مقایسه می‌کند.
  • مقدار response_types_supported برای OAuth 2.1 باید دقیقاً ["code"] باشد.

اگر S256 وجود نداشته باشد، سرور MCP از استقرار روی این IdP خودداری می‌کند — هیچ حالت تنزل‌یافته‌ای (Degraded Mode) برای PKCE وجود ندارد. اگر هیچ‌کدام از مسیرهای ثبت‌نام اعلام نشده باشند و client_id از پیش ثبت‌شده‌ای هم نداشته باشید، امکان ثبت‌نام وجود ندارد؛ ایراد از مانیفست (Manifest) استقرار است، نه از کد.

یادآوری RFC 9728 — متادیتای منبع محافظت‌شده

درس 16 این RFC را پوشش داد. تفاوت در محیط عملیاتی این است: این سند تنها جایی است که کلاینت برای پیدا کردن سرورهای احراز هویت مورد اعتماد همین سرور MCP به آن مراجعه می‌کند. یک سرور MCP ممکن است توکن‌های چند IdP مختلف را بپذیرد (یکی برای پرسنل، یکی برای همکاران تجاری). استاندارد RFC 9728 آن مجموعه را اعلام می‌کند و RFC 8414 ویژگی‌های هر IdP را برمی‌شمارد.

{
  "resource": "https://notes.example.com",
  "authorization_servers": ["https://auth.example.com", "https://partners.example.com"],
  "scopes_supported": ["mcp:tools.invoke"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://notes.example.com/docs"
}

روش CIMD فرایند ثبت را از حالت ارسال مستقیم (Push) به دریافت درخواستی (Pull) تغییر می‌دهد. به جای آنکه کلاینت از سرور احراز هویت بخواهد یک client_id بسازد، از یک آدرس HTTPS تحت کنترل خود به‌عنوان client_id استفاده می‌کند. این آدرس به یک سند متادیتای JSON متصل می‌شود؛ سرور احراز هویت در زمان اجرای جریان OAuth آن را واکشی می‌کند. ریشه‌ی اعتماد در DNS است: اگر اپراتور سرور به app.example.com اعتماد دارد، به کلاینتی که از https://app.example.com/client.json ارائه می‌شود نیز اعتماد خواهد کرد. نه رفت‌وبرگشتی برای ثبت نیاز است، نه ظرفیت شناسه‌ها پر می‌شود، و نه نیازی به همگام‌سازی وضعیت در سمت سرور وجود دارد.

سند متادیتایی که کلاینت میزبانی می‌کند:

{
  "client_id": "https://app.example.com/oauth/client.json",
  "client_name": "Example MCP Client",
  "client_uri": "https://app.example.com",
  "application_type": "native",
  "redirect_uris": ["http://127.0.0.1:7333/callback", "http://localhost:7333/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

مقدار client_id در سند باید با آدرسی که از آن ارائه می‌شود برابر باشد (سرور احراز هویت این موضوع را بررسی و در صورت عدم تطابق رد می‌کند). سرور احراز هویت پشتیبانی از این قابلیت را با مقدار client_id_metadata_document_supported: true در متادیتای RFC 8414 خود اعلام می‌کند.

در قرارداد فعلی CIMD، مقادیر client_id، client_name و آرایه‌ی غیرخالی redirect_uris الزامی هستند. شناسه‌ی کلاینت یک آدرس مطلق HTTPS دارای مسیر است. فیلد application_type می‌تواند درج شود، اما فیلد اجباری CIMD نیست. الزام فیلد application_type در روش DCR را به مسیر ترجیحی CIMD منتقل نکنید.

دو نکته‌ی امنیتی که مستندات با صراحت بر آن‌ها تأکید دارد:

  • حمله‌ی SSRF. سرور احراز هویت آدرسی را واکشی می‌کند که کلاینت داده است؛ بنابراین باید در برابر جعل درخواست سمت سرور (SSRF) محافظت شود (نباید اندپوینت‌های داخلی یا مدیریتی واکشی شوند).
  • جعل هویت localhost. روش CIMD به‌تنهایی مانع این نمی‌شود که مهاجم محلی آدرس متادیتای یک کلاینت معتبر را بردارد و آن را به بازهدایت localhost دلخواه خود متصل کند. سرور احراز هویت باید نام هاست آدرس بازهدایت را هنگام رضایت‌سنجی واضح نشان دهد و در صورت وجود بازهدایت‌های صرفاً روی localhost بهتر است هشدار دهد.

از آنجا که CIMD نیازی به ذخیره‌ی وضعیت در سمت سرور ندارد، احتیاجی به راه‌اندازی واحد ثبت‌نام (Registrar) مشابه DCR نیست. سمت کلاینت فقط‌خواندنی است: سند متادیتا را روی یک اندپوینت HTTPS ایستا بگذارید تا سرور احراز هویت خودش آن را دریافت کند.

اگر اپراتور سرور احراز هویت شناسه‌ی کلاینت را از قبل ثبت کرده باشد، پیش از رفتن سراغ ثبت خودکار از همان ثبت در دامنه‌ی صادرکننده استفاده کنید. در غیر این صورت CIMD را ترجیح دهید. از DCR منسوخ‌شده فقط زمانی استفاده کنید که صادرکننده نه ثبت قبلی را بپذیرد و نه از CIMD پشتیبانی کند.

روش RFC 7591: ثبت‌نام منسوخ‌شده برای حفظ سازگاری

روش DCR در بازنگری 2026-07-28 منسوخ شده است. آن را صرفاً برای سرورهای احراز هویتی نگه دارید که نمی‌توانند CIMD را پردازش کنند و ثبت قبلی هم در آن‌ها مقدور نیست. در این حالت، کلاینت سازگار این درخواست را ارسال می‌کند:

POST /register
Content-Type: application/json

{
  "application_type": "native",
  "redirect_uris": ["http://127.0.0.1:7333/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "mcp:tools.invoke",
  "client_name": "Cursor",
  "software_id": "com.cursor.cursor",
  "software_version": "0.42.0"
}

سرور با client_id و یک registration_access_token برای به‌روزرسانی‌های بعدی پاسخ می‌دهد:

{
  "client_id": "c_3e7f1a",
  "client_id_issued_at": 1769472000,
  "redirect_uris": ["http://127.0.0.1:7333/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "registration_access_token": "regt_b2...",
  "registration_client_uri": "https://auth.example.com/register/c_3e7f1a"
}

فیلد application_type صوری نیست. کلاینت دسکتاپ محلی مقدار native را اعلام می‌کند؛ کلاینت تحت وب مقدار web را برمی‌گزیند و از آدرس‌های بازهدایت HTTPS استفاده می‌کند. مقدار token_endpoint_auth_method: none پیش‌فرض مناسبی برای کلاینت عمومی محلی (Native) است. این کلاینت فقط یک client_id می‌گیرد و اثبات مالکیت از طریق PKCE تأمین می‌شود.

سه دام امنیتی در محیط عملیاتی:

  • اندپوینت ثبت باید بر اساس IP مبدا محدودیت نرخ (Rate-limit) بگذارد؛ در غیر این صورت، مهاجم با اسکریپت میلیون‌ها ثبت جعلی ایجاد می‌کند و شناسه‌های کلاینت را به پایان می‌رساند. پیش از رسیدگی به درخواست ثبت، بررسی محدودیت نرخ را انجام دهید.
  • فیلد software_statement (یک JWT امضاشده در تأیید کلاینت) در برخی IdPهای سازمانی الزامی است. پیاده‌سازی آزمایشی این درس از آن صرف‌نظر می‌کند، اما سیستم واقعی بررسی اعتباری می‌گذارد که ثبت‌های بدون امضا را جز برای آدرس‌های بازهدایت localhost رد می‌کند.
  • مقدار registration_access_token باید به‌صورت هش ذخیره شود، نه متن خام. سرقت این توکن دسترسی تغییر آدرس‌های بازهدایت کلاینت را به مهاجم می‌دهد.

یادآوری RFC 8707 — شناساگرهای منبع

درس 16 ساختار کلی را مشخص کرد. قاعده‌ی عملیاتی: هر درخواست توکن باید شامل resource=<canonical-mcp-url> باشد، و سرور MCP در هر فراخوانی بررسی می‌کند که token.aud با آدرس منبع خودش همخوانی داشته باشد. آدرس استاندارد (Canonical URI) دقیق‌ترین شناسه‌ی سرور است: از پروتکل و نام هاست با حروف کوچک، بدون بخش fragment، و طبق عرف بدون اسلش پایانی استفاده می‌کند. بخش مسیر (Path) بر اساس قاعده حذف نمی‌شود — این مشخصات مسیر را وقتی برای تفکیک یک سرور MCP خاص لازم باشد نگه می‌دارد. آدرس‌های https://mcp.example.com، https://mcp.example.com/mcp، https://mcp.example.com:8443 و https://mcp.example.com/server/mcp همگی آدرس‌های استاندارد معتبری هستند. برای هر سرور یکی را برگزینید و aud را دقیقاً روی همان قفل کنید. (کد شبیه‌ساز این درس برای اختصار از مخاطب‌هایی فقط شامل هاست مثل https://notes.example.com استفاده می‌کند؛ اما در استقراری که چند سرور MCP را زیر یک مبدا مشترک میزبانی می‌کند، آن‌ها با مسیر از هم تفکیک می‌شوند.)

یادآوری RFC 7636 — مکانیزم PKCE

استفاده از PKCE در OAuth 2.1 اجباری است. جریان کد احراز هویت این درس همیشه پارامترهای code_challenge و code_verifier را همراه دارد. سرور هر درخواست توکنی را که فاقد مدل اعتبارسنج (Verifier) باشد یا هش مدل اعتبارسنج آن با چالش ذخیره‌شده تطابق نداشته باشد، رد می‌کند.

پروفایل احراز هویت MCP در نسخه‌ی 2026-07-28

نسخه‌ی فعلی MCP مرز سرور منبع OAuth را حفظ می‌کند و در عین حال بستر انتقال (Transport) پروتکل MCP را به حالت بی‌وضعیت درمی‌آورد. نشست پروتکلی وجود ندارد که بتوان تصمیم هویتی را روی آن کش کرد. در نتیجه لایه‌ی احراز هویت هر درخواست را جداگانه ارزیابی می‌کند:

  • متادیتای منبع محافظت‌شده‌ی RFC 9728 را پیاده کنید و آدرس آن را یا از طریق هدر WWW-Authenticate: Bearer resource_metadata="..." در خطای 401 یا از طریق آدرس استاندارد /.well-known/oauth-protected-resource ارائه دهید (در SEP-985 این هدر با یک مسیر جایگزین استاندارد اختیاری شد). فیلد authorization_servers در متادیتا باید حداقل یک سرور را مشخص کند.
  • توکن‌ها را در تک‌تک درخواست‌ها فقط از طریق هدر Authorization: Bearer ... بپذیرید — هرگز در Query String نباشد، و هرگز فقط در ابتدای نشست بررسی نشود.
  • فیلدهای aud، iss، exp و دامنه‌های دسترسی مورد نیاز را در هر درخواست بسنجید. سرور باید بررسی کند که توکن مشخصاً برای خودش صادر شده باشد (Audience)؛ نبود یا عدم تطابق aud باید رد شود و نباید با آن مثل یک کاراکتر عمومی (Wildcard) برخورد کرد.
  • در خطاهای 401 و 403، هدر WWW-Authenticate: Bearer همراه با error=...، پارامتر resource_metadata="<PRM-URL>" (آدرس سند متادیتا، نه خود منبع خام)، و در صورت کمبود دسترسی (خطای 403 با insufficient_scope) مقدار scope="..." را برگردانید. دقت کنید: پارامتر ارائه‌شده resource_metadata است که به کشف اشاره دارد — هیچ پارامتری به نام resource در متن چالش وجود ندارد.
  • فرایند کشف سرور احراز هویت هم متادیتای OAuth در RFC 8414 و هم OpenID Connect Discovery 1.0 را می‌پذیرد؛ کلاینت‌ها باید هر دو پسوند well-known را به ترتیب اولویت بررسی کنند.
  • کلاینت (و نه سرور) در برابر حملات اشتباه‌گیری (Mix-up Attacks) دفاع می‌کند: پیش از هدایت، مقدار issuer مورد انتظار را ذخیره می‌کند و مقدار iss برگشتی در پاسخ احراز هویت نهایی (RFC 9207) را قبل از تحویل کد بررسی می‌نماید. مکانیزم PKCE به‌تنهایی مانع این حمله نمی‌شود، چون کلاینت code_verifier خود را به همان اندپوینت توکنی می‌دهد که به سمتش هدایت شده است.
  • شناسه‌ی هویتی کلاینت به یک صادرکننده‌ی مشخص سرور احراز هویت تعلق دارد. اگر کشف به صادرکننده‌ی دیگری برسد، کلاینت به جای فرستادن client_id، توکن ثبت یا توکن دسترسی قبلی، مجدداً ثبت‌نام می‌کند.
  • روش CIMD سازوکار ترجیحی ثبت‌نام است. روش DCR منسوخ است؛ هر درخواست سازگاری با DCR کماکان مقدار درست application_type را اعلام می‌کند.

پیش‌نویس OAuth 2.1 شالوده‌ی کار است؛ استانداردهای RFC 8414/7591/8707/9728/9207 به همراه RFC 7636 و CIMD سطح بیرونی را می‌سازند؛ و مشخصات MCP پروفایل اجرایی نهایی است.

چک‌لیست قابلیت‌های استقرار

جدول قابلیت‌های شرکت‌ها سریع قدیمی می‌شود. به جای اعتماد به آن‌ها، متادیتای برگشتی از سرور احراز هویتی را که قصد استقرارش را دارید مستقیماً ارزیابی کنید. معیارها کاملاً مشخص هستند:

بررسی تصمیم الزامی
صادرکننده‌ی کشف‌شده دقیقاً همان صادرکننده‌ی HTTPS که در سیاست تعریف شده است
مکانیزم PKCE وجود S256 اعلام شده باشد؛ در غیر این صورت متوقف شود
روش ثبت‌نام اولویت با CIMD، ثبت از پیش انجام‌شده پذیرفته است، DCR فقط برای سازگاری منسوخ‌شده
پاسخ احراز هویت اعتبارسنجی iss طبق RFC 9207 در صورت وجود یا اعلام در متادیتا
محدودسازی منبع درخواست توکن پارامتر resource را بفرستد؛ سرور منبع وجود aud منطبق را الزامی بداند
ذخیره‌سازی اطلاعات هویتی کلیدگذاری شناسه‌های کلاینت و مدارک ثبت بر اساس صادرکننده؛ کلیدگذاری توکن‌های دسترسی بر اساس صادرکننده به علاوه‌ی منبع
سازگاری DCR اعلام native یا web؛ رد آدرس‌های بازهدایتی که با نوع برنامه‌ی اعلام‌شده همخوانی ندارند

پشتیبانی را از نام محصول یا سطح قیمتی آن حدس نزنید. سند کشف‌شده را در شواهد استقرار ثبت کنید و با نبود هر فیلد اجباری، بلافاصله دسترسی را متوقف نمایید (Fail-closed).

الگوی بازخوانی JWKS (چرخش در سرور احراز هویت، بازخوانی در سرور منبع)

این دو اصطلاح را جدا از هم در نظر بگیرید، چون یکی دانستن آن‌ها خطای بزرگی در محیط عملیاتی می‌سازد:

  • چرخش (Rotate) کاری است که سرور احراز هویت انجام می‌دهد: کلید امضای جدیدی می‌سازد، آن را در JWKS منتشر می‌کند، و کلید قبلی را بعداً بازنشسته می‌نماید. سرور منبع هیچ نقشی در این بخش ندارد و توان انجامش را هم ندارد — چون کلیدهای خصوصی IdP دست آن نیست.
  • بازخوانی (Refresh) کاری است که سرور منبع انجام می‌دهد: دریافت مجدد (GET) لیست کلیدهای JWKS منتشرشده و ریختن آن در کش. این تنها کاری است که سرور منبع با JWKS انجام می‌دهد.

حالت شکست (Failure mode) در محیط عملیاتی، منقضی و کهنه شدن کش است. راه‌حل آن یک تسک زمان‌بندی‌شده‌ی بازخوانی همراه با یک کش کلید-مقدار است. سرور منبع پردازشی (کرون، تایمر، یا هرچه محیط اجرا (Runtime) دارد) را اجرا می‌کند که در بازه‌های زمانی ثابت، مسیر <issuer>/.well-known/jwks.json را دریافت کرده و مقدار cache[issuer] = {keys, fetched_at} را بازنویسی می‌کند. سیستم اعتبارسنجی مقادیر را از این کش می‌خواند. اگر توکنی برسد که مقدار kid آن در کش نباشد، فقط یک بار بازخوانی همگام به‌عنوان جایگزین انجام می‌شود و سپس اعتبارسنجی تکرار می‌گردد. این ساختار دو حالت را هم‌زمان پوشش می‌دهد: بازخوانی دوره‌ای زمان‌بندی‌شده، و بازه‌های هم‌پوشانی کلیدها که در آن توکنی با کلید کاملاً جدید، پیش از فرارسیدن زمان بازخوانی بعدی از راه می‌رسد.

واکشی مجدد در زمان خالی بودن کش باید دریافت دوباره باشد، نه ساخت و چرخش کلید جدید. اگر مسیر خطا را طوری بنویسید که کلید جدید بچرخاند و تولید کند، دو مشکل رخ می‌دهد: (1) کلید تولیدشده جدید باز هم دارای kid متفاوتی با توکن است، بنابراین جستجو در هر حال به نتیجه نمی‌رسد؛ و (2) مهاجمی که توکن‌هایی با شناسه‌های kid تصادفی بفرستد، سرور را وادار به تولید بی‌نهایت کلید می‌کند — یک حمله‌ی منع سرویس (DoS) خودخواسته. واکشی دوباره یک عملیات بدون اثر جانبی (Idempotent) است، بنابراین ارسال یک kid نامعتبر نهایتاً به اندازه‌ی یک درخواست واکشی بی‌فایده هزینه می‌برد.

ساختار کش:

{
  "https://auth.example.com": {
    "keys": [
      {"kid": "k_2026_03", "kty": "RSA", "n": "...", "e": "AQAB", "alg": "RS256", "use": "sig"},
      {"kid": "k_2026_04", "kty": "RSA", "n": "...", "e": "AQAB", "alg": "RS256", "use": "sig"}
    ],
    "fetched_at": 1772668800
  }
}

وجود هم‌زمان دو کلید در کش وضعیت پایدار سیستم است. سرورهای احراز هویت برای چرخش کلید، کلید بعدی (k_2026_04) را پیش از کنار گذاشتن کلید قبلی (k_2026_03) معرفی می‌کنند تا توکن‌های صادرشده با کلید قبلی تا زمان انقضا معتبر بمانند. کش اجتماع این کلیدها را نگه می‌دارد؛ سیستم اعتبارسنجی کلید مورد نظر را از روی kid پیدا می‌کند.

روتین اعتبارسنجی

سرور MCP پیش از اجرای هر ابزار، اعتبارسنجی را انجام می‌دهد. ساختاری که در code/main.py به کار رفته است:

result = server.validate(bearer_token, required_scope="mcp:tools.invoke")
if not result["valid"]:
    return {"status": result["status"], "WWW-Authenticate": result["www_authenticate"]}

تابع validate توکن JWT را رمزگشایی (Decode) می‌کند، کلید امضا را از کش JWKS می‌خواند (و در صورت نیافتن، یک بار بازخوانی می‌کند)، امضا را می‌سنجد، و سپس iss را با لیست مجاز، aud را با منبع استاندارد این سرور، exp و دامنه‌ی مورد نیاز را مقایسه می‌نماید — و در صورت مشاهده‌ی اولین خطا، چالش WWW-Authenticate را پس می‌فرستد. تجمیع این موارد در قالب یک تابع واحد روی سرور منبع تضمین می‌کند که تمامی نقاط ورودی (هر فراخوانی ابزار، هر بستر انتقال) بررسی‌های یکسانی را طی می‌کنند؛ هیچ مسیری وجود ندارد که بدون اعتبارسنجی قبلی به ابزاری دسترسی پیدا کند.

توکن‌های کدر نیاز به درون‌نگری دارند، نه حدس و گمان

همه‌ی توکن‌های دسترسی از نوع JWT نیستند. اگر صادرکننده توکن کدر تولید کند، سرور منبع نمی‌تواند آن را به کلیم‌های قابل اعتماد رمزگشایی (Decode) کند. در این شرایط، سرور منبع توکن را از طریق یک کانال ارتباطی امن و احرازشده به اندپوینت درون‌نگری صادرکننده مطابق RFC 7662 می‌فرستد و شروط مختلف را بررسی می‌کند: active: true بودن، انطباق صادرکننده، مخاطب یا منبع دقیق MCP، نگذشتن زمان انقضا، و دامنه‌های مورد نیاز برای همان ابزار مشخص.

نتیجه‌ی درون‌نگری را بر اساس صادرکننده، خلاصه‌ی یک‌طرفه‌ی توکن (Token Digest) و منبع MCP کش کنید. هرگز متن خام توکن را در لاگ‌ها یا عنوان کش نگذارید. زمان مجاز یک ورودی در کش معتبر را محدود کنید به کمترین مقدار از میان: زمان انقضای توکن، توصیه‌ی کش صادرکننده، یا هدف زمانی تازگی ابطال در سامانه‌ی خود. مدت زمان کش پاسخ‌های نامعتبر (Negative Caching) را هم به اندازه‌ای کوتاه در نظر بگیرید که توکن تازه صادرشده به اشتباه غیرفعال نماند. نتیجه‌ی بررسی برای یک منبع نمی‌تواند به منبع دیگری مجوز دهد، حتی اگر متن توکن کدر کاملاً یکسان باشد.

حالت اعتبارسنجی را بر اساس محتوای توکن — که ممکن است دستکاری‌شده باشد — تعیین نکنید. انتخاب میان JWT یا درون‌نگری باید بر اساس متادیتای اعتبارسنجی‌شده‌ی صادرکننده و تنظیمات استقرار قفل شده باشد. در مسیر JWT، الگوریتم‌های مجاز و jwks_uri مورد اعتماد را ثابت نگه دارید؛ هرگز آدرس کلید یا الگوریتمی را که صرفاً از هدر توکن خوانده شده دنبال نکنید.

ابطال توکن یک قرارداد مبتنی بر تازگی داده است

استاندارد RFC 7009 به کلاینت امکان می‌دهد ابطال توکن را از سرور احراز هویت بخواهد. این درخواست نسخه‌هایی را که در سرورهای منبع مختلف کش شده‌اند فوراً پاک نمی‌کند. حداکثر زمان قابل قبول برای تأخیر در ابطال را مشخص کنید و همه‌ی کش‌ها را ملزم به رعایت آن نمایید.

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

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

قطعی وابستگی‌ها نیاز به تصمیم‌گیری صریح دارد

سیاست‌های دسترسی‌پذیری سیستم را در بلوک‌های مدیریت خطا فی‌البداهه تعیین نکنید.

نوع خطا رفتار امن در محیط عملیاتی
بازخوانی دوره‌ای JWKS با شکست مواجه می‌شود، ولی مقدار kid شناسایی‌شده در کش معتبر و محدود وجود دارد فقط در محدوده‌ی پنجره‌ی زمانی خطای مجاز کار را ادامه دهید و شواهد سلامت تنزل‌یافته را ثبت کنید
توکن مقدار kid ناشناسی دارد و تک‌فرصت بازخوانی آن هم شکست می‌خورد درخواست را رد کنید؛ هرگز امضای تاییدنشده را نپذیرید
سیستم درون‌نگری از دسترس خارج شده است دسترسی فراخوانی‌های محافظت‌شده را قطع کنید (Fail-closed)؛ خطای شبکه را به active: true تعبیر نکنید
متادیتای منبع محافظت‌شده یا صادرکننده ناگهان تغییر می‌کند فرایند ثبت‌نام جدید و دریافت توکن را متوقف کنید؛ فقط پیکربندی منقضی‌نشده و قفل‌شده را تحت سیاست اضطراری محدود نگه دارید
اندپوینت ابطال از دسترس خارج شده است خروج یا ابطال را ناقص اعلام کنید، در صورت امکان مدرک را به‌صورت محلی غیرقابل استفاده نگه دارید، و موفقیت سراسری ابطال را اعلام نکنید
منبع زمان سرور یا نوع کلیم نامعتبر است به جای زیاد کردن بازه‌ی انحراف مجاز زمان برای عبور توکن، آن را بلافاصله رد کنید

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

بررسی گام‌به‌گام بازپخش مخاطب (محدودسازی اختیارات توکن دسترسی)

سرور A با آدرس notes.example.com و سرور B با آدرس tasks.example.com هر دو در یک سرور احراز هویت مشترک ثبت شده‌اند. سرور A هک می‌شود. مهاجم توکن یادداشت‌های کاربر را برمی‌دارد و آن را روی سرور B ارسال می‌کند.

سیستم اعتبارسنجی سرور B:

  1. توکن JWT را رمزگشایی (Decode) کرده، کلید را از طریق kid از JWKS می‌خواند و صحت امضا را تایید می‌کند.
  2. مقدار iss را با لیست authorization_servers در متادیتای منبع محافظت‌شده‌ی خود تطبیق می‌دهد. (تایید می‌شود — IdP مشترک است.)
  3. شرط aud == "https://tasks.example.com" را ارزیابی می‌کند. (رد می‌شود — مخاطب توکن https://notes.example.com است.)
  4. کد خطای 401 را همراه با هدر WWW-Authenticate: Bearer error="invalid_token", error_description="audience mismatch", resource_metadata="https://tasks.example.com/.well-known/oauth-protected-resource" برمی‌گرداند.

کلیم مخاطب تنها محافظ پروتکل در برابر این حمله است. نادیده گرفتن آن به بهانه‌ی سرعت و کارایی از خطاهای رایج در پروژه‌هاست؛ این بررسی باید در هر درخواست اجرا شود، نه فقط در شروع نشست. در مشخصات استاندارد این سازوکار محدودسازی اختیارات توکن دسترسی (Access-token Privilege Restriction) نامیده می‌شود: سرور MCP باید هر توکنی را که نامش در کلیم مخاطب آن ثبت نشده رد کند.

نکته در مورد نام‌گذاری. مستندات استاندارد اصطلاح نایب فریب‌خورده (Confused deputy) را برای مشکلی دیگر به کار می‌برد: سرور MCP که نقش پروکسی OAuth برای یک API ثالث را با شناسه‌ی کلاینت ثابت ایفا می‌کند، و توکن را بدون رضایت جداگانه‌ی هر کلاینت بازفروارد می‌نماید. انطباق کلیم مخاطب جلوی بازپخش توکن بالا را می‌گیرد؛ رفع مشکل نایب فریب‌خورده به رضایت مجزا برای هر کلاینت و خودداری از ارسال توکن دریافتی به APIهای بالادستی وابسته است (سرور MCP باید خودش توکن بالادستی جداگانه‌ای بگیرد).

حملات اشتباه‌گیری (راهکار دفاعی سمت کلاینت که از عهده‌ی سرور خارج است)

کلاینت در طول عمر خود با چندین سرور احراز هویت ارتباط برقرار می‌کند. یک سرور احراز هویت مخرب می‌تواند تلاش کند کلاینت را فریب دهد تا کد احراز هویت دریافت‌شده از یک سرور معتبر را در اندپوینت توکن سرور مهاجم تحویل دهد. بررسی مخاطب مانع این کار نمی‌شود — حمله قبل از ساخته شدن توکن اتفاق می‌افتد. راه‌حل در سمت کلاینت پیاده می‌شود (RFC 9207):

  1. پیش از هدایت، کلاینت مقدار issuer مورد انتظار را از متادیتای اعتبارسنجی‌شده‌ی سرور احراز هویت ذخیره می‌کند.
  2. در پاسخ احراز هویت، کلاینت پارامتر iss دریافتی را پیش از ارسال کد به هر نقطه‌ای، با مقدار ذخیره‌شده مقایسه می‌کند (مقایسه‌ی ساده‌ی رشته‌ها، بدون تغییر و یکسان‌سازی).
  3. در صورت مغایرت (یا نبود مقدار iss در شرایطی که سرور پشتیبانی از authorization_response_iss_parameter_supported را اعلام کرده بود) → درخواست رد می‌شود و حتی فیلدهای error نیز نمایش داده نمی‌شوند.

استفاده از PKCE به‌تنهایی جلوی این حمله را نمی‌گیرد، چون کلاینت code_verifier را به همان اندپوینت توکنی می‌دهد که به سمتش منحرف شده است. به همین علت است که مشخصات، ثبت مقدار صادرکننده را برای هر درخواست در کنار اعتبارسنج PKCE و پارامتر state ضروری می‌داند.

حالت‌های شکست

  • کهنه شدن کش JWKS. سرور اعتبارسنج پس از چرخاندن کلید توسط سرور احراز هویت، توکن‌های معتبر را رد می‌کند. راه‌حل همان الگوی بازخوانی زمان‌بندی‌شده + بازخوانی در زمان خالی بودن کش است. هرگز JWKS را بدون تسک بازخوانی دوره‌ای کش نکنید.
  • چرخش کلید به جای بازخوانی در خطای کش. وصل کردن مسیر خالی بودن کش به تولید و چرخش کلید جدید یک خطای مهلک است: هرگز مقدار ناموجود kid را پیدا نمی‌کند و سیستم را با مقادیر ساختگی مهاجم دچار حمله‌ی DoS در ساخت کلید می‌کند. این مسیر بازگشتی باید متد بدون عوارض جانبی refresh-jwks باشد.
  • نبود کلیم aud. برخی IdPها تا زمانی که پارامتر resource در درخواست توکن نباشد، فیلد aud را حذف می‌کنند. اعتبارسنج باید توکن‌های بدون aud را رد کند، نه اینکه نبود آن را مجوزی برای همه در نظر بگیرد.
  • اشتباه‌گیری ناشی از بررسی نکردن iss. کلاینتی که پارامتر iss در پاسخ احراز هویت (RFC 9207) را با صادرکننده‌ی ثبت‌شده قبل از تغییر مسیر مقایسه نکند، ممکن است فریب بخورد و کد سرور معتبر را در اندپوینت توکن سرور مهاجم تحویل دهد. این خطا در سمت کلاینت است و سرور منبع راهی برای جبران آن ندارد.
  • تداخل در ارتقای دامنه‌ی دسترسی (Scope Upgrade Race). دو جریان ارتقای دسترسی هم‌زمان برای یک کاربر ممکن است هر دو تایید شوند و دو توکن با دسترسی‌های متفاوت بسازند. اعتبارسنج باید دسترسی را از همان توکنی که همراه درخواست آمده بخواند، نه اینکه «دامنه‌ی فعلی کاربر» را دوباره از دیتابیس استعلام کند — چنین کاری پنجره‌ی آسیب‌پذیری TOCTOU می‌سازد.
  • سرقت توکن ثبت‌نام. افشای registration_access_token به مهاجم امکان می‌دهد آدرس‌های بازهدایت را بازنویسی کند. این توکن‌ها را به‌صورت هش‌شده ذخیره کنید؛ از کلاینت بخواهید متن خام را در هر ویرایش بفرستد؛ و در صورت شک داشتن به افشا، آن‌ها را تعویض کنید.
  • قفل نکردن مقدار iss. اعتبارسنجی که هر مقداری را برای iss بپذیرد، به مهاجم امکان می‌دهد سرور احراز هویت خودش را بالا بیاورد، کلاینتی برای مخاطب هدف بسازد و توکن صادر کند. لیست authorization_servers در متادیتای منبع محافظت‌شده همان لیست مجاز شماست؛ آن را اعمال کنید.
  • تداخل در کش اطلاعات هویتی یا توکن. کلاینتی که اطلاعات ثبت را فقط بر اساس منبع کلیدگذاری کند، ممکن است هویت ثبت‌شده در یک سرور احراز هویت را به سرور دیگری نشان دهد. کلاینتی که توکن‌های دسترسی را فقط بر اساس صادرکننده کلیدگذاری کند، ممکن است توکن را در مخاطب اشتباهی بازپخش نماید. اطلاعات ثبت‌نام را بر اساس صادرکننده‌ی معتبر، و توکن‌های دسترسی را بر اساس (issuer, resource) کلیدگذاری کنید، و با تغییر صادرکننده، ثبت‌نام را از نو انجام دهید.
چرخش کلیدهای JWKSتازه‌سازی پیش از انقضای کلیدها انجام می‌شود
JWKS ROTATIONThe authorization server rolls its signing key from kid A to kid B on a schedule. A resource server that fetched JWKS once at boot starts rejecting every token at that moment. The production shape is a cached key set with a refresh job that overwrites the cache before the old keys expire, plus a fallback fetch on cache miss, so a token signed by the new key validates at 3 a.m. without a restart.سرور مجوزدهی/.well-known/jwks.jsonکلید امضا kid:Aکلید امضا kid:Bسرور منبع MCPتازه‌سازی پیش از انقضاکش JWKS: Aکش JWKS: A+Bدریافتتوکن kid:Aتوکن kid:Bدرخواست‌های کلاینتمعتبر
سرور احراز هویت طبق زمان‌بندی کلید امضای خود را از kid A به kid B تغییر می‌دهد. اگر سرور منبع JWKS را فقط یک‌بار هنگام بوت دریافت کرده باشد، در همان لحظه رد کردن تمام توکن‌ها را آغاز می‌کند. ساختار مناسبِ محیط عملیاتی، یک کش کلید با تسک تازه‌سازی دوره‌ای است که کش را قبل از منقضی شدن کلیدهای قدیمی به‌روزرسانی می‌کند؛ به‌علاوه واکشی رزرو هنگام یافت نشدن کلید در کش، تا توکن امضاشده با کلید جدید ساعت 3 بامداد بدون نیاز به راه‌اندازی مجدد تأیید شود.

استفاده از ابزارهای واقعی

فایل code/main.py این چرخه‌ی کامل را با ماژول‌های پیش‌فرض پایتون و با سه نقش AuthorizationServer، ResourceServer و Client اجرا می‌کند. مراحل کار:

از ریشه‌ی مخزن دستور زیر را اجرا کنید:

cd phases/13-tools-and-protocols/18-mcp-auth-production
python3 code/main.py
python3 -m unittest discover -s code/tests -v

دستور اول گزارش ثبت وابسته به صادرکننده و مراحل اعتبارسنجی توکن را نمایش می‌دهد. دستور دوم پاس شدن 18 بررسی مختلف را گزارش می‌کند. هیچ‌کدام از این دستورها پورتی روی شبکه باز نمی‌کنند و مدرک هویتی روی دیسک نمی‌نویسند.

  1. سرور احراز هویت متادیتای RFC 8414 را در آدرس /.well-known/oauth-authorization-server منتشر می‌کند.
  2. کلاینت MCP اندپوینت متادیتا را می‌خواند و گزینه‌های ثبت‌نام (client_id_metadata_document_supported برای CIMD و registration_endpoint برای DCR) و پشتیبانی از PKCE با روش S256 را بررسی می‌کند.
  3. کلاینت وجود ثبت قبلی در دامنه‌ی صادرکننده را بررسی می‌کند، و در غیر این صورت با سند متادیتای شناسه‌ی کلاینت HTTPS خود ثبت‌نام را انجام می‌دهد. روش منسوخ‌شده‌ی DCR هم به‌عنوان یک روش سازگاری مجزا و قابل آزمایش در دسترس است.
  4. کلاینت صادرکننده‌ی تاییدشده را ثبت می‌کند، چالش S256 می‌سازد، کد احراز هویت یک‌بارمصرف را به همراه iss می‌گیرد، صادرکننده‌ی بازگشتی را می‌سنجد، و کد را با مدل اعتبارسنج اصلی و شناساگر منبع resource مطابق RFC 8707 نقد می‌کند.
  5. کلاینت MCP با هدر Authorization: Bearer ... ابزاری را روی سرور MCP فراخوانی می‌کند.
  6. سرور MCP تابع validate را اجرا می‌کند و کلید امضا را از کش JWKS می‌خواند.
  7. سرور احراز هویت کلید را می‌چرخاند؛ تسک زمان‌بندی‌شده‌ی بازخوانی، کلیدهای جدید JWKS را دریافت و کش را به‌روز می‌کند.
  8. فراخوانی بعدی بدون نیاز به راه‌اندازی مجدد سرور با کلیدهای تازه اعتبارسنجی می‌شود، و توکن قبلی نیز در پنجره‌ی هم‌پوشانی کلیدها کماکان معتبر می‌ماند.
  9. تلاش برای بازپخش توکن روی منبع MCP دیگر با خطای 401، پیام audience mismatch و ارائه‌ی آدرس resource_metadata رد می‌شود.

توکن JWT در این سناریو از الگوریتم HS256 با کلید مشترک استفاده می‌کند (تا کد تنها با کتابخانه‌های پیش‌فرض پایتون اجرا شود). در سیستم‌های واقعی از الگوریتم‌های RS256 یا EdDSA با ساختار JWKS اشاره‌شده استفاده می‌شود؛ منطق اعتبارسنجی کاملاً یکسان است. از آنجا که IdP و سرور منبع در این سناریو در یک پردازش هستند، تابع refresh_jwks لیست کلیدهای سرور احراز هویت را مستقیم می‌خواند؛ در بستر شبکه، این کار با ارسال یک درخواست HTTP GET به jwks_uri انجام می‌گیرد.

خروجی این درس

این درس فایل outputs/skill-mcp-auth.md را تولید می‌کند. این مهارت با دریافت تنظیمات سرور MCP و ویژگی‌های IdP، پیکربندی امنیتی لازم را تولید می‌کند — شامل متادیتای منبع محافظت‌شده، مسیر مناسب ثبت‌نام (CIMD، پیش‌ثبت‌نام، یا fallback به DCR)، زمان‌بندی بازخوانی JWKS، نگاشت دامنه‌های دسترسی (Scope)، و قواعدی که در صورت پشتیبانی نکردن IdP از تمامی بندهای RFC باید اجرا شوند.

تمرین‌ها

  1. اسکریپت code/main.py را اجرا کنید و فرایند را دنبال نمایید. دقت کنید که IdP چگونه در گام 6 خروجی اسکریپت (گام 7 جریان بالا) یک کلید را می‌چرخاند، تسک زمان‌بندی‌شده‌ی refresh_jwks کلیدهای تازه را بازخوانی می‌کند، و هر دو توکن قبلی (در پنجره‌ی هم‌پوشانی) و توکن جدید بدون راه‌اندازی مجدد با موفقیت اعتبارسنجی می‌شوند.

  2. یک IdP جدید به لیست authorization_servers در متادیتای منبع محافظت‌شده اضافه کنید. توکنی با امضای این IdP جدید صادر کرده و بررسی کنید که سیستم اعتبارسنج آن را می‌پذیرد. سپس توکنی از یک IdP خارج از لیست بسازید و ببینید که سیستم آن را با پاسخ WWW-Authenticate: Bearer error="invalid_token", error_description="iss not allowed" رد می‌کند.

  3. یک بررسی محدودیت نرخ به تابع register_client اضافه کنید تا پیش از پذیرش هر درخواستی در واحد ثبت اجرا شود. از الگوریتم Token-bucket بر اساس هر IP مبدا استفاده کنید و وضعیت را در یک دیکشنری ساده با کلید IP نگه دارید.

  4. استاندارد RFC 7591 را بخوانید و دو فیلدی را که هندلر /register این درس بررسی نمی‌کند بیابید. اعتبارسنجی آن‌ها را به کد اضافه کنید. (راهنمایی: software_statement و ساختار پروتکل در redirect_uris.)

  5. یک سرور احراز هویت دوم به سیستم اضافه کنید. بررسی کنید که کلاینت مشخصات ثبت‌نام را به تفکیک صادرکننده نگه دارد و هرگز از توکن یا client_id سرور اول برای سرور دوم استفاده نکند.

  6. کارکرد صحیح جلوگیری از حمله‌ی DoS را بیازمایید. توکنی با یک kid تصادفی به تابع اعتبارسنجی بفرستید و بررسی کنید که refresh_jwks نهایتاً یک بار اجرا شود و به تعداد کلیدهای سرور احراز هویت افزوده نگردد. سپس عمداً مسیر جایگزین خطا را طوری تغییر دهید که کلید بسازد و بچرخاند؛ ببینید با ارسال هر توکن جعلی تعداد کلیدها چگونه بالا می‌رود — در انتها کد را به حالت اولیه‌ی واکشی مجدد بازگردانید.

  7. روش DCR منسوخ‌شده را برای هر دو نوع کلاینت native و web اجرا کنید. مطمئن شوید که کلاینت وب با آدرس بازهدایت HTTP و کلاینت native بدون آدرس بازهدایت محلی (Loopback) دقیق، پذیرفته نمی‌شوند و خطای لازم را دریافت می‌کنند.

اصطلاحات کلیدی

اصطلاح اصطلاح رایج معنای واقعی
ASM «سند متادیتای OAuth» ساختار JSON در مسیر /.well-known/oauth-authorization-server بر اساس RFC 8414
CIMD «آدرس متادیتای کلاینت» سند متادیتای شناسه‌ی کلاینت: یک آدرس HTTPS که به‌عنوان client_id استفاده می‌شود؛ سرور احراز هویت محتوای JSON را واکشی می‌کند. روش ارجح در MCP نسخه‌ی 2026-07-28
DCR «ثبت‌نام خودکار کلاینت» متد POST /register در RFC 7591؛ در MCP فعلی منسوخ شده و فقط برای سازگاری با سیستم‌های قدیمی وجود دارد
JWKS «کلیدهای عمومی اعتبارسنجی JWT» مجموعه‌کلید وب جیسون، ارائه‌شده از طریق jwks_uri و ایندکس‌شده بر اساس kid
چرخش در برابر بازخوانی (Rotate vs refresh) «به‌روزرسانی کلیدها» چرخش = ساخت و بازنشستگی کلیدهای امضا در سرور احراز هویت؛ بازخوانی = دریافت مجدد کلیدهای ارائه‌شده در سرور منبع. سرور منبع فقط بازخوانی می‌کند
شناساگر منبع (Resource indicator) «پارامتر مخاطب» پارامتر resource در RFC 8707 که توکن را به یک سرور خاص مقید می‌کند
کلیم aud «مخاطب (Audience)» کلیم داخل JWT که اعتبارسنج آن را با آدرس استاندارد منبع مقایسه می‌کند
بازپخش به مخاطب (Audience replay) «بازپخش توکن» توکن ساخته‌شده برای سرور A در سرور B استفاده شود؛ راهکار دفاعی بررسی کلیم مخاطب است (در مستندات: محدودسازی اختیارات توکن دسترسی)
نایب فریب‌خورده (Confused deputy) «سوءاستفاده از توکن پروکسی» سرور واسط MCP با شناسه‌ی کلاینت ثابت که توکن را بدون رضایت جداگانه‌ی کلاینت بازفروارد می‌کند؛ مسئله‌ای جدا از بازپخش به مخاطب
حمله‌ی اشتباه‌گیری (Mix-up attack) «ارسال به اندپوینت توکن اشتباه» فریب کلاینت برای نقد کردن کدِ گرفته‌شده از سرور معتبر در اندپوینت مهاجم؛ راه‌حل در سمت کلاینت با اعتبارسنجی iss طبق RFC 9207
لیست مجاز iss «سرورهای احراز هویت مورد اعتماد» مجموعه‌ای که در بخش authorization_servers متادیتای منبع محافظت‌شده معرفی شده است
پارامتر resource_metadata «محل پیدا کردن سند PRM» پارامتری در هدر WWW-Authenticate برای اشاره به آدرس متادیتای RFC 9728 در زمان خطای 401 یا 403
کلاینت عمومی (Public client) «کلاینت دسکتاپ یا مرورگر» کلاینت OAuth بدون امکان ذخیره‌ی client_secret؛ با PKCE محافظت می‌شود
هدر WWW-Authenticate «هدر پاسخ خطای 401/403» حاوی مقادیر Bearer error=... که کلاینت را برای جبران و بازیابی راهنمایی می‌کند

برای مطالعه‌ی بیشتر

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسیک کلاینت MCP برای همان منبع، صادرکننده سرور مجوزدهی متفاوتی را کشف می‌کند. با ثبت‌نام و توکن دسترسی فعلی خود چه کار باید بکند؟
  2. حین درسکدام رویکرد ثبت‌نام در پروفایل مجوزدهی MCP 2026-07-28 ترجیح داده شده است؟
  3. حین درسیک درخواست DCR منسوخ‌شده، کلاینت دسکتاپ لوپ‌بک را توصیف می‌کند. کدام متادیتا برای دسته‌بندی درست آن ضروری است؟
  4. حین درسچرا کلاینت قبل از تبدیل کد مجوز به توکن، مقدار iss استاندارد RFC 9207 را اعتبارسنجی می‌کند؟
  5. پس از درسیک سرور منبع MCP مجوز را برای بستر انتقال بی‌وضعیت فعلی در کجا باید کش کند؟
  6. پس از درسکدام شواهد استقرار، قوی‌ترین معیار قبل از اعتماد به سرور مجوزدهی است؟

سؤالات متداول

تفاوت CIMD با ثبت پویای کلاینت (DCR) در مشخصات جدید MCP چیست؟

روش CIMD فرآیند ثبت را از Push به Pull تبدیل می‌کند؛ کلاینت یک آدرس اینترنتی HTTPS با متادیتای مشخص را به‌عنوان client_id قرار می‌دهد و سرور احراز هویت آن را واکشی می‌کند، بنابراین نیازی به رجیسترار و ذخیره‌سازی وضعیت سرور ندارد، در حالی که DCR منسوخ شده و فقط برای سازگاری باقی مانده است.

چرا در زمان خالی بودن کش JWKS نباید چرخش کلید جدید انجام داد؟

چرخش کلید کار سرور احراز هویت است، نه سرور منبع. اگر در خطای نبود کلید (Cache Miss) کلید جدید بسازید، همچنان با توکن فعلی ناهمخوان خواهد بود و مهاجم می‌تواند با ارسال مقادیر ساختگی kid سرور را دچار حمله‌ی منع سرویس (DoS) کند؛ راه‌حل استاندارد تنها یک واکشی مجدد (Re-fetch) است.

کلیم aud در توکن‌های دسترسی چه حمله‌ای را متوقف می‌کند؟

بررسی دقیق کلیم aud بر اساس آدرس استاندارد سرور منبع، مانع بازپخش توکن صادرشده برای یک سرویس دیگر روی سرویس هدف می‌شود (محدودسازی اختیارات توکن دسترسی).

دفاع در برابر حمله‌ی اشتباه‌گیری (Mix-up Attack) بر عهده‌ی کدام بخش است؟

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

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.