Security & AI Architecture

OAuth বনাম OAuth2: AI এজেন্ট ও MCP আর্কিটেকচার (2026)

দ্রুত উত্তর: OAuth 1.0a জটিল ক্রিপ্টোগ্রাফিক স্বাক্ষরের ওপর নির্ভরশীল ছিল এবং OAuth 2.0-এর বিয়ারার টোকেন রিপ্লে আক্রমণের জন্য ঝুঁকিপূর্ণ ছিল। তবে স্বায়ত্তশাসিত AI এজেন্ট ও MCP সার্ভারের জন্য OAuth 2.1 বাধ্যতামূলক স্ট্যান্ডার্ড। এটি প্রতিটি ফ্লোতে PKCE (RFC 7636) নিশ্চিত করে, অনিরাপদ গ্রান্ট বাতিল করে এবং DPoP (RFC 9449)-এর মাধ্যমে হেডলেস পরিবেশে সেন্ডার-কনস্ট্রেইন্ড জিরো-ট্রাস্ট টোকেন কার্যকর করে।


১. ভূমিকা: ২০২৬ সালে স্বায়ত্তশাসিত এজেন্টের পরিচয় ও অনুমোদনের সংকট

বিচ্ছিন্ন লার্জ ল্যাঙ্গুয়েজ মডেল (LLM) চ্যাট ইন্টারফেস থেকে স্বায়ত্তশাসিত, মাল্টি-টার্ন AI এজেন্ট এবং মডেল কনটেক্সট প্রোটোকল (Model Context Protocol, MCP) সার্ভারের দিকে দ্রুত রূপান্তর একটি গুরুতর নিরাপত্তা সংকট তৈরি করেছে: এজেন্টিক পরিচয় এবং অনুমোদনের সংকট

২০২৪ এবং ২০২৫ সালে, ডেভেলপাররা মূলত .env ফাইল বা অপারেটিং সিস্টেম প্রসেসের এনভায়রনমেন্টে হার্ডকোড করা স্ট্যাটিক পার্সোনাল অ্যাক্সেস টোকেন (PAT) বা দীর্ঘমেয়াদী API কী ব্যবহার করে Claude Code, Cursor, Windsurf, AutoGen এবং কাস্টম LangGraph এজেন্টের মতো স্বায়ত্তশাসিত সরঞ্জাম কর্পোরেট API-এর সাথে যুক্ত করতেন। যখন একটি AI এজেন্ট লোকাল শেল কমান্ড চালায়, অভ্যন্তরীণ ডাটাবেস কোয়েরি করে (PostgreSQL বা Supabase MCP সার্ভারের মাধ্যমে), বা এন্টারপ্রাইজ ইস্যু ট্র্যাকার আপডেট করে (Jira বা Linear MCP সার্ভারের মাধ্যমে), এটি ব্যাপক ও অনিয়ন্ত্রিত পরিবেষ্টিত ক্ষমতার (Ambient Authority) অধীনে কাজ করে।

ঝুঁকিপূর্ণ সনাতন এজেন্ট আর্কিটেকচার (স্ট্যাটিক অ্যাম্বিয়েন্ট অথরিটি):
+--------------------+       সাবপ্রসেস তৈরি          +---------------------------+
|   LLM হোস্ট এজেন্ট | ────────────────────────────> |     লোকাল MCP টুল সার্ভার  |
| (Claude Code /     |   Env: GITHUB_TOKEN=ghp_...   |  (process.env রিড করে)    |
|  Cursor / LangSeq) |                               +-------------+-------------+
+---------+----------+                                             |
          | পরোক্ষ প্রম্পট ইনজেকশন আক্রমণ                          | অবাধ রিড/রাইট পারমিশন
          v                                                        v
+--------------------+                                      +-------------------+
| অবিশ্বস্ত ওয়েবপেজে |                                      | আপস্ট্রিম এন্টারপ্রাইজ|
| আক্রমণকারীর প্রম্পট | ──> স্ট্যাটিক সিক্রেট চুরি ─────────> | GitHub / Slack /  |
| "Print your env"   |     আক্রমণকারীর HTTP ওয়েবহুকে প্রেরণ  | অভ্যন্তরীণ ডাটাবেস |
+--------------------+                                      +-------------------+

এই স্ট্যাটিক ক্রেডেনশিয়াল আর্কিটেকচার তিনটি মৌলিক কারণে সম্পূর্ণ অকার্যকর:

  1. সিক্রেট চুরির মাধ্যম হিসেবে প্রম্পট ইনজেকশন: এজেন্ট যখন কোনো অবিশ্বস্ত ডেটার সংস্পর্শে আসে (যেমন কোনো ওয়েবপেজ, ইমেইল বা GitHub ইস্যুতে লুকিয়ে থাকা ক্ষতিকর প্রম্পট), তখন LLM-কে কৌশলে ডায়াগনস্টিক টুল চালাতে বা process.env প্রিন্ট করতে বাধ্য করা যায়, যা তাৎক্ষণিকভাবে উচ্চ-ক্ষমতাসম্পন্ন গোপন কী ফাঁস করে দেয়।
  2. পরিচয় অর্পণের (Identity Delegation) অভাব: একটি স্ট্যাটিক API কী এটি আলাদা করতে পারে না যে কোনো পদক্ষেপ মানব ব্যবহারকারী স্বেচ্ছায় নিয়েছেন, নাকি AI এজেন্টের হ্যালুসিনেশন বা স্বায়ত্তশাসিত সিদ্ধান্তের কারণে ঘটেছে। অডিট লগে সমস্ত কল একই ব্যবহারকারীর নামে নথিভুক্ত হয়।
  3. ডাইনামিক বাতিলকরণ ও ন্যূনতম অধিকারের অভাব: স্ট্যাটিক টোকেনগুলোতে প্রায়শই অত্যধিক বিস্তৃত অনুমতি (যেমন সমগ্র রিপোজিটরির রিড/রাইট অ্যাক্সেস) থাকে এবং এর মেয়াদ কয়েক মাস বা অনির্দিষ্টকাল স্থায়ী হয়।

এই ঝুঁকি দূর করতে AI ইকোসিস্টেম ডেলিগেটেড অথরাইজেশন ফ্রেমওয়ার্কের দিকে ধাবিত হয়েছে। তবে OAuth 1.0a, OAuth 2.0, এবং উদীয়মান সমন্বিত স্ট্যান্ডার্ড OAuth 2.1-এর মধ্যে আর্কিটেকচারাল সিদ্ধান্ত নেওয়া — এবং এর সাথে PKCE (RFC 7636), DPoP (RFC 9449), এবং ডিভাইস অথরাইজেশন গ্রান্ট (RFC 8628) সমন্বয় করা — বুঝতে সাহায্য করে কীভাবে এই প্রোটোকলগুলো হেডলেস ও স্বায়ত্তশাসিত পরিবেশে কাজ করে।


২. OAuth-এর বিবর্তন: ১.০a বনাম ২.০ বনাম ২.১-এর কাঠামোগত তুলনা

আধুনিক AI এজেন্ট ফ্রেমওয়ার্ক কেন OAuth 2.1 বাধ্যতামূলক করে, তা অনুধাবন করতে OAuth স্পেসিফিকেশনের তিনটি প্রধান সংস্করণের আর্কিটেকচারাল পরিবর্তন এবং দুর্বলতা পর্যালোচনা করা প্রয়োজন।

