এই পর্বে যা শিখবেন
- OAuth-এর চার ভূমিকা, আর delegated access আসলে কী
- Authorization আর authentication-এর পার্থক্য, আর কেন access token পরিচয়ের প্রমাণ নয়
- Authorization code flow + PKCE ধাপে ধাপে, RFC 7636-এর test vector চালিয়ে
- Scope, consent, access token, refresh token,
state,nonceআর redirect URI-র নিয়ম - OIDC, SSO, federation আর SAML-এর সাথে তুলনা
- ডেভেলপারের সাধারণ ভুল, consent phishing আর তার প্রতিরক্ষা ও detection
১ · Password ধার দেওয়ার সমস্যা আর চারটি ভূমিকা
ধরুন একটি ক্যালেন্ডার সাজানোর app আপনার email থেকে মিটিংয়ের দাওয়াত পড়তে চায়। পুরোনো দিনে এমন app সরাসরি বলত: “আপনার email-এর password দিন।” ভেবে দেখুন এতে কী কী হয়:
- App পুরো account পায়, শুধু দাওয়াত পড়ার অধিকার নয়। সে mail মুছতে, পাঠাতে, password বদলাতেও পারে।
- App-কে password সেভ করে রাখতে হয়। App-এর database ফাঁস হলে আপনার password-ও ফাঁস।
- একটি app-এর অধিকার তোলার একমাত্র উপায় password বদলানো, আর তাতে অন্য সব app-ও একসাথে বন্ধ হয়।
- MFA চালু থাকলে এই পথ চলেই না, আবার MFA বন্ধ করলে account দুর্বল হয়।
বাড়ির মালিক প্লাম্বারকে আলমারির আসল চাবি দেন না। তিনি দারোয়ানকে বলে দেন: “এই লোক আজ সন্ধ্যা পর্যন্ত শুধু ছাদের ট্যাংকের কাছে যেতে পারবে।” দারোয়ান একটি পাস দেন। পাসে কাজের সীমা আছে, মেয়াদ আছে, চাইলে মাঝপথে বাতিলও করা যায়, আর আসল চাবি মালিকের কাছেই থাকে। OAuth ঠিক এই পাসের ব্যবস্থা।
এই ধারণার নাম delegated access: আপনি নিজের অধিকারের একটি ছোট অংশ, নিজের password না দিয়ে, অন্য একটি app-কে ধার দেন। এর চারটি ভূমিকা মুখস্থ না করে ছবিতে দেখুন।
OAuth-এর চার ভূমিকা
Password থাকে শুধু authorization server-এর কাছে
| ভূমিকা | কে | পাসের গল্পে |
|---|---|---|
| Resource owner | যাঁর ডেটা, সাধারণত ব্যবহারকারী | বাড়ির মালিক |
| Client | যে app ডেটা চায় | প্লাম্বার |
| Authorization server | Login আর consent নিয়ে token দেয় | দারোয়ান, যিনি পাস দেন |
| Resource server | যে API ডেটা রাখে, token দেখে কাজ করে | ছাদের দরজার পাহারাদার |
OAuth-এর কাজ শুরু হয় ২০০৬-২০০৭ সালের দিকে, যখন কয়েকটি web সেবা বুঝতে পারে যে password চাওয়ার এই রীতি বন্ধ করতে একটি সাধারণ নিয়ম দরকার। OAuth 1.0 RFC হয় ২০১০ সালে (RFC 5849). OAuth 2.0 প্রকাশিত হয় ২০১২ সালে: RFC 6749 মূল কাঠামো, RFC 6750 bearer token-এর নিয়ম। আর পরিচয়ের স্তর OpenID Connect Core 1.0 চূড়ান্ত হয় ২০১৪ সালে।
২ · Authorization আর authentication — কেন OAuth একা login নয়
পর্ব ১৫-র ভাষায় authentication মানে “আপনি কে” আর authorization মানে “আপনি কী করতে পারেন”। এই পার্থক্যটিই OAuth নিয়ে সবচেয়ে বড় ভুলের উৎস। OAuth 2.0 তৈরি হয়েছে authorization-এর জন্য। এর token বলে: “এই token-এর ধারক এই API-তে এই কাজগুলো করতে পারে।” Token হাতে থাকা client জানে না মানুষটি কে, এমনকি মানুষটি এই মুহূর্তে উপস্থিত কি না, সেটিও জানে না।
হোটেলের রুম-কার্ড ভাবুন। কার্ডটি ৩০৪ নম্বর দরজা খোলে। কিন্তু যিনি কার্ড হাতে দাঁড়িয়ে আছেন, তিনি কি অতিথি নিজে, নাকি তাঁর কুড়িয়ে পাওয়া কেউ? কার্ড সেটি বলতে পারে না। পরিচয় জানতে লাগে পরিচয়পত্র, যাতে নাম, ছবি আর কে ইস্যু করেছে লেখা। Access token হলো রুম-কার্ড, আর ID token হলো পরিচয়পত্র।
তাহলে “Login with …” বোতামটি কীভাবে চলে? OpenID Connect (OIDC) OAuth 2.0-র ওপরে একটি পরিচয়ের স্তর যোগ করে। একই flow, শুধু client একটি বাড়তি জিনিস পায়: ID token, যা বিশেষভাবে এই client-এর জন্য issuer-এর স্বাক্ষর করা একটি বিবৃতি: “এই মানুষটি এইমাত্র আমার কাছে login করেছেন।”
কোন token কার পড়ার জন্য
ভুল প্রাপকের হাতে token ব্যবহারই বহু ভুলের মূল
৩ · Authorization code flow আর PKCE — ধাপে ধাপে
আজকের ডিফল্ট পথ authorization code flow. মূল চালাকি হলো: ব্রাউজারের পথে token নয়, পাঠানো হয় একটি ছোট, এক-বার-ব্যবহারের কোড। আসল token আসে আলাদা, সরাসরি একটি অনুরোধে। ব্রাউজারের ঠিকানা-বার, history আর redirect পথে তাই token পড়ে থাকে না।
কিন্তু কোডটিও তো ব্রাউজারের পথে যায়। কেউ সেটি মাঝপথে পেলে কী হবে? মোবাইল app আর ব্রাউজার app-এ তো গোপন client_secret রাখা যায় না, কারণ কোড ব্যবহারকারীর যন্ত্রে থাকে। এ ধরনের app-কে বলে public client, আর server-এ গোপন রাখতে পারে এমন app-কে confidential client. এখানেই আসে PKCE (RFC 7636, ২০১৫, উচ্চারণ “পিক্সি”).
Client প্রতিবার একটি নতুন গোপন মান বানায় (code verifier). শুরুর অনুরোধে পাঠায় তার SHA-256 ছাপ (code challenge). শেষে কোড ভাঙানোর সময় আসল মানটি দেখায়। ফলে যে কোডটি মাঝপথে পেল, কিন্তু verifier জানে না, সে কোডটি ভাঙাতে পারে না। এক দিকের hash function এখানে সত্যিকারের কাজে লাগছে (পর্ব ৯, Hash আর MAC).
নিচের উদাহরণে verifier আর challenge RFC 7636-এর প্রকাশিত test vector, বাকি নাম আর ঠিকানা কাল্পনিক।
- Client
app.example.comverifier বানায় - ব্রাউজার
redirectবার্তা বয়ে নেয় - Auth server
auth.example.comlogin, consent, token - API
api.example.comtoken দেখে কাজ
code_verifier = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
code_challenge = BASE64URL(SHA256(code_verifier))
= E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
state = state-a1
nonce = n-7f3a
https://auth.example.com/authorize ?response_type=code &client_id=toy-app &redirect_uri=https://app.example.com/callback &scope=openid calendar.read &state=state-a1 &nonce=n-7f3a &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256 (বোঝার সুবিধায় লাইন ভাঙা আর URL-encoding ছাড়া দেখানো)
toy-app চায়:
✓ আপনার পরিচয় (openid)
✓ ক্যালেন্ডার পড়া (calendar.read)
[ অনুমতি দিন ] [ বাতিল ]
সার্ভারে জমা: code → client_id, redirect_uri,
scope, user, code_challenge
Location: https://app.example.com/callback
?code=example-code-123
&state=state-a1
session-এ জমা state : state-a1
URL-এ আসা state : state-a1
ফল : মিলেছে, জমা মান মুছে ফেলা হলো
POST /token
grant_type=authorization_code
code=example-code-123
redirect_uri=https://app.example.com/callback
client_id=toy-app
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
BASE64URL(SHA256(verifier))
= E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM মিলেছে
{ "access_token": "example-access-token",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "example-refresh-token",
"id_token": "example-id-token" }
GET /v1/events Host: api.example.com Authorization: Bearer example-access-token API যাচাই করে: মেয়াদ, aud = এই API, scope = calendar.read
| code_verifier | |
| code | |
| Token | |
| Password |
- Client প্রস্তুতি নেয়। Verifier-এর জন্য ৩২ byte অনিশ্চিত মান (২৫৬ bit entropy), তার S256 ছাপ, আর আলাদা
stateওnonce. সবই এই একটি login-এর জন্য। - ব্রাউজার auth server-এ যায়। অনুরোধে client-এর নাম, ফেরার ঠিকানা, scope আর challenge আছে; verifier নেই। পথের কেউ URL দেখলেও verifier বের করতে পারবে না, কারণ SHA-256 উল্টানো যায় না।
- Login আর consent. ব্যবহারকারী শুধু auth server-এর পাতায় password আর MFA দেন, তারপর দেখেন app কী চাইছে। Server একটি কোড বানিয়ে তার সাথে challenge জমা রাখে।
- কোড ফিরে আসে। Server শুধু আগে নিবন্ধিত redirect URI-তে পাঠায়, হুবহু মিলিয়ে। সাথে
stateঅপরিবর্তিত ফেরত আসে। কোডটি মেয়াদে খুব ছোট আর একবারের জন্য। - Client
stateমেলায়। এই উত্তর কি আমার ব্রাউজারের শুরু করা login-এর? না মিললে এখানেই থামে। এটি CSRF ঠেকায়। - কোড ভাঙানো। Client সরাসরি TLS পথে কোড আর আসল verifier পাঠায়। এই প্রথম verifier client-এর বাইরে গেল, আর গেল শুধু auth server-এ।
- Server S256 মিলিয়ে token দেয়। Verifier থেকে ছাপ হিসাব করে জমা challenge-এর সাথে মেলায়। সাথে কোড, client আর redirect URI-ও মেলায়। সব মিললে কোড বাতিল করে token দেয়।
- API ডাক। Access token নিয়ে client API ডাকে। API নিজে যাচাই করে token তার জন্যই কি না, মেয়াদ আছে কি না, এই কাজের scope আছে কি না। লক্ষ করুন, পুরো পথে password client-এর কাছে আসেনি।
এবার S256 হিসাবটি সত্যিকারের hashlib দিয়ে করে RFC-র প্রকাশিত মানের সাথে মিলিয়ে নিন।
import base64, hashlib, hmac, secrets
def b64url(raw):
# base64url, শেষের '=' বাদ (RFC 7636 অনুযায়ী)
return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
def s256(verifier):
return b64url(hashlib.sha256(verifier.encode("ascii")).digest())
# RFC 7636 Appendix B-র প্রকাশিত test vector, কোনো গোপন মান নয়
octets = [116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125,
216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240,
91, 88, 5, 88, 83, 132, 141, 121]
verifier = b64url(bytes(octets))
expected = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
challenge = s256(verifier)
print("verifier :", verifier)
print("length :", len(verifier))
print("challenge:", challenge)
print("RFC match:", challenge == expected)
# Token endpoint যা করে: verifier থেকে হিসাব করে তুলনা
def server_check(stored, sent):
return hmac.compare_digest(stored, s256(sent))
wrong = "x" * 43
print("right verifier accepted:", server_check(challenge, verifier))
print("wrong verifier accepted:", server_check(challenge, wrong))
# বাস্তবে verifier প্রতিবার নতুন আর অনিশ্চিত হয়
fresh = secrets.token_urlsafe(32)
print("fresh verifier length :", len(fresh))আউটপুট: challenge: E9Melhoa2OwvFrEMTJgu আর RFC match: True, অর্থাৎ হিসাব RFC 7636 Appendix B-র সাথে হুবহু মিলেছে। ঠিক verifier-এ right verifier accepted: True, ভুলটিতে wrong verifier accepted: False. length : 43 কেন? ৩২ byte = ২৫৬ bit, আর base64url-এর প্রতি অক্ষরে ৬ bit থাকে, তাই ২৫৬ ÷ ৬ = ৪২.৬৭, মানে ৪৩ অক্ষর। RFC-র সীমা ৪৩ থেকে ১২৮ অক্ষর। তুলনায় hmac.compare_digest ব্যবহার করা হয়েছে, যাতে constant-time comparison হয়।
PKCE কী ঠেকায় না: এটি কোড চুরি আর কোড বসানো ঠেকায়। কিন্তু ব্যবহারকারী নকল পাতায় password দিলে, app-এ XSS থাকলে, বা token চুরি হলে PKCE কিছু করতে পারে না। আর plain method-এ challenge আর verifier একই মান, তাই S256-ই ব্যবহার করুন। PKCE কাজ করছে কি না জানার উপায়: নিজের test পরিবেশে verifier ছাড়া বা ভুল verifier দিয়ে token চাইলে server-কে অবশ্যই invalid_grant ফেরত দিতে হবে।
৪ · Scope, consent আর token-এর জীবনচক্র
Scope হলো client-এর চাওয়া অধিকারের তালিকা, যেমন calendar.read. Consent হলো ব্যবহারকারী বা admin-এর সেই তালিকায় সম্মতি। মনে রাখার মতো একটি সূত্র আছে:
অর্থাৎ scope ব্যবহারকারীর অধিকার বাড়ায় না, শুধু কমায়। সাধারণ কর্মীর token-এ admin.write scope থাকলেও API-কে আলাদাভাবে দেখতে হবে কর্মীটি admin কি না। এটিই least privilege-এর OAuth রূপ (পর্ব ৩, নিরাপত্তার মূলনীতি).
Scope-এর খেলনা হিসাব আর একটি প্রতিষ্ঠানের consent নীতি একসাথে চালিয়ে দেখুন। Scope-এর নাম, ঝুঁকির মাত্রা আর app-গুলো কাল্পনিক।
// খেলনা সিমুলেটর: token কী পারবে, আর consent কোথায় আটকাবে
const risk = { "profile.read": "low", "calendar.read": "low",
"mail.read": "high", "mail.send": "high",
"files.readwrite.all": "high", "offline_access": "medium" };
const policy = { userMayGrant: ["low"], verifiedOnly: true };
// কার্যকর অনুমতি = app চেয়েছে ∩ user অনুমোদন করেছে ∩ user নিজে পারেন
const effective = (asked, granted, userCan) =>
asked.filter(s => granted.includes(s) && userCan.includes(s));
function consent(app) {
const high = app.scopes.filter(s => risk[s] !== "low");
if (policy.verifiedOnly && !app.verified)
return "BLOCK: unverified publisher";
if (high.length) return "ADMIN REVIEW: " + high.join(", ");
return "USER MAY CONSENT";
}
const apps = [
{ name: "MeetingPlanner", verified: true,
scopes: ["profile.read", "calendar.read"] },
{ name: "PDF-Helper", verified: false,
scopes: ["profile.read", "mail.read", "offline_access"] },
{ name: "ExpenseSync", verified: true,
scopes: ["profile.read", "files.readwrite.all"] },
];
for (const a of apps) console.log(a.name.padEnd(15), consent(a));
const userCan = ["profile.read", "calendar.read"];
const eff = effective(["profile.read", "calendar.read", "mail.send"],
["profile.read", "mail.send"], userCan);
console.log("effective:", eff.join(", "));
console.log("mail.send allowed:", eff.includes("mail.send"));আউটপুট: MeetingPlanner USER MAY CONSENT, কারণ যাচাইকৃত প্রকাশক আর দুটিই কম ঝুঁকির scope. PDF-Helper BLOCK: unverified publisher আর ExpenseSync ADMIN REVIEW: files.readwrite.all. শেষ দুই লাইন effective: profile.read আর mail.send allowed: false সূত্রটি দেখায়: app mail.send চেয়েছে আর পেয়েছেও, কিন্তু ব্যবহারকারীর নিজের সেই অধিকার নেই, তাই কার্যকর অধিকারে সেটি নেই।
Access token আর refresh token
| বিষয় | Access token | Refresh token |
|---|---|---|
| কাজ | API ডাকা | নতুন access token আনা |
| কোথায় যায় | Resource server-এ | শুধু authorization server-এ |
| মেয়াদ | ছোট, মিনিট থেকে ঘণ্টা | লম্বা, তাই ঝুঁকি বেশি |
| চুরি হলে | মেয়াদ পর্যন্ত ক্ষতি | Rotation বা revocation না থাকলে দীর্ঘ ক্ষতি |
| প্রতিরক্ষা | aud সীমিত, scope ছোট, মেয়াদ ছোট | Rotation বা sender-constraint, এনক্রিপ্ট করে রাখা |
সাধারণ access token একটি bearer token: যার হাতে আছে, সে-ই ব্যবহার করতে পারে, ঠিক নগদ টাকার মতো। তাই RFC 9700 বলে public client-এর refresh token হয় sender-constrained হবে, নয়তো refresh token rotation থাকবে। Rotation-এ প্রতিবার ব্যবহারে নতুন refresh token আসে, আর পুরোনোটি বাতিল হয়। কেউ পুরোনোটি আবার ব্যবহার করলে server বুঝে যায় দুই পক্ষের হাতে একই token আছে, আর পুরো পরিবার বাতিল করতে পারে। এই reuse ঘটনাটি একটি শক্তিশালী detection সংকেত।
৫ · state, nonce আর redirect URI — তিন পাহারাদার
তিনটি মান দেখতে ছোট, কিন্তু প্রতিটি আলাদা ঝুঁকি ঠেকায়। এদের গুলিয়ে ফেলা খুব সাধারণ ভুল।
| মান | কোথায় মেলে | কী ঠেকায় | না থাকলে কী হয় |
|---|---|---|---|
state | Client, callback-এ | CSRF: অন্যের শুরু করা flow-র উত্তর আপনার session-এ বসা | ব্যবহারকারী অজান্তে অন্য কারও account-এ যুক্ত হতে পারেন |
nonce | Client, ID token-এর ভেতরে | ID token-এর replay বা অন্য session-এ বসানো | পুরোনো ID token দিয়ে login মেনে নেওয়া সম্ভব |
| Redirect URI | Authorization server | কোড বা token ভুল ঠিকানায় যাওয়া | কোড বাইরের কোনো সাইটে পৌঁছাতে পারে |
RFC 9700-এর স্পষ্ট নিয়ম: authorization server হুবহু string মিলিয়ে redirect URI যাচাই করবে। ব্যতিক্রম শুধু native app-এর localhost ঠিকানার port নম্বর। শুরুটা মেলা, wildcard বা “একই domain হলেই চলবে” ধরনের নিয়ম চলবে না, কারণ একই domain-এর কোনো open redirect পাতা বা ভুলে যাওয়া subdomain-ও তখন ফেরার ঠিকানা হয়ে যায়।
নিচের খেলনা server দুই ধরনের যাচাই পাশাপাশি দেখায়, আর client-এর দিকে state একবারই ব্যবহার হওয়া দেখায়। URL-গুলো কাল্পনিক example domain-এর।
// খেলনা authorization server, in-memory ডেটা, কোনো network নেই
const client = { id: "toy-app",
redirects: ["https://app.example.com/callback"] };
function naivePrefix(uri) { // অনিরাপদ ধরন
return uri.startsWith("https://app.example.com");
}
function exactMatch(uri) { // RFC 9700: exact string match
return client.redirects.includes(uri);
}
const tests = [
"https://app.example.com/callback",
"https://app.example.com/callback/",
"https://app.example.com.lookalike.example/cb",
"http://app.example.com/callback",
];
console.log("exact prefix redirect_uri");
for (const u of tests) {
const e = exactMatch(u) ? "OK " : "NO ";
const p = naivePrefix(u) ? "OK " : "NO ";
console.log(e + " " + p + " " + u);
}
// Client-এর দিক: state একবারই মেলে
const pending = { "sess-1": "state-a1", "sess-2": "state-b2" };
function callback(sess, state) {
const saved = pending[sess];
if (!saved) return "REJECT: no pending login";
if (saved !== state) return "REJECT: state mismatch";
delete pending[sess]; // একবার ব্যবহারের পরে মুছে দিন
return "ACCEPT: exchange code";
}
console.log("---");
[["sess-1", "state-a1"], ["sess-1", "state-a1"],
["sess-2", "state-a1"], ["sess-9", "state-a1"]]
.forEach(([s, v], i) => console.log(i + 1, callback(s, v)));আউটপুট বলছে, নিবন্ধিত ঠিকানা ছাড়া exact যাচাই বাকি তিনটিই বাদ দিয়েছে। Prefix যাচাই কিন্তু OK দিয়েছে শেষে / থাকা ঠিকানা আর app.example.com-কেও, যা সম্পূর্ণ আলাদা একটি domain. নিচের অংশে 1 ACCEPT: exchange code প্রথমবারে; একই মান আবার পাঠালে 2 REJECT: no pending login; অন্য session-এর state দিলে 3 REJECT: state mismatch; অজানা session-এ 4 REJECT: no pending login. সুরক্ষার মূল কথা: state ব্রাউজার session-এর সাথে বাঁধা, অনিশ্চিত, আর একবারের।
PKCE থাকলে state কি লাগবে না? RFC 9700 অনুযায়ী CSRF ঠেকানো client-এর দায়িত্ব। Auth server PKCE সমর্থন করে বলে নিশ্চিত হলে PKCE, অথবা OIDC-তে nonce এই সুরক্ষা দিতে পারে, নয়তো এক-বার-ব্যবহারের state লাগবে। বাস্তবে অনেক দল সহজ নিয়ম মেনে তিনটিই রাখে, কারণ state-এ login-এর পরের পাতার হিসাবও রাখা যায়, আর একটি স্তর ভুল হলে অন্যটি থাকে।
৬ · কোন flow কখন — বাদ পড়া পথ আর আজকের পথ
| Flow | কখন | অবস্থা |
|---|---|---|
| Authorization code + PKCE | Web, মোবাইল, ব্রাউজার app, যেখানে মানুষ আছেন | ডিফল্ট |
| Client credentials | মানুষ নেই, এক service অন্য service ডাকে | ঠিক আছে, শুধু confidential client-এ |
| Device flow (RFC 8628, ২০১৯) | টিভি, প্রিন্টার, CLI: ইনপুট কষ্টকর | ঠিক আছে, phishing নিয়ে সতর্ক থাকুন |
| Implicit grant | পুরোনো ব্রাউজার app | RFC 9700: SHOULD NOT, শর্ত ছাড়া ব্যবহার নয় |
| Password grant | পুরোনো first-party app | RFC 9700: MUST NOT |
কেন বাদ? Implicit grant-এ access token সরাসরি ব্রাউজারের redirect-এ আসত, ফলে ঠিকানা, history আর referrer-এ পড়ে থাকার ঝুঁকি ছিল, আর token অন্য কেউ বসিয়ে দিলেও client ধরতে পারত না। Password grant-এ তো client আবার password দেখে, যে সমস্যা মেটাতেই OAuth; সাথে MFA বা passkey-র মতো phishing-প্রতিরোধী login-ও এই পথে চলে না (পর্ব ১৬, MFA আর passkey).
RFC 9700 (Best Current Practice for OAuth 2.0 Security, BCP 240, জানুয়ারি ২০২৫) এই নিয়মগুলো লিখিত করেছে: public client-এ PKCE বাধ্যতামূলক, confidential client-এও সুপারিশকৃত, আর authorization server-কে PKCE সমর্থন করতেই হবে। OAuth 2.1 এসব ভালো অভ্যাস একত্র করার একটি খসড়া। অক্টোবর ২০২৬-এর শেষে datatracker-এ এর সর্বশেষ সংস্করণ -17, আর সেটি এখনো Internet-Draft, RFC নয়। এই অবস্থা বদলাতে পারে, তাই IETF datatracker দেখে নিন, আর “OAuth 2.1 standard” কথাটি দেখলে সাবধান হোন।
Client credentials — যেখানে মানুষ নেই
রাতের হিসাব মেলানোর একটি service পেমেন্ট API ডাকছে। এখানে resource owner হিসেবে কোনো মানুষ নেই; client নিজের পরিচয়েই token নেয়। সবচেয়ে বড় ঝুঁকি গোপন মানটি কোডে বা log-এ ফেলে রাখা। ভালো অভ্যাস: secret vault, নিয়মিত key rotation, সম্ভব হলে mTLS বা signed assertion, আর প্রতিটি service-এর আলাদা client ও ছোট scope. বিস্তারিত দেখুন Cloud & DevOps · Secrets আর KMS পর্বে।
Device flow — টিভিতে login
টিভি একটি user_code আর একটি ছোট ঠিকানা দেখায়, যেমন WDJB-MJHT. আপনি ফোনে সেই ঠিকানায় গিয়ে কোডটি দিয়ে login করেন, আর টিভি নির্দিষ্ট বিরতিতে token endpoint-কে জিজ্ঞেস করতে থাকে। উত্তর authorization_pending হলে অপেক্ষা, slow_down হলে বিরতি ৫ সেকেন্ড বাড়ানো।
একটি হিসাব: RFC 8628-এর উদাহরণে ২০টি অক্ষরের ভান্ডার থেকে ৮ অক্ষরের কোড। সম্ভাব্য কোড ২০৮ = ২৫৬০ কোটি (২.৫৬ × ১০১০), অর্থাৎ log₂(২০৮) = ৮ × ৪.৩২ ≈ ৩৪.৬ bit. একটি কোডে মাত্র ৫ বার চেষ্টার সীমা থাকলে এলোমেলো অনুমানে সফল হওয়ার সম্ভাবনা ৫ ÷ (২.৫৬ × ১০১০) ≈ ১.৯৫ × ১০−১০, প্রায় ২−৩২. ছোট কোডের সুরক্ষা তাই rate limit-এর ওপর নির্ভর করে। আসল ঝুঁকি অনুমান নয়: কেউ যদি ব্যবহারকারীকে কোডটি নিজে বসাতে রাজি করায়, তাহলে অন্যের যন্ত্রে login হয়ে যায়। তাই পাতায় স্পষ্ট লেখা থাকা উচিত “আপনি একটি যন্ত্রকে অনুমতি দিচ্ছেন, যন্ত্রটি কি আপনার সামনেই আছে?”
৭ · OpenID Connect — ID token, UserInfo, discovery আর SSO
OIDC-তে client scope-এ openid যোগ করে। বিনিময়ে পায় তিনটি জিনিস:
- ID token: একটি স্বাক্ষরিত JWT (পর্ব ১৭, Session আর token), যার ভেতরে login-এর তথ্য বা claim: কে দিল (
iss), কার জন্য (aud), কে login করলেন (sub), কখন (iat), কখন মেয়াদ শেষ (exp). Core spec-এ এই পাঁচটি সব ID token-এ বাধ্যতামূলক। - UserInfo endpoint: Access token দিয়ে নাম, email ইত্যাদি আনার API. উত্তরের
subID token-এরsub-এর সাথে হুবহু মিলতে হবে, নইলে সেই তথ্য ব্যবহার করা যাবে না। - Discovery: Issuer-এর একটি পরিচিত ঠিকানায় থাকা JSON, যা বলে দেয় কোন endpoint কোথায় আর স্বাক্ষরের key কোথায়।
উদাহরণ: discovery document-এর ছোট অংশ (কাল্পনিক issuer, নিজের সিস্টেম কনফিগার করার সময় এমন ফাইল পড়বেন)
{
"issuer": "https://id.example.com",
"authorization_endpoint": "https://id.example.com/authorize",
"token_endpoint": "https://id.example.com/token",
"userinfo_endpoint": "https://id.example.com/userinfo",
"jwks_uri": "https://id.example.com/jwks.json",
"response_types_supported": ["code"],
"code_challenge_methods_supported": ["S256"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"]
}এই ফাইল থাকে /.well-known/openid-configuration পথে। jwks_uri-তে থাকে issuer-এর public key-গুলো, যাকে বলে JWKS. Digital signature কীভাবে যাচাই হয় তা পর্ব ১১-তে দেখেছেন (Digital signature আর PKI). এবার একটি ID token ধাপে ধাপে যাচাই করি। মানগুলো খেলনা, ঘড়ি স্থির করা আছে now = 1800000000, প্রত্যাশিত client_id হলো toy-app.
1{"alg":"RS256","kid":"key-a"}2"iss": "https://id.example.com"3"aud": "toy-app"4"sub": "u-1001"5"iat": 17999997006"exp": 18000003007"nonce": "n-7f3a"8"email": "user@example.com"
| Signature | |
| iss | |
| aud | |
| সময় | |
| nonce |
- Token হাতে এলো। JWT-র ভেতরটা base64url, যে কেউ পড়তে বা বদলাতে পারে। তাই decode করা মানে যাচাই নয়। Header-এর
alg-কে নিজের allowlist-এর সাথে মেলান, token নিজে যা দাবি করে তা মেনে নেবেন না। - Signature. Discovery থেকে পাওয়া
jwks_uri-র key-গুলোর মধ্যেkid=key-aখুঁজে স্বাক্ষর মেলানো হলো। মিলেছে, মানে issuer-এর key দিয়েই সই করা, আর claim বদলানো হয়নি। iss. Discovery-র issuerhttps://id.example.com-এর সাথে হুবহু মিলতে হবে। অন্য issuer-এর সঠিক স্বাক্ষরও এখানে বাদ।aud. এখানেtoy-appথাকতে হবে। অন্য app-এর জন্য বানানো token এখানে চললে একটি ক্ষতিকর app-এ login করা token দিয়ে আপনার app-এ ঢোকা যেত।- সময়।
now= 1800000000.exp−now= ৩০০ সেকেন্ড, তাই মেয়াদ আছে।iat৩০০ সেকেন্ড আগের। ঘড়ির সামান্য পার্থক্যের জন্য অল্প ছাড় রাখা যায়, তবে ছাড় যেন কয়েক মিনিটের বেশি না হয়। nonce. শুরুতে পাঠানোn-7f3a-র সাথে মিলেছে, অর্থাৎ এই token এই login-এর জন্যই। মেলানোর পরে জমা মানটি মুছে দিন।- পরিচয়ের চাবি। Session বাঁধুন
iss+subজোড়ায়। OIDC-র মতে এই জোড়াই স্থায়ী শনাক্তকারী।emailবদলাতে পারে, আবার অন্য কেউ পরে সেই ঠিকানা পেতেও পারে, তাই email দিয়ে account মেলানো ঝুঁকিপূর্ণ।
// খেলনা checker: claim আগে থেকেই decode করা; signature ধরে নেওয়া ফল
const expect = { iss: "https://id.example.com", aud: "toy-app",
nonce: "n-7f3a", now: 1800000000, skew: 60 };
function check(t) {
if (!t.sigOk) return "FAIL signature";
if (t.iss !== expect.iss) return "FAIL iss";
const aud = [].concat(t.aud);
if (!aud.includes(expect.aud)) return "FAIL aud";
if (aud.length > 1 && t.azp !== expect.aud) return "FAIL azp";
if (t.exp <= expect.now - expect.skew) return "FAIL exp";
if (t.iat > expect.now + expect.skew) return "FAIL iat (future)";
if (t.nonce !== expect.nonce) return "FAIL nonce";
return "PASS user=" + t.iss + "#" + t.sub;
}
const base = { sigOk: true, iss: expect.iss, aud: "toy-app",
sub: "u-1001", iat: 1799999700, exp: 1800000300, nonce: "n-7f3a" };
const cases = [
["valid token", {}],
["bad signature", { sigOk: false }],
["other issuer", { iss: "https://id.other.example" }],
["made for other app", { aud: "other-app" }],
["expired", { exp: 1799999000 }],
["replayed nonce", { nonce: "n-old" }],
["two audiences", { aud: ["toy-app", "other-app"] }],
["two aud + azp", { aud: ["toy-app", "other-app"], azp: "toy-app" }],
];
for (const [name, change] of cases) {
console.log(name.padEnd(20), check({ ...base, ...change }));
}আউটপুটে valid token পেয়েছে PASS user=https://; লক্ষ করুন পরিচয় হিসেবে ছাপা হয়েছে iss#sub. বাকি লাইনগুলোতে প্রতিটি ত্রুটি ঠিক একটি নিয়মে ধরা পড়েছে: FAIL signature, FAIL iss, FAIL aud, FAIL exp, FAIL nonce. two audiences-এ FAIL azp, কিন্তু azp ঠিক থাকলে two aud + azp পাস করেছে। এখানে azp-এর নিয়মটি শেখার জন্য কড়া করা; spec বলে একাধিক aud-এ অচেনা audience থাকলে token বাতিল করতে হবে। এটি শেখার খেলনা। বাস্তবে নিজে এই কোড না লিখে পরীক্ষিত OIDC library ব্যবহার করুন, আর তার issuer, audience, nonce-এর সেটিংগুলো ঠিকমতো দিন। Signature এখানে শুধু একটি true বা false মান ধরে নেওয়া হয়েছে।
SSO, federation আর SAML
SSO (single sign-on) মানে একবার login করে অনেক app-এ ঢোকা। আপনার identity provider (IdP) পরিচয় যাচাই করে, আর app-গুলো তাকে বিশ্বাস করে। Federation মানে সেই বিশ্বাস দুই প্রতিষ্ঠানের সীমানা পার হওয়া, যেমন একটি ব্যাংকের কর্মী নিজের অফিসের login দিয়ে অংশীদার সেবায় ঢোকেন। লাভ অনেক: কম password, এক জায়গায় MFA, কর্মী চলে গেলে এক জায়গায় বন্ধ। দাম হলো blast radius: IdP ভাঙলে সব app-এর দরজা একসাথে খোলা। তাই IdP-র admin account সবচেয়ে কড়া পাহারায় রাখতে হয় (পর্ব ২০, Privileged access), আর phishing-প্রতিরোধী MFA সেখানেই আগে দরকার।
| বিষয় | SAML 2.0 (২০০৫) | OIDC (২০১৪) |
|---|---|---|
| বার্তার ধরন | স্বাক্ষরিত XML assertion | স্বাক্ষরিত JWT |
| যেখানে বেশি দেখা যায় | পুরোনো enterprise SSO | নতুন web, মোবাইল, API |
| মোবাইল ও API | স্বাভাবিকভাবে মানায় না | স্বাভাবিকভাবে মানায়, OAuth-এর ওপর তৈরি |
| বড় ঝুঁকি | XML signature যাচাইয়ের জটিলতা | Token আর claim যাচাই বাদ দেওয়া |
আরও গভীরে: OIDC Core-এর ৩.১.৩.৭ ধারা বলে, ID token যদি client সরাসরি TLS পথে token endpoint থেকে পায়, তাহলে signature-এর বদলে TLS server যাচাইয়ের ওপর ভরসা করা যেতে পারে (MAY). কিন্তু ব্রাউজারের পথে আসা token-এ এই ছাড় নেই, আর library প্রায় সব ক্ষেত্রেই signature দেখে, তাই ডিফল্ট হিসেবে যাচাই রাখাই ভালো। DPoP (RFC 9449, ২০২৩) আর mTLS-বাঁধা token (RFC 8705) bearer token-কে sender-constrained করে; RFC 9207-এর iss প্যারামিটার mix-up ঝুঁকি কমায়, যেখানে client একাধিক auth server-এর উত্তর গুলিয়ে ফেলে। এসব বাস্তবায়ন আপনার auth server-এর সমর্থনের ওপর নির্ভর করে।
৮ · ডেভেলপারের সাধারণ ভুল আর তার সমাধান
OAuth-এর বেশিরভাগ বাস্তব সমস্যা protocol-এর নয়, ব্যবহারের। নিচের তালিকাটি code review-এর জন্য চেকলিস্ট হিসেবেও কাজে লাগবে।
| ভুল | কেন বিপজ্জনক | সমাধান | যাচাইয়ের উপায় |
|---|---|---|---|
| খোলা বা wildcard redirect URI | কোড ভুল ঠিকানায় যেতে পারে | হুবহু মিলানো নিবন্ধিত তালিকা, পুরোনো ঠিকানা মুছে ফেলা | নিজের test পরিবেশে অনিবন্ধিত ঠিকানায় error আসে |
state নেই, বা স্থির মান | CSRF, অন্যের account যুক্ত হওয়া | প্রতি login-এ অনিশ্চিত, session-বাঁধা, এক-বার-ব্যবহারের মান | ভুল state-এ callback প্রত্যাখ্যান করে |
| ID token যাচাই ছাড়া বিশ্বাস | Decode করলেই সত্য ধরে নেওয়া | Signature, iss, aud, exp, nonce যাচাই, পরীক্ষিত library | বদলানো token-এর test case ব্যর্থ হয় |
| Access token দিয়ে পরিচয় প্রমাণ | অন্য app-এর জন্য পাওয়া token-ও “প্রমাণ” হয়ে যায়; confused deputy | Login-এ ID token, API-তে aud যাচাই | অন্য audience-এর token API প্রত্যাখ্যান করে |
| অতিরিক্ত বড় scope | Token চুরি হলে ক্ষতি বড়, মানুষ consent-এ ভয় পান বা না পড়েই সম্মতি দেন | ছোট scope, দরকারের সময় বাড়তি অনুমতি চাওয়া | কোড যা ব্যবহার করে তার সাথে scope মিলিয়ে দেখা |
| Token log বা URL-এ | Log-এর পাঠক মানেই token-এর ধারক | Log-এ মুছে ফেলা, Authorization header ব্যবহার | Log নমুনায় Bearer খোঁজা |
client_secret মোবাইল বা ব্রাউজার কোডে | কোড খুলে দেখা যায়, গোপন থাকে না | Public client + PKCE, অথবা server-এ ছোট backend | Build-এ secret scanning |
| Email দিয়ে account মেলানো | একই email-এর ভিন্ন issuer মিশে যায় | iss + sub দিয়ে মেলানো, যুক্ত করার আগে যাচাই | Test: দুই issuer-এ একই email |
Token রাখার জায়গা: ব্রাউজারের localStorage-এ রাখা token কোনো XSS ত্রুটিতে চুরি হতে পারে (পর্ব ৪০). অনেক দল তাই token ব্রাউজারে না রেখে নিজের backend-এ রাখে, আর ব্রাউজার পায় শুধু নিরাপদ session cookie. কোন পথ আপনার জন্য ঠিক, তা নির্ভর করে threat model-এর ওপর (পর্ব ৪, Threat modeling).
কীভাবে ধরবেন — detection
Audit log-এ এই ঘটনাগুলো দেখুন, আর SIEM-এ পাঠান: নতুন consent grant, ঝুঁকিপূর্ণ scope, নতুন client নিবন্ধন, redirect URI বদল, refresh token reuse, অচেনা network থেকে token ব্যবহার, আর একটি client-এর হঠাৎ অনেক বেশি ডেটা নামানো। নিচের Sigma-ধাঁচের নিয়মটি ধারণা দেখায়। Log-এর field-এর নাম কাল্পনিক, আপনার IdP-র log অনুযায়ী বদলাতে হবে।
# উদাহরণ: Sigma-ধাঁচের detection, নিজের IdP log-এর জন্য
title: High-risk OAuth scope granted by a non-admin
status: experimental
logsource:
product: example_idp
category: oauth_consent
detection:
selection:
event_type: consent.granted
scope|contains:
- mail.read
- files.readwrite.all
filter_admin:
actor_role: admin
condition: selection and not filter_admin
falsepositives:
- Approved app rollout during a change window
level: high৯ · Consent phishing — password ছাড়াই যখন ক্ষতি
সাধারণ phishing password চায়। Consent phishing অন্য পথে যায়: মানুষকে একটি ক্ষতিকর বা ভুয়া নামের third-party app-কে আসল login পাতাতেই অনুমতি দিতে রাজি করানো হয়। পাতাটি আসল, domain আসল, MFA-ও ঠিকঠাক হয়। ক্ষতিটি আসে স্বয়ং দেওয়া অনুমতি থেকে। ফলে তিনটি সাধারণ প্রতিরক্ষা এখানে সরাসরি কাজ করে না:
- MFA: মানুষ নিজেই login করেছেন, তাই MFA পাস।
- URL দেখা: Consent পাতার ঠিকানা আসলই।
- Password বদলানো: App-এর হাতে আলাদা token আছে। Password বদলালে সেই grant বাতিল হবে কি না, তা provider-ভেদে আলাদা, তাই grant আলাদাভাবে তুলতে হয়।
দারোয়ানের কাছে আসল মালিক নিজে গিয়ে সই করে এক অচেনা লোককে পাস দিলেন, কারণ লোকটি বলেছিল সে “বিদ্যুৎ অফিস থেকে এসেছে”। দারোয়ানের যাচাই ঠিকই ছিল, ভুলটি হয়েছে মালিকের বিশ্বাসে। প্রতিকার তাই নিয়ম: অচেনা লোককে পাস দিতে হলে আগে অফিসের অনুমোদন লাগবে।
প্রকাশিত উদাহরণ: মে ২০১৭-তে “Google Docs” নামের একটি ভুয়া তৃতীয় পক্ষের app-এর দাওয়াত email-এ ছড়ায়। সংবাদ ও গবেষকদের প্রতিবেদন অনুযায়ী সেটি password চায়নি, mail আর contact-এর অনুমতি চেয়েছিল। Google app ও account বন্ধ করে, আর পরে app নিবন্ধনে নিয়ম কড়া করে। শিক্ষা: নাম মিলিয়ে নকল ঠেকানো আর প্রকাশক যাচাই দরকার।
নিচে একজন নিরাপত্তা-কর্মীর চোখে একটি app-এর consent পর্যালোচনা। প্রতিষ্ঠান আর app কাল্পনিক। লক্ষ্য হলো পর্যালোচনার অভ্যাস।
App : PDF-Helper প্রকাশক : যাচাই করা নয় Redirect : https://pdf-helper.example/cb বলা কাজ : PDF জোড়া লাগানো চাওয়া scope: profile.read mail.read files.readwrite.all offline_access
| প্রশ্ন | |
| দেখা গেল | |
| ফল |
- অনুরোধ এলো। Consent নীতির কারণে কর্মীর অনুরোধটি সরাসরি পাস হয়নি, admin-এর সারিতে এসেছে। এটিই admin consent-এর মূল ফল: একজনের ভুল ক্লিক সবার ঝুঁকি হয় না।
- প্রকাশক। নামে বড় ব্র্যান্ডের ছোঁয়া থাকতে পারে, তাই নাম নয়, যাচাই করা প্রকাশক দেখুন। এখানে যাচাই নেই, ওয়েবসাইটও নেই।
- Scope বনাম কাজ। PDF জোড়া লাগাতে mail পড়া বা সব ফাইলে লেখার দরকার নেই। ৪টি scope-এর ৩টি (
mail.read,files.readwrite.all,offline_access) কাজের সাথে মেলে না। - Offline access. এই scope মানে refresh token: ব্যবহারকারী চলে গেলেও app কাজ চালাতে পারে। অকারণে থাকলে সতর্ক হওয়ার কারণ।
- সিদ্ধান্ত। নীতি অনুযায়ী অনুরোধ প্রত্যাখ্যান, আর app-টি blocklist-এ। কর্মীকে কারণ জানিয়ে বিকল্প অনুমোদিত টুল দেখানো হলো, যাতে তিনি অন্য পথ না খোঁজেন।
- পিছনে তাকানো। Grant তালিকায় ২ জন পাওয়া গেল। তাঁদের grant আর refresh token বাতিল, log-এ app-টির কাজ পরীক্ষা, আর ঘটনা incident response ধারায় পাঠানো হলো।
প্রতিরক্ষার তালিকা
- Admin consent নীতি: ব্যবহারকারী শুধু যাচাইকৃত প্রকাশকের কম-ঝুঁকির scope-এ সম্মতি দিতে পারবেন, বাকিটা admin-এর পর্যালোচনায়।
- App allowlist আর নিয়মিত grant পর্যালোচনা: কোন app-এর কাছে কী অনুমতি আছে, তার একটি তালিকা রাখুন। অব্যবহৃত grant তুলে দিন।
- সচেতনতা: “অনুমতি” পাতাও সিদ্ধান্তের জায়গা, শুধু একটি “Next” বোতাম নয় (পর্ব ৫৩, Phishing প্রতিরক্ষা).
- সাড়া দেওয়ার ধাপ: App বন্ধ করা, grant ও token বাতিল, log-এ যা পড়া হয়েছে তা দেখা, ক্ষতিগ্রস্তদের জানানো, ডেটা ফাঁস হলে আইনি বাধ্যবাধকতা দেখা।
Third-party-র হাতে থাকা token নিজেও ঝুঁকি। এপ্রিল ২০২২-এ GitHub জানায়, Heroku আর Travis CI-কে দেওয়া OAuth user token চুরি করে কয়েক ডজন প্রতিষ্ঠানের private repository-র ডেটা নামানো হয়েছে। প্রকাশিত প্রতিবেদন অনুযায়ী GitHub নিজে সরাসরি ভাঙেনি, আঘাত এসেছিল integration-এর পথে। শিক্ষা: ছোট scope, ছোট মেয়াদ, token এনক্রিপ্ট করে রাখা, ব্যবহারের অস্বাভাবিকতায় নজর, আর দ্রুত বাতিলের প্রস্তুতি। এ নিয়ে পর্ব ৭১-এ (Vendor আর third-party ঝুঁকি) আবার আসবে।
আইন আর নৈতিকতা: এই পর্বের সিমুলেটর শুধু খেলনা ডেটায় চলে। Consent phishing-এর সচেতনতা মহড়াও চালাতে হয় প্রতিষ্ঠানের লিখিত অনুমতিতে, নিজের tenant-এ। অন্যের account বা সিস্টেমে অনুমতি ছাড়া পরীক্ষা অপরাধ হতে পারে; সীমারেখা দেখুন পর্ব ৬-এ (আইন আর নৈতিকতা).
পরীক্ষা ও ইন্টারভিউতে: এ ধরনের প্রশ্ন আসতে পারে।
OAuth আর OIDC-র পার্থক্য? OAuth অধিকার দেয় (authorization), OIDC তার ওপরে পরিচয় দেয় (authentication) ID token দিয়ে।
PKCE কী ঠেকায়? Authorization code চুরি আর বসানো; verifier ছাড়া কোড ভাঙানো যায় না।
state আর nonce-এর পার্থক্য? state callback-এ মেলে, CSRF ঠেকায়; nonce ID token-এর ভেতরে মেলে, replay ঠেকায়।
Implicit grant কেন বাদ? Token ব্রাউজারের redirect পথে আসে; code + PKCE বেশি নিরাপদ।
Consent phishing MFA দিয়ে ঠেকে না কেন? ব্যবহারকারী নিজে আসল পাতায় login করে অনুমতি দেন; দরকার admin consent নীতি।
১০ · AI-এর যুগে OAuth
AI “Login with …” কোড মিনিটে লিখে দেয়, আর সেটি চলেও। সমস্যা হলো, এখানে “চলা” আর “নিরাপদ হওয়া” এক জিনিস নয়। ভুল যাচাই ছাড়া সব happy-path পরীক্ষা পাস করে। AI-এর পরিচিত ভুলগুলো হলো:
| AI যা করতে পারে | কেন ভুল | আপনি যা করবেন |
|---|---|---|
| পুরোনো টিউটোরিয়াল দেখে implicit বা password grant দেয় | RFC 9700-এ বাদ | Code + PKCE চান, RFC মিলিয়ে দেখুন |
| JWT decode করে signature না দেখে পরিচয় নেয় | যে কেউ এমন token বানাতে পারে | Library-র verify ফাংশন, test-এ বদলানো token |
Error সারাতে যাচাই বন্ধ করে, যেমন verify=False | TLS বা token যাচাই নিষ্ক্রিয় | মূল কারণ খুঁজুন, যাচাই বন্ধ করবেন না |
client_secret frontend কোডে বসায় | সবাই দেখতে পায় | Public client বা backend |
| RFC নম্বর বা CVE বানিয়ে বলে | নম্বরটি ভুল বা অস্তিত্বহীন হতে পারে | rfc-editor.org-এ নিজে খুলে পড়ুন |
| সব scope একসাথে চায়, “যাতে পরে সমস্যা না হয়” | Least privilege ভাঙে | ব্যবহৃত API ধরে scope মিলান |
AI-কে নিজের OAuth কোড দেখাতে হলে আগে client secret, token, আর আসল domain সরিয়ে ফেলুন। তারপর এভাবে পর্যালোচনা চান:
AI-এর লেখা OAuth কোড — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১OAuth 1.0-তে প্রতিটি অনুরোধ স্বাক্ষর করতে হতো। OAuth 2.0 সেটি সরিয়ে bearer token আনল কেন, আর এর দাম কী?
OAuth 1.0-তে client প্রতিটি অনুরোধকে একটি গোপন মান দিয়ে স্বাক্ষর করত। নিরাপত্তার দিক থেকে এটি ভালো ছিল, কারণ token একা চুরি হলেও স্বাক্ষরের key ছাড়া কাজ করত না। কিন্তু ডেভেলপারদের জন্য এটি ছিল যন্ত্রণা। অনুরোধের প্রতিটি প্যারামিটার ঠিক ক্রমে সাজিয়ে, ঠিক encoding-এ স্বাক্ষর করতে হতো। একটি অক্ষর এদিক-ওদিক হলে স্বাক্ষর মিলত না, আর ভুল কোথায় তা বোঝাও কঠিন ছিল।
OAuth 2.0 (RFC 6749 আর 6750, ২০১২) সিদ্ধান্ত নেয়: পথের সুরক্ষা TLS-এর হাতে থাকুক, token হোক সহজ bearer. প্রায় যেকোনো ভাষায় একটি header বসালেই কাজ চলে। ফলে গ্রহণ দ্রুত ছড়ায়।
দাম হলো: token এখন নগদ টাকার মতো। যার হাতে যায়, সে-ই খরচ করতে পারে। আর এই আলোচনা তখন শান্ত ছিল না। OAuth 2.0 খসড়ার প্রধান সম্পাদক Eran Hammer ২০১২ সালে কাজ ছেড়ে দেন, আর “OAuth 2.0 and the Road to Hell” লেখায় জটিলতা, ঐচ্ছিকতা আর দুর্বল নিরাপত্তার অভিযোগ তোলেন।
পরের এক দশকের ইতিহাস অনেকটাই সেই ফাঁক পূরণের: PKCE, RFC 9700-এর নিয়ম, mTLS-বাঁধা token (RFC 8705), DPoP (RFC 9449). শেষের দুটি token-কে আবার একটি key-র সাথে বাঁধে, তবে স্বাক্ষরের জায়গা আগের চেয়ে অনেক সহজ করে। অর্থাৎ পেন্ডুলাম আবার ফিরছে, তবে সহজ রূপে।
বাস্তবে এর প্রভাব কী? Bearer token-কে log, URL, crash রিপোর্ট আর ব্রাউজারের storage থেকে দূরে রাখতে হয়, মেয়াদ ছোট রাখতে হয়, আর TLS certificate যাচাই কখনো বন্ধ করা চলে না। OAuth 1.0-তে TLS দুর্বল হলেও token একা তেমন কাজে লাগত না; 2.0-তে TLS-ই প্রধান পাহারা।
মূল উপলব্ধি: নিরাপত্তার নকশা বাস্তবে যতটুকু ব্যবহার হয়, ততটুকুই নিরাপদ। সহজ নকশা ছড়ায়, তার ফাঁক পরে আলাদা স্তরে পূরণ করতে হয়।
প্র ০২“Access token পেয়েছি, API থেকে নাম এনেছি, তাহলে login হয়ে গেল” — এই যুক্তিতে ভুলটা ঠিক কোথায়?
যুক্তিটি স্বাভাবিক, আর OIDC আসার আগে বহু সাইট ঠিক এভাবেই “Login with …” বানাত। ভুলটি হলো access token কার জন্য বানানো, সেই প্রশ্ন বাদ পড়ে।
ধরুন একটি কাল্পনিক ক্ষতিকর ছবির app মানুষকে “Login with …” বোতাম দিয়ে ঢোকায়। সে বৈধভাবেই ব্যবহারকারীর access token পায়। এখন আপনার app যদি শুধু বলে “যেকোনো বৈধ access token আনুন, আমি profile API ডেকে দেখব কে”, তাহলে ওই ছবির app সেই token আপনার app-এ জমা দিলেই আপনার app ভাববে ব্যবহারকারী নিজেই এসেছেন। Token সত্যিই বৈধ, কিন্তু আপনার জন্য নয়। আপনার app একজন বিভ্রান্ত প্রতিনিধি, যাকে বলে confused deputy.
আরেকটি ফাঁক: access token বলে না মানুষটি এখন উপস্থিত কি না। Refresh দিয়ে পাওয়া token সপ্তাহ পরেও ঠিক একইভাবে কাজ করে।
OIDC ঠিক এই দুই সমস্যার উত্তর। ID token-এর aud-এ client-এর নাম থাকে, ফলে অন্য app-এর জন্য বানানো token আপনার app মানে না। iat, auth_time আর nonce বলে login কখন, কোন অনুরোধে হলো। Token-টি issuer-এর স্বাক্ষরে আপনার উদ্দেশেই লেখা।
API-র দিকেও একই শিক্ষা: aud না মিললে access token প্রত্যাখ্যান করুন। RFC 9700-ও audience সীমিত রাখার সুপারিশ করে।
এই ভুল কীভাবে ধরবেন? Code review-এ দেখুন login পথে ID token যাচাই আছে কি না, নাকি শুধু profile API ডাকা হচ্ছে। নিজের test পরিবেশে অন্য client-এর জন্য বানানো token দিয়ে login চেষ্টা করুন; প্রত্যাখ্যান না হলে ভুলটি আছে। মোবাইল app থেকে backend-এ access token পাঠিয়ে “ইনি আমাদের ব্যবহারকারী” ধরে নেওয়ার নকশায় এই ভুল প্রায়ই দেখা যায়। সমাধান হলো backend-এ ID token পাঠিয়ে সেখানে পুরো যাচাই করা, অথবা নিজের API-র জন্য বানানো access token-এর audience কড়াভাবে দেখা।
মূল উপলব্ধি: একটি credential বৈধ হওয়া যথেষ্ট নয়; সেটি কার জন্য, কী কাজে, কোন প্রসঙ্গে দেওয়া, তিনটিই মিলতে হবে।
প্র ০৩PKCE তৈরি হয়েছিল মোবাইল app-এর জন্য। তাহলে server-এ চলা confidential client-এরও এটি দরকার কেন?
PKCE-র জন্ম মোবাইলের একটি বাস্তব সমস্যা থেকে। মোবাইল app redirect ধরত নিজের custom URL scheme দিয়ে, আর একই scheme অন্য app-ও নিবন্ধন করতে পারত। ফলে কোড ভুল app-এর হাতে পৌঁছানো সম্ভব ছিল, আর client secret তো মোবাইলে গোপন নয়। RFC 7636 (২০১৫) এই ফাঁক বন্ধ করে। পরে RFC 8252 (২০১৭) native app-এর জন্য সিস্টেম ব্রাউজার ব্যবহারের সেরা অভ্যাস ঠিক করে।
তাহলে server-side app-এর তো secret আছে, কোড চুরি হলেও ভাঙাতে পারবে না? চুরির ক্ষেত্রে এটি অনেকটা ঠিক। কিন্তু আরেকটি ঝুঁকি আছে: code injection. এখানে কেউ নিজের পাওয়া একটি কোড অন্য ব্যবহারকারীর callback-এ বসিয়ে দেয়। তখন আসল client নিজের secret দিয়েই কোডটি ভাঙায়, আর ভুল account-এর token পেয়ে যায়। Client secret এখানে সুরক্ষা দেয় না, কারণ ভাঙাচ্ছে আসল client-ই।
PKCE এটি ঠেকায়, কারণ কোডটি একটি নির্দিষ্ট login-এর challenge-এর সাথে বাঁধা। ভিন্ন login-এর verifier দিয়ে ওই কোড ভাঙে না। এই কারণেই RFC 9700 confidential client-এও PKCE সুপারিশ করে।
একটি সতর্কতা: PKCE client authentication-এর বিকল্প নয়। PKCE প্রমাণ করে “যে শুরু করেছে, সে-ই শেষ করছে”; কে শুরু করেছে তা প্রমাণ করে না। তাই public client কাউকে নিজের পরিচয় প্রমাণ করতে পারে না, আর সেজন্য তাদের অধিকার ও refresh token-এর নিয়ম কড়া রাখতে হয়।
কাজ করছে কি না জানার উপায়: নিজের test পরিবেশে এক login-এর কোড অন্য login-এর verifier দিয়ে ভাঙাতে চাইলে server-কে প্রত্যাখ্যান করতে হবে। আর public client-এর PKCE-ছাড়া অনুরোধ server মেনে নিচ্ছে কি না, সেটিও পরীক্ষা করুন। শুধু client-এর কোডে PKCE থাকলেই চলবে না, server-কেও সেটি বাধ্যতামূলক করতে হবে।
মূল উপলব্ধি: একটি নিয়ন্ত্রণ যে সমস্যার জন্য জন্মায়, প্রায়ই তার চেয়ে বেশি সমস্যা ঠেকায়। তবু জানতে হবে ঠিক কোনটি ঠেকায়, আর কোনটি ঠেকায় না।
প্র ০৪Implicit grant তৈরি হয়েছিল ভালো কারণেই। তাহলে সেটি বাদ পড়ল কেন, আর আজকের ব্রাউজার app কী করবে?
২০১২ সালে ব্রাউজারের JavaScript অন্য domain-এর token endpoint-এ অনুরোধ পাঠাতে সহজে পারত না, কারণ CORS তখনো সর্বত্র ছিল না। তাই নকশাকারেরা একটি বুদ্ধিদীপ্ত পথ নিলেন: token সরাসরি redirect URL-এর fragment অংশে দিয়ে দেওয়া। তখনকার সীমাবদ্ধতায় এটি যুক্তিসঙ্গত ছিল।
সময় বদলাল। CORS সব আধুনিক ব্রাউজারে এলো, ফলে ব্রাউজার app সরাসরি token endpoint ডাকতে পারে। PKCE এলো, ফলে client secret ছাড়াও কোড সুরক্ষিত করা গেল। আর implicit-এর দুর্বলতাগুলো স্পষ্ট হলো: token URL-এ আসে, ফলে history আর নানা পথে ফাঁসের সুযোগ থাকে; client বুঝতে পারে না token তার জন্যই দেওয়া কি না; refresh token-ও এ পথে নেই।
RFC 9700 তাই বলে, client-এর implicit grant ব্যবহার করা উচিত নয় (SHOULD NOT), যদি না token injection ঠেকানো আর ফাঁসের পথ বন্ধের ব্যবস্থা থাকে। OAuth 2.1 খসড়া এটিকে পুরো বাদ দেয়।
আজকের ব্রাউজার app-এর জন্য মোটামুটি দুই পথ। এক, app নিজেই code + PKCE চালায় আর token memory-তে রাখে; সহজ, কিন্তু XSS হলে token ঝুঁকিতে। দুই, backend-for-frontend (BFF): একটি ছোট server token রাখে, ব্রাউজার পায় শুধু HttpOnly cookie. এতে token ব্রাউজারে থাকে না, তবে CSRF-এর দায়িত্ব ফিরে আসে আর একটি server চালাতে হয়। IETF-এর ব্রাউজার app-বিষয়ক একটি খসড়া এই পথগুলো তুলনা করে; তার বর্তমান অবস্থা দেখে নিন।
যে পথই নিন, কিছু নিয়ম একই থাকে: CSP দিয়ে XSS-এর ঝুঁকি কমানো, access token-এর মেয়াদ ছোট রাখা, refresh token-এ rotation রাখা, আর dependency-র ওপর নজর রাখা, কারণ ব্রাউজারে চলা প্রতিটি script আপনার token-এর কাছে পৌঁছাতে পারে। BFF ঝুঁকি কমায়, শূন্য করে না: XSS তখনো ব্যবহারকারীর session থেকে অনুরোধ পাঠাতে পারে।
মূল উপলব্ধি: “ভালো নকশা” সব সময়ের জন্য নয়। নকশা তার সময়ের সীমাবদ্ধতার ফল; সীমাবদ্ধতা সরে গেলে সেরা অভ্যাসও বদলায়। পুরোনো টিউটোরিয়াল তাই ঝুঁকি।
প্র ০৫একটি প্রতিষ্ঠানে কর্মীরা ভুল করে ক্ষতিকর app-কে অনুমতি দিয়েছেন। MFA চালু, password ফাঁস হয়নি। প্রথম ২৪ ঘণ্টায় কী করবেন, আর দীর্ঘমেয়াদে?
আগে বুঝে নিন কেন স্বাভাবিক প্রতিক্রিয়া যথেষ্ট নয়। “সবার password বদলান” বললে মনে হবে কিছু করা হয়েছে, কিন্তু app-এর হাতে থাকা refresh token অনেক ব্যবস্থায় টিকে থাকতে পারে। মে ২০১৭-র ভুয়া “Google Docs” ঘটনার আলোচনাতেও এই কথাটি বারবার আসে: আক্রমণের কেন্দ্রে ছিল OAuth অনুমতি, password নয়।
প্রথম ২৪ ঘণ্টা:
- App-টি পুরো tenant-এ বন্ধ করুন, যাতে নতুন consent না হয়।
- Grant তালিকা বের করে সবার grant আর refresh token বাতিল করুন। Access token-এর মেয়াদ শেষ হওয়া পর্যন্ত ছোট একটি ঝুঁকি থাকতে পারে, সেটি মাথায় রাখুন।
- প্রমাণ সংরক্ষণ করুন: audit log, app-এর client id, IP, সময়। বাতিল করার আগে যা নষ্ট হতে পারে, তা আগে রাখুন।
- Log-এ দেখুন app কী পড়েছে, পাঠিয়েছে, বা mail rule বদলেছে কি না।
- ব্যক্তিগত ডেটা গেলে আইনি ও চুক্তিগত বিজ্ঞপ্তির প্রশ্ন আইনজীবীর সাথে দেখুন।
দীর্ঘমেয়াদে: Admin consent নীতি চালু করুন, শুধু যাচাইকৃত প্রকাশককে অনুমতি দিন, grant পর্যালোচনা নিয়মিত করুন, আর ঝুঁকিপূর্ণ scope-এর detection নিয়ম বসান। মানুষকে দোষ না দিয়ে শেখান, কারণ দোষারোপে পরের ভুল আর রিপোর্ট হয় না (পর্ব ৫৪, নিরাপত্তা সংস্কৃতি). আর ভাবুন, ব্যবহারকারীদের সব app-এ অনুমতি দিতে দেওয়ার সুবিধা আর ঝুঁকির ভারসাম্য কোথায়; অতিরিক্ত কড়া নীতি মানুষকে অননুমোদিত পথে ঠেলে দিতে পারে।
ব্যবস্থা কাজ করল কি না কীভাবে বুঝবেন? App-এর client id ধরে নতুন token ইস্যু আর API ডাক শূন্যে নামছে কি না দেখুন, আর কয়েক দিন নজরে রাখুন। ঘটনার পরে একটি tabletop মহড়া (পর্ব ৬৬) করলে পরের বারের ধাপগুলো পরিষ্কার হয়।
মূল উপলব্ধি: পরিচয়ের যুগে “অনুমতি” নিজেই একটি সম্পদ। Password-এর মতো grant-এরও তালিকা, মেয়াদ আর বাতিলের পরিকল্পনা থাকা চাই।
অনুশীলন
হিসাব, পূর্বাভাস আর নকশার সমালোচনা। পরীক্ষা শুধু খেলনা ডেটায় বা নিজের সিস্টেমে করুন।
-
হিসাব করুন: (ক) ৯৬ byte অনিশ্চিত মান থেকে base64url-এ বানানো verifier কত অক্ষরের হবে? RFC 7636-এর সীমায় পড়বে? (খ) ৯ অঙ্কের সংখ্যা দিয়ে device
user_codeবানালে সম্ভাব্য কোড কত, কত bit, আর ৫ বার চেষ্টার সীমায় এলোমেলো অনুমানে সাফল্যের সম্ভাবনা কত?(ক) ৯৬ × ৮ = ৭৬৮ bit. প্রতি অক্ষরে ৬ bit, তাই ৭৬৮ ÷ ৬ = ১২৮ অক্ষর। এটি ঠিক RFC-র সর্বোচ্চ সীমা, তাই চলবে।
(খ) ১০৯ = ১০০ কোটি কোড। log₂(১০৯) = ৯ × ৩.৩২ ≈ ২৯.৯ bit. সম্ভাবনা ৫ ÷ ১০৯ = ৫ × ১০−৯. অর্থাৎ ২০ অক্ষরের ৮-অক্ষরের কোডের চেয়ে প্রায় ২৬ গুণ সহজ (২.৫৬ × ১০১০ ÷ ১০৯ ≈ ২৫.৬), তাই সীমা আরও কড়া রাখা ভালো।
-
আউটপুট অনুমান করুন: একটি client-এ জমা আছে
{ s1: "a", s2: "b" }. যাচাই ফাংশন session না পেলেNO_PENDING, মান না মিললেMISMATCH, মিললে জমা মান মুছেOKফেরত দেয়। ক্রমে আসে:(s1,"a"),(s2,"a"),(s1,"a"),(s2,"b"). আউটপুট কী, আর শেষে জমা কী থাকে?১)
OK, s1 মুছে গেল। ২)MISMATCH, কারণ s2-র মান"b"; ব্যর্থ হলে মান মোছে না। ৩)NO_PENDING, কারণ s1 আগেই মুছে গেছে। ৪)OK, s2 মুছে গেল। শেষে জমা খালি,{ }. -
নকশার সমালোচনা: একটি কাল্পনিক অনলাইন বাজারের ব্রাউজার app-এর নকশা: implicit flow; token
localStorage-এ; redirect নিয়মhttps://*.example.com; login-এর পরে email দিয়ে account খোঁজা; scopeall;stateনেই, কারণ “HTTPS তো আছেই”। অন্তত পাঁচটি ত্রুটি আর সমাধান লিখুন।- Implicit → code + PKCE S256.
localStorage→ XSS-এ চুরির ঝুঁকি; memory-তে ছোট মেয়াদে, অথবা BFF ও HttpOnly cookie.- Wildcard redirect → সব subdomain বিশ্বাসে; হুবহু মিলানো নিবন্ধিত তালিকা।
- Email দিয়ে মেলানো →
iss+sub. scope=all→ ছোট scope, দরকারে বাড়তি অনুমতি।stateনেই → TLS পথ রক্ষা করে, CSRF ঠেকায় না; অনিশ্চিতstateবা যাচাইকৃত PKCE. OIDC হলেnonce-ও।
-
নিজের হাতে চালান: পাতার
pkce_s256.pyব্লকেoctets-এর প্রথম সংখ্যাটি 116 থেকে 117 করে চালান।RFC matchকী হবে, আর কেন? তারপর নিজের কোনো একটি account-এর সেটিংসে “যুক্ত app” তালিকা খুলে দেখুন কোন app-এর কাছে কী অনুমতি আছে।RFC match: Falseআসবে। একটি byte বদলালে verifier বদলায়, আর SHA-256-এর avalanche effect-এ পুরো ছাপ অন্যরকম হয়ে যায়। এটিই কোড ভাঙানোর সময় ভুল verifier ধরার কারণ। নিজের account-এর তালিকায় সাধারণত এমন app পাবেন যা আর ব্যবহার করেন না; সেগুলোর অনুমতি তুলে নিন।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমAuthorization code + PKCE-র ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Client verifier, challenge আর
stateবানায় - ব্রাউজার challenge নিয়ে auth server-এ যায়
- ব্যবহারকারী login আর consent করেন
- কোড আর
stateনিবন্ধিত redirect URI-তে ফেরে - Client কোড আর verifier দিয়ে token নেয়
- Client access token দিয়ে API ডাকে
OAuth 2.0-র মূল উদ্দেশ্য কোনটি?
- Client-এর কাছে ব্যবহারকারীর password নিরাপদে জমা রাখা
- Password না দিয়ে একটি app-কে সীমিত অধিকার ধার দেওয়া
- ব্যবহারকারী কে, তা client-কে প্রমাণ করে দেখানো
- API-র ডেটা এনক্রিপ্ট করা
PKCE-র S256 method-এ শুরুর authorization অনুরোধে client কী পাঠায়?
- code_verifier-এর আসল মান
- client_secret
- code_verifier-এর SHA-256 ছাপের base64url রূপ
- Access token
Authorization server-এ নিবন্ধিত redirect URI https://app.example.com/callback. অনুরোধে এলো https://app.example.com/callback/ (শেষে একটি বাড়তি /). RFC 9700 মেনে চলা server কী করবে?
- প্রত্যাখ্যান করবে, কারণ string হুবহু মেলেনি
- মেনে নেবে, কারণ domain একই
- শেষের / মুছে নিজে ঠিক করে নেবে
- ব্যবহারকারীকে জিজ্ঞেস করবে
OIDC-তে একটি client ID token পেল, যার signature ঠিক, কিন্তু aud-এ অন্য একটি app-এর নাম। কী করা উচিত?
- মেনে নেওয়া, কারণ signature ঠিক আছে
- sub মিললে মেনে নেওয়া
- Refresh token দিয়ে নতুন ID token আনা
- প্রত্যাখ্যান করা, কারণ token এই client-এর জন্য নয়
মানুষহীন একটি রাতের service আরেকটি অভ্যন্তরীণ API ডাকবে। কোন OAuth flow সবচেয়ে মানানসই?
- Password grant
- Client credentials
- Device flow
- Implicit grant
Consent phishing-এ কর্মী আসল login পাতায় MFA দিয়ে একটি ক্ষতিকর app-কে অনুমতি দিলেন। সবচেয়ে কার্যকর প্রতিরোধমূলক ব্যবস্থা কোনটি?
- সবাইকে বারবার password বদলাতে বলা
- MFA-র ধাপ বাড়ানো
- Admin consent নীতি, যাচাইকৃত প্রকাশক আর ঝুঁকিপূর্ণ scope-এ পর্যালোচনা
- TLS-এর version বাড়ানো
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- OAuth delegated access দেয়: password নয়, সীমিত আর মেয়াদি token.
- OAuth = authorization, OIDC = authentication. Access token পরিচয়ের প্রমাণ নয়; login-এর জন্য ID token.
- ডিফল্ট পথ authorization code + PKCE S256; implicit আর password grant বাদ।
- Redirect URI হুবহু,
stateবা PKCE-তে CSRF ঠেকানো, আর ID token-এ signature, iss, aud, exp, nonce যাচাই। - Consent phishing MFA এড়িয়ে যায়; প্রতিরক্ষা admin consent, grant পর্যালোচনা আর detection.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৯ · Authorization model — RBAC, ABAC আর least privilege বাস্তবেপরবর্তী পর্বকে কী করতে পারবে, তা নকশা করা।
- সাইবার নিরাপত্তা · কোর্স হোম৮০টি পর্বপুরো কোর্সের রোডম্যাপ।
- পর্ব ৬ · আইন আর নৈতিকতাসীমারেখাঅনুমতি আর দায়িত্বশীল প্রকাশ।
- পর্ব ১১ · Digital signature আর PKIআগের ভিত্তিID token-এর স্বাক্ষর যাচাইয়ের মূল ধারণা।
- System Design · নকশায় নিরাপত্তাপাশের ট্র্যাকবড় সিস্টেমে OAuth, OIDC আর JWT কোথায় বসে।
- Cloud & DevOps · Secrets আর KMSগভীরে যানClient secret আর token কোথায় রাখবেন।