একটি signed request কী প্রমাণ করে?
বেশিরভাগ স্বয়ংক্রিয় traffic একটি user agent string দিয়ে নিজেকে চিহ্নিত করে। সেই string একটি দাবি, এবং সেটি অনুলিপি করা প্রায় কিছুই খরচ হয় না। Signed request এই চিত্র বদলে দেয়। যখন একটি স্বয়ংক্রিয় client private key দিয়ে তার HTTP request sign করে এবং operator-এর সঙ্গে যুক্ত স্থানে matching public key প্রকাশ করে, তখন আপনার server যাচাই করতে পারে যে request-টি সত্যিই সেই key ধারণকারী কেউ তৈরি করেছে।
এর পেছনের মানদণ্ড হলো HTTP Message Signatures, যা RFC 9421 হিসেবে প্রকাশিত। IETF Web Bot Auth working group স্বয়ংক্রিয় client-এর জন্য এর ওপর ভিত্তি করে conventions তৈরি করছে: agent কীভাবে তার key directory নির্দেশ করবে এবং কোন request component তাকে sign করা উচিত। নভেম্বর 2026 পর্যন্ত, working group-এর protocol document হলো draft-ietf-webbotauth-httpsig-protocol, যা Signature-Agent header, JSON Web Key Sets-এর ওপর ভিত্তি করে একটি key directory format, এবং সেটি পরিবেশনের জন্য একটি well-known URI নির্ধারণ করে। দুটিই বিস্তারিতভাবে বোঝা মূল্যবান, কারণ verification সূক্ষ্মভাবে ভুল হওয়া সহজ।
Mechanics-এ যাওয়ার আগে, ফলাফল সম্পর্কে সুনির্দিষ্ট থাকুন। একটি valid signature দেখায় যে request-টি একটি published identifier, যেমন operator-এর domain-এর সঙ্গে যুক্ত key দিয়ে signed হয়েছে, এবং request-এর signed অংশগুলি পরিবর্তিত হয়নি। এটি দেখায় না agent-টিকে কে নির্দেশ দিয়েছে, কোনো ব্যক্তি তাকে authority delegate করেছে কি না, বা action-টি নিরাপদ কি না। Identity evidence এবং authorization আলাদা প্রশ্ন, এবং আপনার application দ্বিতীয়টির উত্তর দেয়। আমাদের signed AI agent request কী প্রমাণ করে সম্পর্কে blog post-এ উদাহরণসহ সেই সীমারেখা ব্যাখ্যা করা হয়েছে।
কোন header involved
একটি signed request সাধারণত তিনটি header বহন করে:
Signature-Input(RFC 9421, Section 4.1) প্রতিটি signature বর্ণনা করে: একটি label, covered component-এর তালিকা, এবংcreated,expires,keyid,alg,nonceএবংtag-এর মতো parameter।Signature(RFC 9421, Section 4.2) structured field হিসেবে প্রতিটি label-এর signature byte বহন করে, base64 encoded।Signature-Agent(Web Bot Auth convention থেকে) দেখায় client-এর public key কোথায় প্রকাশিত, সাধারণত operator-এর domain-এ একটি HTTPS URL।
একটি সরলীকৃত উদাহরণ:
GET /api/catalog?page=2 HTTP/1.1
Host: shop.example
Signature-Agent: "https://agent.example"
Signature-Input: sig1=("@method" "@authority" "@path" "signature-agent");created=1762934400;expires=1762934700;keyid="kq1";alg="ed25519";tag="web-bot-auth";nonce="d9f1c2"
Signature: sig1=:Base64SignatureBytes==:পঠনযোগ্যতার জন্য উদাহরণটি সরলীকৃত। বর্তমান working group draft-এ, Signature-Agent নিজেই signature label দ্বারা key করা একটি structured field (উদাহরণস্বরূপ sig1="https://agent.example"), তাই আপনার verification library কোন form আশা করে তা যাচাই করুন।
ধাপ ১: structured field সঠিকভাবে parse করুন
Signature-Input এবং Signature হলো HTTP Structured Fields (RFC 9651, যা RFC 8941-কে বাতিল করেছে)। এগুলো string splitting দিয়ে parse করবেন না। Quoted string, parameter এবং inner list-এর grammar নির্দিষ্ট, এবং ঢিলেঢালা parser ভুল প্রত্যাখ্যান এবং প্রকৃত নিরাপত্তা ফাঁক দুটোই তৈরি করে।
একটি maintained structured-field parser ব্যবহার করুন, অথবা আরও ভাল, একটি maintained RFC 9421 library ব্যবহার করুন যা parsing এবং verification একসঙ্গে করে। specification শুরু থেকে implement করা শেখার জন্য ভাল, production-এর জন্য খারাপ পছন্দ। নিচের ধাপগুলো এমন একটি library কী করে তা বর্ণনা করে, যাতে আপনি সেটি সঠিকভাবে configure করতে পারেন এবং এর output পর্যালোচনা করতে পারেন।
ধাপ ২: কোন signature evaluate করবেন তা বেছে নিন
একটি request-এ একাধিক signature label থাকতে পারে। আপনার policy-এর সঙ্গে মেলে এমনটি নির্বাচন করুন, উদাহরণস্বরূপ যে label-এর tag হলো web-bot-auth. যদি নিম্নলিখিতগুলির কোনোটি ঘটে, verification purpose-এর জন্য request-টি reject করুন (serving purpose-এর জন্য নয়):
- প্রয়োজনীয় parameter-সহ কোনো signature নেই,
- algorithm allowlist-এ নেই (উদাহরণস্বরূপ
ed25519), অথবা - covered component-এ আপনার প্রয়োজনীয় ন্যূনতম সেট নেই।
স্বয়ংক্রিয় client-এর জন্য যুক্তিসংগত ন্যূনতম হলো @method, @authority এবং @path, এবং header উপস্থিত থাকলে signature-agent। @authority ছাড়া, এক site-এর জন্য captured signature অন্য site-এর বিরুদ্ধে replay করা যেতে পারে। @path ছাড়া, এটি endpoint জুড়ে পুনরায় ব্যবহার করা যেতে পারে। Web Bot Auth protocol draft agent-দের @authority বা @target-uri cover করতে বলে, এবং সতর্ক করে যে শুধুমাত্র authority cover করা signature expiry পর্যন্ত সেই host-এ যেকোনো method বা path-এর বিরুদ্ধে যাচাই হয়ে যায়।
ধাপ ৩: সময় parameter পরীক্ষা করুন
RFC 9421, Section 2.3-এ সংজ্ঞায়িত created এবং expires, সময় parameter পড়ুন। তারপর:
- যে signature-এর
createdসময় সামান্য clock skew allowance-এর বাইরে ভবিষ্যতে, তা reject করুন। - যে signature
expiresঅতিক্রম করেছে, তা reject করুন। expiresঅনুপস্থিত হলে,created-এর ভিত্তিতে আপনার own maximum age enforce করুন।
সংক্ষিপ্ত validity window captured request replay হওয়ার সময় কমায়।
ধাপ ৪: key খুঁজে বের করুন
Signature-Agent header operator-এর key directory-এর দিকে নির্দেশ করে। সেটি HTTPS দিয়ে আনুন, এবং শুধু HTTPS দিয়েই। Web Bot Auth protocol draft directory-টিকে একটি JSON Web Key Set হিসেবে বর্ণনা করে (keys JWK format-এ) যা সেই origin-এর http-message-signatures-directory well-known URI থেকে পরিবেশিত হয়। তারপর এমন key খুঁজুন যার identifier keyid parameter-এর সঙ্গে মেলে।
type Jwk = { kid: string; kty: string; crv?: string; x?: string };
const keyCache = new Map<string, { keys: Jwk[]; fetchedAt: number }>();
const KEY_TTL_MS = 60 * 60 * 1000;
export async function getKey(directoryUrl: string, keyId: string): Promise<Jwk | null> {
const url = new URL(directoryUrl);
if (url.protocol !== "https:") return null;
const cached = keyCache.get(url.origin);
const fresh = cached && Date.now() - cached.fetchedAt < KEY_TTL_MS;
const keys = fresh ? cached.keys : await fetchKeys(url.origin);
return keys.find((k) => k.kid === keyId) ?? null;
}
async function fetchKeys(origin: string): Promise<Jwk[]> {
const res = await fetch(`${origin}/.well-known/http-message-signatures-directory`, {
headers: { accept: "application/http-message-signatures-directory+json, application/json" },
signal: AbortSignal.timeout(2000),
});
if (!res.ok) return [];
const body = (await res.json()) as { keys?: Jwk[] };
const keys = Array.isArray(body.keys) ? body.keys : [];
keyCache.set(origin, { keys, fetchedAt: Date.now() });
return keys;
}Key discovery-এর ব্যবহারিক নিয়ম:
- Key cache করুন সীমাবদ্ধ lifetime দিয়ে, যাতে verification প্রতিটি request-এ network call যোগ না করে।
- সংক্ষিপ্ত timeout সেট করুন এবং failed fetch-কে "could not verify" হিসেবে ধরুন, কখনোই "verified" হিসেবে নয়।
- সম্ভব হলে কোন origin থেকে fetch করবেন তা সীমিত করুন, অথবা অন্তত private address range ব্লক করুন যাতে server-side request forgery এড়ানো যায়। Protocol draft ঠিক এই কারণেই এর security consideration-এ server-side request forgery তালিকাভুক্ত করেছে।
- Identity-কে origin-এর সঙ্গে bind করুন, user agent-এর free-text নামের সঙ্গে নয়। আপনি যে identifier record করেন সেটি হলো directory origin।
ধাপ ৫: signature base পুনর্গঠন করুন
Signer raw request sign করেনি। এটি signature base নামে একটি canonical string sign করেছে (RFC 9421, Section 2.5), যা covered component-গুলোকে ক্রমানুসারে নিয়ে এবং শেষে @signature-params line যোগ করে তৈরি। আপনার verifier একই request থেকে একই string পুনর্গঠন করে।
উপরের উদাহরণের জন্য base দেখতে এমন:
"@method": GET
"@authority": shop.example
"@path": /api/catalog
"signature-agent": "https://agent.example"
"@signature-params": ("@method" "@authority" "@path" "signature-agent");created=1762934400;expires=1762934700;keyid="kq1";alg="ed25519";tag="web-bot-auth";nonce="d9f1c2"Proxy-এর পেছনে verification ভেঙে দিতে পারে এমন বিষয়গুলোর দিকে লক্ষ্য করুন:
@authorityঅবশ্যই client যে host লক্ষ্য করেছিল সেটিই হতে হবে। যদি load balancerHostrewrite করে, forwarded original value ব্যবহার করুন।@pathquery string বাদ দেয়। যদি client@querycover করে, সেটি যেমন received হয়েছে ঠিক তেমনই পুনর্গঠন করুন।- Header value নির্দিষ্টভাবে canonicalize করা হয়: trimmed, এবং multiple value একত্রিত করা হয়। একটি ভাল library আপনার জন্য এটি করে।
ধাপ ৬: signature যাচাই করুন
Base এবং public key দিয়ে verification একটি একক call, RFC 9421, Section 3.2-এর ধাপ অনুসরণ করে। Node.js-এ Ed25519-এর জন্য, built-in crypto module ব্যবহার করে:
import { createPublicKey, verify } from "node:crypto";
export function verifyEd25519(jwk: Jwk, base: string, signatureB64: string): boolean {
const key = createPublicKey({ key: jwk as any, format: "jwk" });
return verify(null, Buffer.from(base, "utf8"), key, Buffer.from(signatureB64, "base64"));
}চেক ব্যর্থ হলে request verified নয়। তবু এটি serve করা যেতে পারে। শুধু এটি "verified" basis পায় না।
ধাপ ৭: replay প্রতিরোধ করুন
একটি valid signature capture করে expiry না হওয়া পর্যন্ত আবার পাঠানো যেতে পারে। RFC 9421, Section 7.2.2 signature replay কভার করে, এবং Web Bot Auth protocol draft নিজস্ব nonce নিয়ম যোগ না করে সেটির ওপর নির্ভর করে। দুটি নিয়ন্ত্রণ এই window-এর বেশির ভাগ বন্ধ করে:
- সংক্ষিপ্ত expiry, ধাপ ৩-এ enforced।
- Nonce tracking. যখন
nonceউপস্থিত থাকে, সেটি তার key identifier সহ expiry পর্যন্ত সংরক্ষণ করুন, এবং দ্বিতীয়বার ব্যবহার হলে reject করুন।
const seen = new Map<string, number>();
export function acceptNonce(keyId: string, nonce: string, expiresAt: number): boolean {
const id = `${keyId}:${nonce}`;
const now = Date.now() / 1000;
for (const [k, exp] of seen) if (exp < now) seen.delete(k);
if (seen.has(id)) return false;
seen.set(id, expiresAt);
return true;
}একাধিক instance-যুক্ত deployment-এ, বাকি validity-এর সমান time to live সহ Redis-এর মতো একটি shared store-এ nonce রাখুন।
ধাপ ৮: basis-সহ ফলাফল record করুন
Verification evidence তৈরি করে, verdict নয়। এটি এমনভাবে record করুন যাতে আপনার analytics এবং policy এটি ব্যবহার করতে পারে:
const result = {
actor: "agent",
basis: "verified",
operator: "https://agent.example",
keyId: "kq1",
coveredComponents: ["@method", "@authority", "@path", "signature-agent"],
verifiedAt: new Date().toISOString(),
};Verification ব্যর্থ হলে বা চলতে না পারলে, সেটিও reason-সহ record করুন: missing header, unsupported algorithm, key not found, expired, replayed, বা signature mismatch। এই কারণগুলো মূল্যবান। একটি operator থেকে "key not found"-এর spike প্রায়ই বোঝায় তারা key rotate করেছে, আক্রমণ করছে তা নয়।
Signature verification থেকে কী log করা উচিত?
Operator origin, key identifier, covered component, time parameter, outcome এবং failure reason log করুন। পূর্ণ signature byte বা পুরো request body log করবেন না। Pattern অধ্যয়নের জন্য যতটুকু প্রয়োজন ততদিন log রাখুন, এবং privacy commitment সম্মান করার জন্য যতটা সম্ভব সংক্ষিপ্ত রাখুন।
একটি verified signature আপনার policy কীভাবে বদলাবে?
Request verified হতে পারলে, আপনি সেগুলোকে অর্থবহভাবে ভিন্নভাবে treat করতে পারেন। মানুষ এবং AI agent-এর জন্য app policy ডিজাইন করা সম্পর্কে আমাদের post-এ বিস্তৃত policy model কভার করা হয়েছে:
- unverified automation-এর তুলনায় বেশি rate limit সহ verified operator-দের read-only route-এ allow করুন।
- পূর্ণ HTML page-এর বদলে JSON বা Markdown-এর মতো structured response offer করুন।
- উচ্চ-প্রভাবের action আপনার স্বাভাবিক authorization-এর পেছনে রাখুন। একটি verified signature আপনাকে জানায় কোন client call করছে, এটি account পরিবর্তন করতে পারে কি না তা নয়।
Observation mode-এ শুরু করুন। কোনো policy এর ওপর নির্ভর করার আগে আপনার স্বয়ংক্রিয় traffic-এর কত অংশ signed, কোন operator দেখা যাচ্ছে, এবং verification কতবার ব্যর্থ হচ্ছে তা measure করুন।
Proof of Actor কোথায় fit করে
Signature verification হলো কয়েকটি evidence source-এর একটি। Proof of Actor এটি অন্য signal-এর সঙ্গে combine করে এবং প্রতিটি result-এ basis দৃশ্যমান রাখে, যাতে একটি verified agent কখনো শুধুমাত্র নিজেকে declared করেছে এমন agent-এর সঙ্গে গুলিয়ে না যায়। আপনার application নির্ধারণ করে এরপর কী হবে।
আমাদের engineers-এর সঙ্গে production app-এ এটি build করতে, early access-এর জন্য আবেদন করুন। evidence কী দেখাতে পারে এবং কী দেখাতে পারে না, তার পূর্ণ চিত্রের জন্য Detection & Limitations পড়ুন।
উৎস
- RFC 9421: HTTP Message Signatures: signing এবং verification standard, signature parameter, signature base এবং replay consideration সহ
- RFC 9651: Structured Field Values for HTTP:
Signature-InputএবংSignature-এ ব্যবহৃত grammar - IETF Web Bot Auth working group: automated client authentication কাজের charter এবং status
- draft-ietf-webbotauth-httpsig-protocol: Internet-Draft যা
Signature-Agent, JWKS key directory এবং এর well-known URI নির্ধারণ করে (নভেম্বর 2026 পর্যন্ত work in progress) - RFC 7517: JSON Web Key (JWK): প্রকাশিত key directory-তে ব্যবহৃত key format
- RFC 8615: Well-Known URIs: key directory-এর মতো well-known path কীভাবে নিবন্ধিত হয়
- RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA): উদাহরণে ব্যবহৃত Ed25519 algorithm
- Node.js crypto documentation: ধাপ ৬-এ ব্যবহৃত
createPublicKeyএবংverify