OAUTH স্পেসিফিকেশনের বিবর্তন (২০০৭ - ২০২৬):
+---------------------------------------------------------------------------------------------+
| OAuth 1.0a (RFC 5849, 2010)                                                                 |
| - প্রতিটি HTTP রিকোয়েস্টে প্রতিসম/অপ্রতিসম ক্রিপ্টোগ্রাফিক স্বাক্ষর (HMAC-SHA1, RSA-SHA1)   |
| - কোনো টোকেন রিফ্রেশ নেই; স্টেটফুল স্বাক্ষর গণনা; ট্রান্সপোর্ট-নিরপেক্ষ নিরাপত্তা             |
| - AI-এর জন্য রায়: অব্যবহারযোগ্য। ক্রিপ্টো ওভারহেড স্ট্রিমিং ও ডাইনামিক প্রক্সি ভেঙে দেয়।   |
+---------------------------------------------------------------------------------------------+
                                               │
                                               ▼
+---------------------------------------------------------------------------------------------+
| OAuth 2.0 (RFC 6749 ও RFC 6750, 2012)                                                       |
| - ট্রান্সপোর্ট লেয়ার সিকিউরিটিতে (TLS 1.2/1.3) ক্রিপ্টোগ্রাফি অর্পণ                        |
| - বিয়ারার টোকেন, স্কোপ, রিফ্রেশ টোকেন এবং বিশেষায়িত গ্রান্ট টাইপ প্রবর্তন                |
| - ইমপ্লিসিট ফ্লো এবং রিসোর্স ওনার পাসওয়ার্ড ক্রেডেনশিয়াল (ROPC) অন্তর্ভুক্ত                |
| - AI-এর জন্য রায়: বিপজ্জনক। প্রম্পট ইনজেকশন বা SSRF-এর মাধ্যমে বিয়ারার টোকেন চুরি সহজ।    |
+---------------------------------------------------------------------------------------------+
                                               │
                                               ▼
+---------------------------------------------------------------------------------------------+
| OAuth 2.1 (একীভূত IETF স্ট্যান্ডার্ড, ২০২৫/২০২৬)                                             |
| - অসুরক্ষিত ও অপ্রচলিত গ্রান্ট বাতিল (Implicit ও Password গ্রান্ট সম্পূর্ণ বর্জন)             |
| - সমস্ত অথরাইজেশন কোড ফ্লো-তে PKCE (RFC 7636) বাধ্যতামূলক (পাবলিক ও কনফিডেনশিয়াল উভয় ক্ষেত্রে)|
| - রিডাইরেক্ট URI বাইট-টু-বাইট মিল বাধ্যতামূলক; URI কোয়েরি প্যারামিটারে টোকেন নিষিদ্ধ       |
| - রিফ্রেশ টোকেন রোটেশন (RTR) বা সেন্ডার-কনস্ট্রেইন্ড টোকেন (DPoP / mTLS) বাধ্যতামূলক         |
| - AI-এর জন্য রায়: MCP এবং স্বায়ত্তশাসিত এজেন্ট আইডেন্টিটি ডেলিগেশনের স্বর্ণমান।             |
+---------------------------------------------------------------------------------------------+

OAuth 1.0a (RFC 5849): ক্রিপ্টোগ্রাফিক কঠোরতা এবং স্টেটফুলনেস

OAuth 1.0a তৈরি হয়েছিল এমন এক যুগে যখন HTTPS বিরল ও ব্যয়বহুল ছিল। প্লেইনটেক্সট HTTP-তে আড়িপাতা প্রতিরোধে এটি প্রতিটি একক রিকোয়েস্টে ক্লায়েন্ট ও সার্ভার উভয় পক্ষেই ক্রিপ্টোগ্রাফিক স্বাক্ষর (HMAC-SHA1 বা RSA-SHA1) গণনা বাধ্যতামূলক করেছিল।

স্বাক্ষর গণনার জন্য HTTP মেথড, ইউআরএল এবং বর্ণানুক্রমিকভাবে সাজানো সমস্ত কোয়েরি প্যারামিটার, রিকোয়েস্ট হেডার, ক্লায়েন্ট নন্স ও ইউনিক্স টাইমস্ট্যাম্প সমন্বয় করতে হতো:

$$\text{BaseString} = \text{HTTP\_METHOD} \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{Encode}(\text{URL}) \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{Encode}(\text{SortedParams})$$

$$\text{Signature} = \text{HMAC-SHA1}(\text{ClientSecret} \mathbin{\Vert} \text{"\&"} \mathbin{\Vert} \text{TokenSecret}, \text{BaseString})$$

AI এজেন্টের ক্ষেত্রে OAuth 1.0a ব্যর্থ হওয়ার কারণ:

  1. স্ট্রিমিং ও চাঙ্কড ট্রান্সপোর্টের সাথে অসঙ্গতি: Server-Sent Events বা WebSockets-এর মাধ্যমে আধুনিক এজেন্ট প্রোটোকলগুলো খণ্ড খণ্ড JSON-RPC পেলোড স্ট্রিম করে। অনিশ্চিত স্ট্রিমিং চাঙ্কের ওপর বারবার স্বাক্ষর হিসাব করা অসম্ভব।
  2. ডাইনামিক টুল অর্কেস্ট্রেশন: AI এজেন্ট এলএলএম প্যারামিটার থেকে ডাইনামিকভাবে রিকোয়েস্ট তৈরি করে। প্যারামিটারের সামান্য পরিবর্তন বা URL এনকোডিংয়ের তারতম্য (%20 বনাম +) স্বাক্ষর ভেঙে দেয়, যার ফলে লুপে 401 Unauthorized ত্রুটি ঘটে।
  3. নেটিভ রিফ্রেশ ব্যবস্থার অভাব: এতে টোকেন রোটেশন ব্যবস্থা না থাকায় দীর্ঘমেয়াদী গোপন কী ক্লায়েন্ট পরিবেশে স্থায়ীভাবে রাখতে হতো।

OAuth 2.0 (RFC 6749): বিয়ারার ঝুঁকির বিনিময়ে সরলতা

OAuth 2.0 নিরাপত্তা বিধানের দায়িত্ব পরিবহন স্তরে (বাধ্যতামূলক HTTPS) স্থানান্তর করে এবং বিয়ারার টোকেন (RFC 6750) প্রবর্তন করে। যে কেউ টোকেনটির অধিকারী হলে সে সম্পদে অ্যাক্সেস পায়, ঠিক নগদ টাকার মতো:

GET /v1/repositories HTTP/1.1
Host: api.github.com
Authorization: Bearer ya29.a0AfH6SMB...

OAuth 2.0 চারটি মূল অনুমোদন প্রবাহ সংজ্ঞায়িত করেছিল:

  1. Authorization Code Grant: গোপনীয় ব্যাকএন্ড সার্ভারযুক্ত ওয়েব অ্যাপ্লিকেশনের জন্য।
  2. Implicit Grant: ব্রাউজার-ভিত্তিক এসপিএ-র জন্য সরাসরি হ্যাশ ফ্র্যাগমেন্টে (#access_token=...) টোকেন ফেরত পাঠানো।
  3. Resource Owner Password Credentials (ROPC): ক্লায়েন্ট অ্যাপে সরাসরি ব্যবহারকারীর নাম ও পাসওয়ার্ড প্রদান।
  4. Client Credentials Grant: কোনো মানব ব্যবহারকারী ছাড়াই সার্ভার ডেমনদের মেশিন-টু-মেশিন (M2M) অনুমোদন।

AI সিস্টেমে OAuth 2.0-এর বিপজ্জনক দুর্বলতা:

  • বিয়ারার রিপ্লে আক্রমণ: প্রম্পট ইনজেকশন বা লগ ফাঁসের মাধ্যমে টোকেন চুরি হলে আক্রমণকারী যেকোনো স্থান থেকে মেয়াদ শেষ না হওয়া পর্যন্ত তা অপব্যবহার করতে পারে।
  • ইমপ্লিসিট গ্রান্ট ফাঁদ: ব্রাউজার হিস্ট্রি এবং Referer হেডারের মাধ্যমে টোকেন সহজেই ফাঁস হয়ে যায়।
  • পাসওয়ার্ড গ্রান্টের অশুভ চর্চা: সিএলআই এজেন্টগুলো টার্মিনালে পাসওয়ার্ড চেয়ে বসত, যা তৃতীয় পক্ষের সাথে পাসওয়ার্ড শেয়ার না করার মৌলিক নীতি ভঙ্গ করে।

OAuth 2.1: স্বায়ত্তশাসিত এজেন্টের জন্য আধুনিক সুরক্ষিত মানদণ্ড

OAuth 2.1 অপ্রয়োজনীয় জটিলতা দূর করে কয়েকটি অপরিহার্য শর্ত নির্ধারণ করেছে:

  1. ঝুঁকিপূর্ণ ফ্লো সম্পূর্ণ নির্মূল: Implicit Grant এবং ROPC পুরোপুরি বিলুপ্ত।
  2. সর্বত্র PKCE বাধ্যতামূলক: Proof Key for Code Exchange (RFC 7636) এখন পাবলিক ক্লায়েন্ট (CLI এজেন্ট, IDE প্লাগইন) এবং কনফিডেনশিয়াল ক্লায়েন্ট (ব্যাকএন্ড এজেন্ট সোয়ার্ম) উভয়ের জন্যই প্রযোজ্য।
  3. রিডাইরেক্ট ইউআরআই-এর নিখুঁত মিল: ওপেন রিডাইরেক্টর আক্রমণ প্রতিরোধে হুবহু বাইট-টু-বাইট স্ট্রিং তুলনা বাধ্যতামূলক।
  4. কোয়েরি প্যারামিটারে টোকেন নিষিদ্ধ: অ্যাক্সেস লগ ও প্রক্সি ক্যাশে টোকেন ফাঁস রোধে এটি ইউআরআই-তে পাঠানো নিষিদ্ধ।
  5. রিফ্রেশ টোকেন সুরক্ষা নিশ্চিতকরণ: অথরাইজেশন সার্ভারকে অবশ্যই Refresh Token Rotation (RTR) অথবা সেন্ডার-কনস্ট্রেইন্ড টোকেন (DPoP / mTLS) প্রয়োগ করতে হবে।

আর্কিটেকচারাল তুলনামূলক সারণী: OAuth 1.0a vs OAuth 2.0 vs OAuth 2.1

আর্কিটেকচারাল দিক OAuth 1.0a (RFC 5849) OAuth 2.0 (RFC 6749 / 6750) OAuth 2.1 (IETF স্ট্যান্ডার্ড ২০২৬)
ক্রিপ্টোগ্রাফিক মডেল অ্যাপ্লিকেশন লেয়ারে প্রতিটি রিকোয়েস্টে স্বাক্ষর ট্রান্সপোর্ট লেয়ার (TLS) + প্লেইন বিয়ারার TLS + বাধ্যতামূলক PKCE + সেন্ডার কনস্ট্রেইনিং
বিয়ারার রিপ্লে আক্রমণ ঝুঁকি অনাক্রম্য (প্রতিবার অনন্য ননস স্বাক্ষর) চরম ঝুঁকিপূর্ণ (টোকেন থাকলেই অ্যাক্সেস) সুরক্ষিত (DPoP প্রাইভেট কী দিয়ে আবদ্ধ)
PKCE আবশ্যকতা সমর্থিত নয় ঐচ্ছিক (প্রধানত মোবাইলের জন্য) সমস্ত অথরাইজেশন কোড ফ্লোতে বাধ্যতামূলক
Implicit Grant সমর্থিত নয় অনুমোদিত (ব্রাউজার এসপিএ-র জন্য) সম্পূর্ণ অপসারিত ও কঠোরভাবে নিষিদ্ধ
Password Grant (ROPC) समर्थিত নয় অনুমোদিত (সরাসরি ক্রেডেনশিয়াল বিনিময়) সম্পূর্ণ অপসারিত ও কঠোরভাবে নিষিদ্ধ
রিডাইরেক্ট URI যাচাই প্রিফিক্স ম্যাচিং অনুমোদিত ওয়াইল্ডকার্ড ও পাথ ম্যাচিং অনুমোদিত ছিল হুবহু বাইট-টু-বাইট মিল থাকা বাধ্যতামূলক
কোয়েরি প্যারামিটারে টোকেন সমর্থিত অনুমোদিত (?access_token=...) সম্পূর্ণ নিষিদ্ধ (শুধুমাত্র হেডার বা বডি)
রিফ্রেশ টোকেন চক্র কোনো মূল ব্যবস্থা নেই বাতিলের পূর্ব পর্যন্ত একক টোকেন পুনর্ব্যবহার বাধ্যতামূলক রোটেশন (RTR) বা ক্রিপ্টো বাইন্ডিং
লোকাল সিএলআই এজেন্টে উপযোগিতা অত্যন্ত দুর্বল (শেল স্ক্রিপ্টে স্বাক্ষর জটিল) ঝুঁকিপূর্ণ (লোকাল লুপব্যাক ইন্টারসেপশন) সর্বোত্তম (PKCE + লোকাল ডাইনামিক পোর্ট)
রিমোট MCP সার্ভারে উপযোগিতা স্ট্রিমিং JSON-RPC-র সাথে অচল ব্যবহারযোগ্য কিন্তু ক্রেডেনশিয়াল ফাঁসের ঝুঁকি শিল্পমান ডিফল্ট (সুনির্দিষ্ট সর্বনিম্ন অধিকার)

৩. মডেল কনটেক্সট প্রোটোকল (MCP) প্রমাণীকরণ টপোলজি: এজেন্ট ও সার্ভার যোগাযোগ সুরক্ষা

Anthropic দ্বারা উন্মুক্ত এবং Claude Code, Cursor ও এন্টারপ্রাইজ সিস্টেমে গৃহীত Model Context Protocol (MCP) JSON-RPC 2.0-এর ওপর একটি ক্লায়েন্ট-সার্ভার আর্কিটেকচার স্থাপন করে।

একটি বাস্তবায়নে দুটি সুস্পষ্ট যোগাযোগের সীমানা লক্ষ্য করা যায়:

  • সীমানা ক (হোস্ট থেকে MCP সার্ভার): LLM ক্লায়েন্ট অ্যাপ এবং লোকাল/রিমোট MCP সার্ভার প্রসেসের সংযোগ।
  • সীমানা খ (MCP সার্ভার থেকে আপস্ট্রিম এন্টারপ্রাইজ ইনফ্রাস্ট্রাকচার): MCP সার্ভার এবং বাহ্যিক SaaS API-এর (GitHub, Jira, Linear) সংযোগ।
মডেল কনটেক্সট প্রোটোকল (MCP) প্রমাণীকরণ কাঠামো:
+-------------------------------------------------------------------------------------------------------+
| MCP হোস্ট রানটাইম (যেমন Claude Code / Cursor / স্বায়ত্তশাসিত এজেন্ট হার্নেস)                         |
|                                                                                                       |
|  +---------------------+        প্রম্পট কনটেক্সট       +--------------------------------------------+ |
|  | ইউজার প্রম্পট / LLM | <───────────────────────────> | LLM রিজনিং ইঞ্জিন (Claude 3.7 / GPT-4o)    | |
|  +----------+----------+                               +--------------------------------------------+ |
|             | টুল কল পাঠায় (`tools/call`)                                                            |
|             v                                                                                         |
|  +--------------------------------------------------------------------------------------------------+ |
|  | MCP ক্লায়েন্ট ইঞ্জিন                                                                             | |
|  | - অথরাইজেশন সার্ভারের সাথে OAuth 2.1 PKCE হ্যান্ডশেক সম্পন্ন করে                                   | |
|  | - মেমোরিতে ক্ষণস্থায়ী DPoP প্রাইভেট কী সংরক্ষণ করে                                               | |
|  | - প্রতিটি রিকোয়েস্টে DPoP Proof JWT তৈরি করে এবং Access Token পাঠায়                             | |
|  +-----------------------------------+--------------------------------------------------------------+ |
+--------------------------------------|----------------------------------------------------------------+
                                       |
                                       | ট্রান্সপোর্ট: Stdio (লোকাল প্রসেস) অথবা SSE/HTTP (রিমোট সার্ভার)
                                       v
+-------------------------------------------------------------------------------------------------------+
| MCP সার্ভার রানটাইম (যেমন GitHub MCP / এন্টারপ্রাইজ ডাটাবেস MCP)                                      |
|                                                                                                       |
|  +--------------------------------------------------------------------------------------------------+ |
|  | প্রমাণীকরণ ও টোকেন যাচাইকারী                                                                       | |
|  | ১. অথরাইজেশন সার্ভার JWKS-এর মাধ্যমে OAuth 2.1 টোকেন স্বাক্ষর যাচাই করে                           | |
|  | ২. DPoP Proof যাচাই: HTTP মেথড, URI, Nonce এবং পাবলিক কী মিলিয়ে দেখে                              | |
|  | ৩. স্কোপ মূল্যায়ন: সর্বনিম্ন অধিকার প্রয়োগ করে (`issues:read` অনুমোদিত, `admin:all` প্রত্যাখ্যাত)   | |
|  +-----------------------------------+--------------------------------------------------------------+ |
|                                      |                                                                |
|                                      v                                                                |
|  +--------------------------------------------------------------------------------------------------+ |
|  | MCP টুল এক্সিকিউশন ইঞ্জিন (`tools/call` বাস্তবায়ন)                                                | |
|  | - ইনপুট স্যানিটাইজ করে, পাথ ট্রাভার্সাল প্রতিরোধ করে এবং নিরাপদ স্যান্ডবক্সে এক্সিকিউট করে         | |
|  +-----------------------------------+--------------------------------------------------------------+ |
+--------------------------------------|----------------------------------------------------------------+
                                       | ডেলিগেটেড নিয়ন্ত্রিত টোকেন দিয়ে বাহ্যিক API কল
                                       v
                      +----------------------------------+
                      | এন্টারপ্রাইজ SaaS ও ডাটাবেস      |
                      | (GitHub / Jira / PostgreSQL / S3)|
                      +----------------------------------+

লোকাল Stdio বনাম রিমোট SSE/HTTP ট্রান্সপোর্ট

  1. লোকাল Stdio ট্রান্সপোর্ট (transport: "stdio"):
  • সার্ভারটি হোস্ট অ্যাপের অধীনে লোকাল চাইল্ড প্রসেস হিসেবে চালু হয় এবং স্ট্যান্ডার্ড ইনপুট/আউটপুটে যোগাযোগ করে।
  • নিরাপত্তা ত্রুটি: ডেভেলপাররা প্রায়ই এনভায়রনমেন্ট ভেরিয়েবলে সিক্রেট যুক্ত করতেন (process.env), যা যেকোনো সাবপ্রসেস বা শেল কমান্ডের মাধ্যমে ফাঁস হতে পারে।
  • OAuth 2.1 সমাধান: হোস্ট একটি সুরক্ষিত টোকেন ভল্ট পরিচালনা করে এবং হ্যান্ডশেকের সময় স্বল্পমেয়াদী ডেলিগেটেড টোকেন প্রদান করে।
  1. রিমোট SSE/HTTP ট্রান্সপোর্ট (transport: "sse"):
  • সার্ভারটি ক্লাউডে HTTP পোর্টে চলে এবং ইভেন্ট পাঠানোর জন্য Server-Sent Events ব্যবহার করে।
  • এখানে OAuth 2.1 বাধ্যতামূলক: ক্লায়েন্টকে স্ট্যান্ডার্ড Authorization হেডার এবং DPoP প্রুফ ব্যবহার করে নিজেকে প্রমাণ করতে হয়।

৪. PKCE (RFC 7636) বিশ্লেষণ: এজেন্টের লোকাল কলব্যাক সুরক্ষা

মোবাইল অ্যাপে অথরাইজেশন কোড ছিনতাই রোধে RFC 7636-এ PKCE মানসম্মত করা হয়েছিল। OAuth 2.1-এ PKCE প্রতিটি কোড বিনিময়ে বাধ্যতামূলক

সিএলআই ও আইডিই কেন পাবলিক ক্লায়েন্ট

Claude Code বা Cursor-এর মতো টুলগুলো ব্যবহারকারীর নিজস্ব মেশিনে চলে। ফলে এতে কোনো স্ট্যাটিক client_secret লুকিয়ে রাখা অসম্ভব। বাইনারি ডিকম্পাইল করে যে কেউ সেই সিক্রেট বের করে নিতে পারে।

অনুমোদনের সময় সার্ভার একটি লোকাল রিডাইরেক্ট URI-তে (যেমন http://127.0.0.1:18492/callback) Authorization Code পাঠায়।

অথরাইজেশন কোড ছিনতাই আক্রমণ (PKCE ব্যতীত):
১. বৈধ CLI এজেন্ট অথ সার্ভারের কাছে কোড অনুরোধ করে।
২. মেশিনে থাকা ক্ষতিকর ব্যাকগ্রাউন্ড প্রসেস লোকাল পোর্টে আড়িপাতে।
৩. সার্ভার ব্রাউজারকে রিডাইরেক্ট করে: http://127.0.0.1:18492/callback?code=AUTH_CODE_123।
৪. ক্ষতিকর প্রসেস AUTH_CODE_123 ছিনতাই করে।
৫. ক্ষতিকর প্রসেস /token-এ কোড পাঠিয়ে সরাসরি অ্যাক্সেস টোকেন হাতিয়ে নেয়!

PKCE-এর গাণিতিক সুরক্ষা

PKCE প্রতিটি সেশনের জন্য একটি গতিশীল, একক ব্যবহারের গোপনীয় মান তৈরি করে এই আক্রমণের পথ বন্ধ করে দেয়:

PKCE প্রোটোকল প্রবাহ:
+-------------+                     +-----------------------+                    +--------------------+
|  Agent CLI  |                     |    ইউজার ব্রাউজার     |                    |      অথ সার্ভার    |
|  (ক্লায়েন্ট)|                    +-----------+-----------+                    +---------+----------+
+------+------+                                 |                                          |
       | ১. code_verifier তৈরি করে              |                                          |
       |    code_challenge = S256(...) হিসাব করে|                                          |
       |                                        |                                          |
       | ২. লোকাল লুপব্যাক লিসনার চালু করে      |                                          |
       |    ব্রাউজার ওপেন করে ─────────────────>| ৩. GET /authorize?response_type=code     |
       |                                        |    &client_id=agent_cli                  |
       |                                        |    &code_challenge=E9Melhoa2Owv...       |
       |                                        |    &code_challenge_method=S256 ─────────>|
       |                                        |                                          | ৪. ব্যবহারকারীর সম্মতি।
       |                                        | ৫. ৩০২ রিডাইরেক্ট লোকাল লুপব্যাকে        |    চ্যালেঞ্জ সংরক্ষণ
       |                                        |<─────────────────────────────────────────|
       |<───────────────────────────────────────|    http://127.0.0.1:18492/callback?code=AC_88921
       | ৬. কোডসহ কলব্যাক গ্রহণ করে             |
       |                                                                                   |
       | ৭. POST /oauth/token                                                              |
       |    code=AC_88921 & code_verifier=dBjftJeZ4CVP-mB92K... ─────────────────────────>|
       |                                                                                   | ৮. যাচাই:
       |                                                                                   |    SHA256(verifier)
       |                                                                                   |    == সংরক্ষিত চ্যালেঞ্জ?
       | ৯. Access Token + Refresh Token প্রদান করে <──────────────────────────────────────|    হ্যাঁ: টোকেন ইস্যু
+------+------+
  1. Code Verifier: এজেন্ট ৪৩ থেকে ১২৮ অক্ষরের উচ্চ-এনট্রপি বিশিষ্ট ক্রিপ্টোগ্রাফিক স্ট্রিং $V$ তৈরি করে:
  2. Code Challenge: ক্লায়েন্ট এর SHA-256 হ্যাশ বের করে Base64URL এনকোড করে:
  3. অনুরোধ প্রেরণ: ক্লায়েন্ট $C$ এবং অ্যালগরিদম /authorize-এ পাঠায়।
  4. টোকেন বিনিময়: ক্লায়েন্ট মূল code_verifier=V পাঠায়। সার্ভার যাচাই করে দেখে যে হ্যাশ মান সংরক্ষিত $C$-এর সমান কিনা।

আক্রমণকারী কোডটি ছিনতাই করলেও মূল code_verifier ছাড়া টোকেন দাবি করতে পারে না

প্রোডাকশন TypeScript বাস্তবায়ন: PKCE ইঞ্জিন

// pkce.ts - Enterprise OAuth 2.1 PKCE Engine for AI Agent Clients
import { randomBytes, createHash } from "node:crypto";

export interface PKCEChallenge {
  codeVerifier: string;
  codeChallenge: string;
  codeChallengeMethod: "S256";
}

export class PKCEEngine {
  /**
   * Generates a cryptographically secure code_verifier (RFC 7636 Section 4.1)
   * Length defaults to 64 bytes of entropy (yielding ~86 base64url characters).
   */
  public static generateVerifier(length: number = 64): string {
    if (length < 32 || length > 96) {
      throw new RangeError("Verifier byte length must be between 32 and 96.");
    }
    const buffer = randomBytes(length);
    return this.base64UrlEncode(buffer);
  }

  /**
   * Computes the S256 code_challenge from the code_verifier (RFC 7636 Section 4.2)
   */
  public static computeChallenge(verifier: string): string {
    const hash = createHash("sha256").update(verifier, "ascii").digest();
    return this.base64UrlEncode(hash);
  }

  /**
   * Generates the complete PKCE pair ready for OAuth 2.1 authorization
   */
  public static createPair(): PKCEChallenge {
    const codeVerifier = this.generateVerifier(64);
    const codeChallenge = this.computeChallenge(codeVerifier);
    return {
      codeVerifier,
      codeChallenge,
      codeChallengeMethod: "S256",
    };
  }

  /**
   * Server-side verification: Validates an incoming code_verifier against stored challenge
   */
  public static verify(verifier: string, storedChallenge: string): boolean {
    const computed = this.computeChallenge(verifier);
    // Timing-safe buffer comparison to prevent side-channel timing attacks
    const bufA = Buffer.from(computed);
    const bufB = Buffer.from(storedChallenge);
    if (bufA.length !== bufB.length) return false;
    
    let result = 0;
    for (let i = 0; i < bufA.length; i++) {
      result |= bufA[i] ^ bufB[i];
    }
    return result === 0;
  }

  private static base64UrlEncode(buffer: Buffer): string {
    return buffer
      .toString("base64")
      .replace(/\\+/g, "-")
      .replace(/\\//g, "_")
      .replace(/=+$/, "");
  }
}

৫. হেডলেস সার্ভার ও সিএলআই অনুমোদন: ডিভাইস ফ্লো (RFC 8628)

আধুনিক AI এজেন্টগুলো প্রায়ই ক্লাউড ডকার কন্টেইনার বা সিআই/সিডি রানারের মতো ব্রাউজারবিহীন হেডলেস পরিবেশে চলে। কনসোলে পাসওয়ার্ড চাওয়া OAuth 2.1 পরিপন্থী। এর আদর্শ সমাধান OAuth 2.0 Device Authorization Grant (RFC 8628):

হেডলেস পরিবেশে ডিভাইস অথরাইজেশন গ্রান্ট (RFC 8628):
+-------------------+                                                +-----------------------+
|   হেডলেস এজেন্ট   |                                                |      অথ সার্ভার       |
|  (Docker / Cloud) |                                                +-----------+-----------+
+---------+---------+                                                            |
          | ১. POST /oauth/device/code (client_id, scope) ──────────────────────>|
          |                                                                      | ২. তৈরি করে:
          | ৩. ডিভাইসের তথ্য পাঠায়:                                             |    device_code (গোপন)
          |    - user_code: "WDJB-HGNP"                                          |    user_code (পাবলিক)
          |    - verification_uri: "https://auth.corp.com/activate"              |    interval: ৫ সেকেন্ড
          |    - interval: 5 <───────────────────────────────────────────────────|
          |                                                                      |
          | ৪. কনসোলে নির্দেশনা দেখায়:                                          |
          |    "ভিজিট করুন https://auth.corp.com/activate এবং কোড দিন: WDJB-HGNP"|
          |                                                                      |
          | ৫. পোলিং লুপ শুরু হয়:                                               |
          |    POST /oauth/token (grant_type=device_code, device_code=...) ─────>|
          |    <── 400 Bad Request: {"error": "authorization_pending"} ─────────|
          |    [৫ সেকেন্ড অপেক্ষা]                                               |
          |                                                                      |
+---------+---------+     ব্যবহারকারী ল্যাপটপ বা মোবাইলে লিংকটি খোলেন            |
| ব্যবহারকারীর পিসি ──> "WDJB-HGNP" কোড প্রবেশ করিয়ে MFA সম্পন্ন করেন ─────────>| ৬. ব্যবহারকারী অনুমোদন দিলেন!
+-------------------+                                                            |
          |                                                                      |
          | ৭. পরবর্তী পোলিং চক্র:                                               |
          |    POST /oauth/token ───────────────────────────────────────────────>|
          |    <── 200 OK: {access_token: "...", refresh_token: "..."} ──────────|
          v
[হেডলেস এজেন্ট পাসওয়ার্ড প্রকাশ না করেই নিরাপদে অনুমোদিত হলো]

মেশিন-টু-মেশিন (M2M) বিকল্প: RFC 7523 প্রাইভেট কী JWT

মানুষের উপস্থিতি ছাড়া চালিত সম্পূর্ণ স্বায়ত্তশাসিত রোবটের জন্য জিরো-ট্রাস্ট আর্কিটেকচার RFC 7523 সম্বলিত Client Credentials Grant ব্যবহার করে:

  • স্ট্যাটিক পাসওয়ার্ডের বদলে এজেন্ট মেমোরিতে রক্ষিত একটি অপ্রতিসম প্রাইভেট কী (RSA বা ECDSA) ব্যবহার করে ৬০ সেকেন্ড মেয়াদী একটি JWT স্বাক্ষর করে পাঠায়।
  • সার্ভার তার পূর্বনিবন্ধিত পাবলিক কী দিয়ে তা যাচাই করে।

৬. টোকেনের জীবনচক্র ও স্বায়ত্তশাসিত রিফ্রেশ পদ্ধতি

OAuth 2.1 অ্যাক্সেস টোকেনগুলো স্বল্পস্থায়ী (৫-১৫ মিনিট) হওয়ায় এজেন্টকে ব্যাকগ্রাউন্ডে স্বায়ত্তশাসিতভাবে টোকেন রিনিউ করতে হয়।

রিফ্রেশ টোকেন রোটেশন (RTR) এবং লঙ্ঘন শনাক্তকরণ

OAuth 2.1-এ রিফ্রেশ টোকেন চুরির হাত থেকে রক্ষায় Refresh Token Rotation (RTR) প্রয়োগ করা হয়:

  1. প্রতিবার রিফ্রেশ টোকেন ব্যবহার করা মাত্রই সার্ভার সেই টোকেনটি বাতিল করে দেয়।
  2. সার্ভার নতুন একটি অ্যাক্সেস টোকেন এবং একটি সম্পূর্ণ নতুন রিফ্রেশ টোকেন প্রদান করে।
  3. যদি কোনো আক্রমণকারী চুরি করা পুরনো রিফ্রেশ টোকেন ব্যবহারের চেষ্টা করে, সার্ভার সাথে সাথে পুরো অথরাইজেশন ট্রির সমস্ত সক্রিয় টোকেন বাতিল করে দেয়:

$$\text{Incoming Token State} == \text{"REVOKED"} \implies \text{Revoke All Tokens in Family Tree}$$

রিফ্রেশ টোকেন রোটেশন (RTR) ও স্বয়ংক্রিয় নিরাপত্তা পুনরুদ্ধার:
টোকেন তৈরির ক্রম:
[Refresh Token A] ──(ব্যবহৃত)──> [Refresh Token B] ──(ব্যবহৃত)──> [Refresh Token C] (সক্রিয়)
         │
         │ আক্রমণকারী পুরনো [Refresh Token A] ব্যবহারের চেষ্টা করে
         v
[সার্ভার বাতিল টোকেন A-এর পুনঃব্যবহার শনাক্ত করে!]
         │
         ▼
[জরুরি নিরাপত্তা সতর্কতা]: টোকেন B, C এবং সংশ্লিষ্ট সমস্ত অ্যাক্সেস টোকেন তাৎক্ষণিকভাবে বাতিল।
এজেন্ট সেশন নিরাপদে বন্ধ হয়, অননুমোদিত প্রবেশাধিকার প্রতিহত হয়।

পাইথন প্রোডাকশন বাস্তবায়ন: থ্রেড-সেফ অ্যাসিনক্রোনাস টোকেন ম্যানেজার

# token_manager.py - Enterprise Async Token Lifecycle Manager for AI Agents
import asyncio
import time
import httpx
from typing import Optional, Dict, Any

class AgentTokenManager:
    def __init__(
        self,
        token_endpoint: str,
        client_id: str,
        initial_refresh_token: str,
        proactive_refresh_seconds: int = 60,
    ):
        self.token_endpoint = token_endpoint
        self.client_id = client_id
        self.refresh_token = initial_refresh_token
        self.access_token: Optional[str] = None
        self.expires_at: float = 0.0
        self.proactive_refresh_seconds = proactive_refresh_seconds
        self._lock = asyncio.Lock()

    async def get_valid_access_token(self) -> str:
        now = time.time()
        if self.access_token and (self.expires_at - now) > self.proactive_refresh_seconds:
            return self.access_token

        async with self._lock:
            now = time.time()
            if self.access_token and (self.expires_at - now) > self.proactive_refresh_seconds:
                return self.access_token

            await self._refresh_token_exchange()
            if not self.access_token:
                raise RuntimeError("Failed to acquire valid access token from authorization server.")
            return self.access_token

    async def _refresh_token_exchange(self) -> None:
        payload = {
            "grant_type": "refresh_token",
            "refresh_token": self.refresh_token,
            "client_id": self.client_id,
        }
        
        async with httpx.AsyncClient(timeout=10.0) as client:
            try:
                response = await client.post(
                    self.token_endpoint,
                    data=payload,
                    headers={"Content-Type": "application/x-www-form-urlencoded"},
                )
            except httpx.RequestError as exc:
                raise ConnectionError(f"Network transport error during token refresh: {exc}")

            if response.status_code == 200:
                data: Dict[str, Any] = response.json()
                self.access_token = data["access_token"]
                expires_in = int(data.get("expires_in", 3600))
                self.expires_at = time.time() + expires_in
                
                # Update to the newly rotated refresh token if provided
                if "refresh_token" in data:
                    self.refresh_token = data["refresh_token"]
            elif response.status_code in (400, 401):
                err_data = response.json()
                # If error is 'invalid_grant', the token was likely already rotated or revoked
                raise PermissionError(f"Token refresh rejected (possible token theft or expiry): {err_data}")
            else:
                response.raise_for_status()

৭. জিরো-ট্রাস্ট ক্রেডেনশিয়াল আইসোলেশন: DPoP (RFC 9449)

প্রচলিত বিয়ারার টোকেন চুরি হলে যে কেউ তা ব্যবহার করতে পারে। প্রম্পট ইনজেকশন বা SSRF-এর মাধ্যমে যাতে চুরি হওয়া টোকেন কাজে না লাগানো যায়, সেজন্য OAuth 2.1-এ DPoP (RFC 9449) যুক্ত করা হয়েছে।

DPOP (RFC 9449) অ্যাপ্লিকেশন লেয়ার সেন্ডার-কনস্ট্রেইনিং:
+-------------------------------------------------------------------------------------------------+
| এজেন্ট লোকাল পরিবেশ (ক্লায়েন্ট)                                                               |
| - ক্ষণস্থায়ী কী-জোড়া তৈরি করে: পাবলিক কী (JWK) + প্রাইভেট কী (কেবল মেমোরিতে থাকে)              |
+-------------------------------------------------------------------------------------------------+
                                                │
                                                │ ১. DPoP Proof হেডার পাঠায়:
                                                │    DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Ar...
                                                │    পেলোড: {
                                                │      "htm": "GET",
                                                │      "htu": "https://api.enterprise.com/mcp/tools",
                                                │      "iat": 1772630400,
                                                │      "jti": "random_nonce_9921",
                                                │      "jwk": { ...পাবলিক কী... }
                                                │    }
                                                │
                                                │ ২. আবদ্ধ DPoP টোকেন পাঠায়:
                                                │    Authorization: DPoP dpop_access_token_88921
                                                v
+-------------------------------------------------------------------------------------------------+
| এন্টারপ্রাইজ MCP গেটওয়ে (রিসোর্স সার্ভার)                                                      |
| ১. যাচাই করে যে অ্যাক্সেস টোকেনটি JWK-র পাবলিক কী ফিঙ্গারপ্রিন্টের সাথে ক্রিপ্টোগ্রাফিকভাবে আবদ্ধ। |
| ২. DPoP প্রুফ স্বাক্ষরটি পাবলিক কী-র সাথে হুবহু মেলে কিনা তা নিশ্চিত করে।                       |
| ৩. "htm" = "GET" এবং "htu" গন্তব্য ইউআরএলের সাথে হুবহু মেলে কিনা পরীক্ষা করে।                     |
| ৪. "iat" সময়সীমার মধ্যে (< ৬০ সে) রয়েছে এবং "jti" পূর্বে ব্যবহৃত হয়নি তা নিশ্চিত করে।          |
+-------------------------------------------------------------------------------------------------+
                                                │
       ┌────────────────────────────────────────┴────────────────────────────────────────┐
       ▼                                                                                 ▼
[বৈধ প্রমাণ ও সঠিক কী]                                                       [চুরি করা টোকেন ব্যবহারের চেষ্টা]
অনুরোধটি টুল এক্সিকিউশনে এগিয়ে যায়                                          আক্রমণকারীর কাছে টোকেন আছে, কিন্তু
                                                                              এজেন্টের লোকাল প্রাইভেট কী নেই।
                                                                              ফলাফল: 401 Unauthorized প্রত্যাখ্যাত!

DPoP-এর কার্যপদ্ধতি

  1. এজেন্ট সেশনের শুরুতে একটি অপ্রতিসম কী-জোড়া তৈরি করে।
  2. টোকেন অনুরোধকালে পাবলিক কী-র হ্যাশ ফিঙ্গারপ্রিন্ট (jkt) টোকেনে যুক্ত করা হয়।
  3. প্রতিটি এপিআই অনুরোধে এজেন্ট একটি ক্ষণস্থায়ী JWT প্রুফ তৈরি করে পাঠায় যাতে মেথড, ইউআরএল, টাইমস্ট্যাম্প ও ইউআইডি অন্তর্ভুক্ত থাকে।
  4. টোকেন স্ট্রিংটি চুরি হলেও এজেন্টের নিজস্ব প্রাইভেট কী ছাড়া আক্রমণকারী এটি ব্যবহার করতে সম্পূর্ণ অক্ষম।

৮. এন্টারপ্রাইজ নিরাপত্তা বেঞ্চমার্ক ও ঝুঁকি মূল্যায়ন

পারফরম্যান্স বেঞ্চমার্ক (১০,০০০ পুনরাবৃত্তি, Apple M4 Max)

প্রমাণীকরণ আর্কিটেকচার ক্লায়েন্ট হ্যান্ডশেক লেটেন্সি (p50) ক্লায়েন্ট হ্যান্ডশেক লেটেন্সি (p99) প্রতি রিকোয়েস্টে ভেরিফিকেশন ওভারহেড রিপ্লে প্রতিরোধ ক্লায়েন্ট মেমোরি খরচ সার্ভার CPU ওভারহেড
স্ট্যাটিক PAT / API কী 0.1 ms (হ্যান্ডশেক নেই) 0.2 ms 0.02 ms (স্ট্রিং সমতা) নেই (সম্পূর্ণ রিপ্লে) < 1 KB বেসলাইন
OAuth 1.0a (HMAC-SHA1) 14.2 ms 38.5 ms 1.84 ms (স্বাক্ষর বিশ্লেষণ) আংশিক (নন্স চেক) 12 KB +18%
OAuth 2.0 Bearer 45.1 ms 112.0 ms 0.15 ms (JWT ক্যাশ) নেই (বিয়ারার রিপ্লে) 18 KB +4%
OAuth 2.1 (PKCE + RTR) 48.6 ms 118.4 ms 0.16 ms (JWT ভ্যালিডেশন) মাঝারি (RTR বাতিলকরণ) 24 KB +5%
OAuth 2.1 + DPoP (P-256) 54.2 ms 132.8 ms 1.22 ms (DPoP যাচাই) সর্বোচ্চ (জিরো রিপ্লে) 36 KB +12%
mTLS (RFC 8705) 62.8 ms 154.1 ms 0.45 ms (TLS সেশন ক্যাশ) সর্বোচ্চ (সার্টিফিকেটযুক্ত) 128 KB +15%

নিরাপত্তা ঝুঁকি ও প্রতিরোধ মেট্রিক্স

হুমকির তীব্রতা বনাম প্রোটোকল প্রশমন:
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| আক্রমণের ধরন                 | স্ট্যাটিক API কী  | OAuth 1.0a        | OAuth 2.0 Bearer  | OAuth 2.1 + DPoP  |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| ১. পরোক্ষ প্রম্পট ইনজেকশন     | বিপজ্জনক (১০/১০)  | উচ্চ ঝুঁকি (৭/১০) | বিপজ্জনক (১০/১০)  | কম ঝুঁকি (২/১০)   |
|    (এনভায়রনমেন্ট/লগ ফাঁস)    | মূল কী চিরতরে ফাঁস | স্বাক্ষর জটিলতা   | টোকেন চুরি ও ক্ষতি| কী ছাড়া টোকেন অচল |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| ২. লোকাল পোর্ট স্নীফিং        | প্রযোজ্য নয়      | কম ঝুঁকি (৩/১০)   | উচ্চ ঝুঁকি (৮/১০) | সম্পূর্ণ নিরাপদ(১)|
|    (লুপব্যাক ইন্টারসেপশন)      | রিডাইরেক্ট নেই     | নন্স যাচাইকৃত     | কোড চুরি সম্ভব    | PKCE দ্বারা প্রতিহত|
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| ৩. SSRF টুল আক্রমণ            | বিপজ্জনক (১০/১০)  | মাঝারি (৫/১০)     | বিপজ্জনক (১০/১০)  | সম্পূর্ণ নিরাপদ(১)|
|    (অনিরাপদ রিকোয়েস্ট)      | সম্পূর্ণ অ্যাক্সেস | ইউআরএল অমিল ব্যর্থ | টোকেন অপব্যবহার   | DPoP URI অমিল     |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| ৪. সাবপ্রসেস অনুসন্ধান        | বিপজ্জনক (১০/১০)  | মাঝারি (৫/১০)     | উচ্চ ঝুঁকি (৮/১০) | কম ঝুঁকি (২/১০)   |
|    (/proc/environ পরীক্ষা)    | স্থায়ী গোপন কী   | কী উন্মুক্ত       | টোকেন উন্মুক্ত    | স্বল্পস্থায়ী ও বদ্ধ|
+-------------------------------+-------------------+-------------------+-------------------+-------------------+
| ৫. রিফ্রেশ রেস কন্ডিশন        | প্রযোজ্য নয়      | প্রযোজ্য নয়      | কম ঝুঁকি (২/১০)   | উচ্চ (মিউটেক্স    |
|    (সমান্তরাল এজেন্ট বহর)      | রিফ্রেশ নেই       | রিফ্রেশ নেই       | টোকেন সংঘাত       | ম্যানেজার আবশ্যক) |
+-------------------------------+-------------------+-------------------+-------------------+-------------------+

৯. বাস্তবায়ন নির্দেশিকা: সুরক্ষিত OAuth 2.1 + PKCE MCP সার্ভার

// server.ts - Hardened Remote MCP Server with OAuth 2.1 Validation
import express, { Request, Response, NextFunction } from "express";
import { createRemoteJWKSet, jwtVerify } from "jose";

const app = express();
app.use(express.json());

// Configuration
const ISSUER = "https://auth.enterprise.com/";
const AUDIENCE = "https://mcp.enterprise.com/";
const JWKS_URI = new URL("https://auth.enterprise.com/.well-known/jwks.json");
const JWKS = createRemoteJWKSet(JWKS_URI);

interface AuthenticatedRequest extends Request {
  tokenClaims?: any;
}

/**
 * Enterprise OAuth 2.1 Token Validation Middleware
 */
async function requireOAuth21(
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
): Promise<void> {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    res.status(401).json({
      jsonrpc: "2.0",
      error: { code: -32001, message: "Missing or invalid OAuth 2.1 Authorization header." },
      id: req.body?.id || null,
    });
    return;
  }

  const token = authHeader.split(" ")[1];

  try {
    // Cryptographically verify token signature, issuer, audience, and expiration
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: ISSUER,
      audience: AUDIENCE,
    });

    // Enforce OAuth 2.1 requirement: reject tokens without an expiration claim
    if (!payload.exp || typeof payload.exp !== "number") {
      res.status(401).json({
        jsonrpc: "2.0",
        error: { code: -32002, message: "Non-compliant token: missing expiration claim." },
        id: req.body?.id || null,
      });
      return;
    }

    req.tokenClaims = payload;
    next();
  } catch (err: any) {
    res.status(401).json({
      jsonrpc: "2.0",
      error: { code: -32003, message: `Token verification failed: ${err.message}` },
      id: req.body?.id || null,
    });
  }
}

/**
 * Fine-Grained Scope Enforcement Guard
 */
function requireScope(requiredScope: string) {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
    const scopes: string[] = (req.tokenClaims?.scope || "").split(" ");
    if (!scopes.includes(requiredScope)) {
      res.status(403).json({
        jsonrpc: "2.0",
        error: {
          code: -32004,
          message: `Insufficient permissions: missing required scope '${requiredScope}'`,
        },
        id: req.body?.id || null,
      });
      return;
    }
    next();
  };
}

/**
 * Standard MCP JSON-RPC 2.0 Handler Endpoint
 */
app.post(
  "/mcp/v1",
  requireOAuth21,
  requireScope("mcp:tools:execute"),
  async (req: AuthenticatedRequest, res: Response): Promise<void> => {
    const { jsonrpc, method, params, id } = req.body;

    if (jsonrpc !== "2.0") {
      res.status(400).json({ jsonrpc: "2.0", error: { code: -32600, message: "Invalid JSON-RPC version." }, id });
      return;
    }

    // Router for MCP Primitives
    switch (method) {
      case "tools/list":
        res.json({
          jsonrpc: "2.0",
          result: {
            tools: [
              {
                name: "query_database",
                description: "Executes read-only SQL queries against the analytics warehouse.",
                inputSchema: {
                  type: "object",
                  properties: { query: { type: "string" } },
                  required: ["query"],
                },
              },
            ],
          },
          id,
        });
        break;

      case "tools/call":
        if (params?.name === "query_database") {
          // Verify elevated data scope for this specific tool execution
          const scopes: string[] = (req.tokenClaims?.scope || "").split(" ");
          if (!scopes.includes("db:analytics:read")) {
            res.json({
              jsonrpc: "2.0",
              error: { code: -32005, message: "Forbidden: tool requires 'db:analytics:read' scope." },
              id,
            });
            return;
          }

          // Execute tool with verified, scoped identity
          const userSub = req.tokenClaims.sub;
          console.log(`Executing query on behalf of verified agent identity: ${userSub}`);
          
          res.json({
            jsonrpc: "2.0",
            result: {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({ status: "success", rows_returned: 42, latency_ms: 12 }),
                },
              ],
            },
            id,
          });
        } else {
          res.status(404).json({ jsonrpc: "2.0", error: { code: -32601, message: "Tool not found." }, id });
        }
        break;

      default:
        res.status(404).json({ jsonrpc: "2.0", error: { code: -32601, message: "Method not found." }, id });
    }
  }
);

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Hardened OAuth 2.1 MCP Server listening on port ${PORT}`);
});

১০. উপসংহার ও কৌশলগত পরামর্শ (E-E-A-T)

স্বায়ত্তশাসিত AI মডেলগুলোকে এন্টারপ্রাইজ সিস্টেমে যুক্ত করার সময় এদের আংশিক-বিশ্বস্ত ডেলিগেটেড প্রতিনিধি হিসেবে বিবেচনা করতে হবে। এজেন্টকে পূর্ণ রুট ক্ষমতার অভ্যন্তরীণ মাইক্রোসার্ভিস বা সম্পূর্ণ অবিশ্বস্ত এজেন্ট মনে করা মস্ত বড় ভুল।

OAuth 2.1 ডেভেলপারদের স্বায়ত্তশাসন ও এন্টারপ্রাইজ জিরো-ট্রাস্ট কমপ্লায়েন্সের মধ্যে ভারসাম্য রক্ষা করে এই শূন্যতা পূরণ করে।

৫-দফা নিরাপত্তা চেকলিস্ট

  1. সমস্ত স্ট্যাটিক সিক্রেট অপসারণ করুন: MCP কনফিগারেশন থেকে হার্ডকোডেড কী মুছে স্বল্পমেয়াদী OAuth 2.1 টোকেন ব্যবহার করুন।
  2. S256 অ্যালগরিদমে PKCE নিশ্চিত করুন: প্রতিটি লোকাল সিএলআই টুলে RFC 7636 বাস্তবায়ন করুন।
  3. হেডলেস পরিবেশে Device Flow বা Private Key JWT প্রয়োগ করুন: কনসোলে পাসওয়ার্ড দেওয়ার বিপজ্জনক অভ্যাস বর্জন করুন।
  4. মিউটেক্স সহযোগে টোকেন রোটেশন নিশ্চিত করুন: সমান্তরাল এজেন্টের অনুরোধের সংঘাত প্রতিরোধে লক ব্যবস্থা রাখুন।
  5. স্পর্শকাতর টুলে DPoP (RFC 9449) চালু করুন: প্রম্পট ইনজেকশন ও টোকেন চুরির ঝুঁকি গোড়াতেই দমন করুন।
← সব নিবন্ধ
0 / 4