# Authenticated agent API calls में content type confusion

Authenticated agent calls में network edge से action handler तक request की एक ही व्याख्या होनी चाहिए। अगर gateway को harmless JSON request दिखती है, authorization layer को fields का एक set मिलता है और handler को privileged form submission, तो credential ने अपना काम किया, फिर भी API असफल रही।

यह सिर्फ पुराने browser forms की समस्या नहीं है। Agents सीधे HTTP generate करते हैं, तेज़ी से retry करते हैं, tool descriptions से examples दोबारा इस्तेमाल करते हैं और अक्सर ऐसे credentials के साथ काम करते हैं जो वास्तविक systems बदल सकते हैं। जब request body target, amount, environment, command या permission चुनती है, तो वह authorization decision का हिस्सा होती है। Caller को action की अनुमति है या नहीं, यह तय करने से पहले media type, syntax और schema को स्पष्ट करें।

## Authenticated request का एक ही अर्थ होना चाहिए

Authentication बताता है कि credential किसने प्रस्तुत किया। Authorization बताता है कि वह caller कोई action कर सकता है या नहीं। इनमें से कोई भी यह नहीं बताता कि हर component action के arguments पर सहमत है या नहीं।

मान लीजिए कोई endpoint deployment target बदलता है:

```http
POST /v1/deployments/promote HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json

{"environment":"staging","release":"2026.07.22"}
```

Authorization code `staging` में promotion की अनुमति दे सकता है, लेकिन production को मना कर सकता है। यह तभी सुरक्षित है जब उसे वही `environment` value मिले जिसे action handler इस्तेमाल करता है। अगर middleware layer JSON पढ़ती है, handler बाद में form parameters देखता है और दोनों एक ही request object को भर सकते हैं, तो आपने truth के दो sources बना दिए हैं।

इसके लिए cryptographic token का टूटना ज़रूरी नहीं है। Legitimate session वाला agent ऐसा body भेज सकता है जिसे एक layer अनदेखा करे और दूसरी मान ले। Compromised agent भी यही कर सकता है। नतीजा input formatting के रूप में व्यक्त authorization bypass है।

RFC 9110 के अनुसार `Content-Type` associated representation का media type बताता है और data format तथा recipient द्वारा उसे process करने का तरीका दोनों तय करता है। इसलिए यह header केवल सजावट नहीं, request semantics का हिस्सा है। वही RFC यह भी अनुमति देता है कि `Content-Type` न होने पर recipient `octet-stream` माने या data को inspect करे। Generic file handling में यह उपयोगी है, लेकिन protected action APIs के लिए खराब default है।

Action endpoint के लिए यह invariant तय करें:

> केवल एक accepted media type request bytes को ठीक एक validated command object में बदले। हर security decision और हर side effect उसी object का इस्तेमाल करे।

Endpoint एक से अधिक representations support कर सकता है, लेकिन हर representation का अपना contract और test suite होना चाहिए। कई parsers को interchangeable सुविधा न मानें।

## Content-Type header schema नहीं है

`Content-Type: application/json` का अर्थ यह नहीं है कि request का वही shape है जिसकी आपको उम्मीद थी। इसका अर्थ केवल यह है कि sender दावा कर रहा है कि body JSON media type इस्तेमाल करती है। आपको अब भी तय करना है कि यह type इस route पर supported है या नहीं, parameters allowed हैं या नहीं, body syntactically valid है या नहीं और decoded value operation contract से मेल खाती है या नहीं।

Protected endpoint में allowed representation set जानबूझकर छोटा रखें। कई command endpoints को केवल JSON स्वीकार करना चाहिए। Upload endpoint केवल multipart स्वीकार कर सकता है। बिना arguments वाले action को कोई body स्वीकार नहीं करनी चाहिए। Accepted set जितना बड़ा होगा, parser paths की जिम्मेदारी उतनी ही बढ़ेगी।

OWASP REST Security Cheat Sheet की व्यावहारिक सलाह स्पष्ट है: supported content types document करें और unexpected या missing types अस्वीकार करें। Zero content length वाली request में omitted content type की अनुमति दी जा सकती है। यह भी सुनिश्चित करें कि body और घोषित type एक-दूसरे से मेल खाएं, ताकि producer और consumer अलग अर्थ न निकालें।

Authenticated actions में एक सावधानी और जोड़ें। पहले character को देखकर parser चुनकर claimed type को “match” न करें। `{` से शुरू होने वाली body को JSON की तरह process करने की अनुमति नहीं मिलती, अगर request form data के रूप में घोषित है। Sniffing स्पष्ट contract को implementation guess में बदल देती है।

एक उपयोगी route contract ऐसा हो सकता है:

| Route | Allowed request media type | Body rule |
|---|---|---|
| `POST /v1/deployments/promote` | `application/json` | `PromoteRequest` से मेल खाता required JSON object |
| `POST /v1/artifacts` | `multipart/form-data` | `ArtifactUpload` से मेल खाते required parts |
| `POST /v1/sessions/revoke` | none | बिल्कुल zero bytes |

Media-type parameters के बारे में सटीक रहें। अगर आपका JSON parser `application/json; charset=utf-8` स्वीकार करता है, तो इसे document करें और parameters को एक ही library से normalize करें। अगर केवल bare `application/json` स्वीकार है, तो parameter अस्वीकार करें, ताकि proxy और application अलग व्यवहार न करें। कौन-सा विकल्प चुना गया है, इससे कम महत्वपूर्ण है कि उसे एक ही तरीके से लागू किया जाए।

`Accept` की response preference को request के `Content-Type` से अलग रखें। Client JSON response मांग सकता है, जबकि उसकी request body invalid हो। `Accept` header को कभी यह तय न करने दें कि action endpoint किन request formats को parse करेगा।

## JSON को valid syntax से आगे भी rules चाहिए

JSON parser ऐसी input को सफलतापूर्वक parse कर सकता है जिसे API को फिर भी अस्वीकार करना चाहिए। Duplicate member names इसका स्पष्ट उदाहरण हैं:

```json
{"environment":"staging","environment":"production","release":"2026.07.22"}
```

RFC 8259 object names को unique रखने की सलाह देता है और इसका कारण भी बताता है: receivers duplicate names पर सहमत नहीं होते। कई parsers आखिरी value रखते हैं, कुछ fail होते हैं और कुछ हर pair दिखाते हैं। यह documented interoperability problem है, केवल style preference नहीं।

मान लीजिए authorization middleware पहले `environment` value रखने वाला parser इस्तेमाल करता है, जबकि downstream decoder आखिरी value रखता है। Middleware staging को approve करेगा और handler production को promote कर देगा। इसे बेहतर role names या किसी और token claim से ठीक नहीं किया जा सकता। किसी भी component के decision लेने से पहले request अस्वीकार करें।

Loose language binding में harmless दिखने वाली values के लिए भी यही सावधानी रखें:

- Action requests में unknown object members अस्वीकार करें, जब तक उन्हें रखने का स्पष्ट compatibility reason न हो।
- Expected JSON type अनिवार्य करें। Boolean ऐसा string नहीं है जो संयोग से `true` कहता हो, और integer identifier floating-point number नहीं है।
- Parse करने से पहले body size की सीमा तय करें। विशाल body पढ़कर memory खत्म हो जाने के बाद schema validator आपकी रक्षा नहीं कर सकता।
- तय करें कि field omitted, `null` या empty string हो सकती है या नहीं। ये तीन अलग states हैं।
- Trailing data और comments, `NaN` या unquoted names जैसे parser extensions अस्वीकार करें, यदि library इन्हें enable करती है।

Generic map से सीधे authorization न करें। Explicit schema वाले request type में decode करें, semantic validation करें और फिर ऐसा internal command type बनाएं जिसमें raw parser artifacts न रहें। `PromoteCommand { environment, release }` पाने वाले handler के पास input की नई व्याख्या करने की गुंजाइश उस handler से कम होती है जिसे map, query collection, request object और raw body मिलती है।

Numbers पर विशेष ध्यान दें। JSON grammar बड़े numeric literals की अनुमति देती है, लेकिन कई runtimes numbers को floating-point representation में decode करते हैं। यदि value money, quota, database record या signed payload की पहचान करती है, तो string format या documented range वाले integer parser का इस्तेमाल करें। एक layer को number round करके दूसरी layer से compare न करने दें।

## Form bodies छिपे हुए array और nesting rules बनाती हैं

`application/x-www-form-urlencoded` query string जैसी दिखती है, इसलिए सरल लगती है। लेकिन repeated names, bracket notation, plus signs और empty values को libraries अर्थ देने लगें तो यह सरल नहीं रहती।

इन bodies पर ध्यान दें:

```text
role=user&role=admin
role[]=user&role[]=admin
role[user]=1&role[admin]=1
role=user%26role%3Dadmin
```

अलग frameworks इन्हें last scalar, first scalar, array, object, literal field names या parsing error मान सकते हैं। कुछ middleware हर request method के लिए forms parse करते हैं। कुछ application frameworks query parameters और form parameters को एक सुविधा वाले object में मिला देते हैं। इसी सुविधा वाले object में protected APIs यह भूल जाती हैं कि caller ने वास्तव में क्या भेजा था।

HTTP Parameter Pollution पर OWASP की testing guidance बताती है कि behavior application, web server, WAF और middleware के बीच interaction पर निर्भर करता है। इसी कारण raw repeated parameters को test करें, केवल किसी एक framework के parser documentation पर भरोसा न करें।

हर endpoint पर “client compatibility” के लिए JSON और URL-encoded forms स्वीकार करने की लोकप्रिय सलाह आम तौर पर गलत है। यह इसलिए चलती है क्योंकि demo client लिखना आसान हो जाता है और कई frameworks इसे default रूप से enable करते हैं। लेकिन इससे हर action के representation contracts दोगुने हो जाते हैं और query fields body के साथ merge होने पर एक तीसरा contract भी चुपचाप जुड़ जाता है।

अगर form endpoint support करना ही पड़े, तो उसके लिए endpoint-specific parser policy बनाएं:

1. Repeated names अस्वीकार करें, जब तक schema field को list के रूप में define न करती हो।
2. Bracket syntax अस्वीकार करें, जब तक schema उसकी exact encoding define न करती हो और parser उसे लगातार एक ही तरह implement न करता हो।
3. Query parameters को form fields से अलग रखें। किसी एक source को दूसरे को overwrite न करने दें।
4. Validation के बाद parsed fields को JSON route में इस्तेमाल होने वाले उसी typed internal command में बदलें।
5. Production request path से percent encoding, `+` बनाम `%20`, blank values, missing `=` और duplicate fields test करें।

इसे “first wins” या “last wins” चुनकर हल न करें। इससे एक component के भीतर deterministic answer मिलेगा, लेकिन दूसरे components के बीच disagreement बना रहेगा। Protected scalar field केवल एक बार आनी चाहिए।

## Multipart upload protocol है, flexible JSON नहीं

`multipart/form-data` का सही उपयोग है: यह कई independently headed parts भेजती है, अक्सर file content के साथ। RFC 7578 इसे form values के लिए define करता है और parts अलग करने वाला boundary parameter अनिवार्य करता है। हर part अपने headers और filename metadata भी ला सकता है।

इसी structure के कारण multipart ordinary authenticated commands के लिए खराब fallback representation है। इसमें अधिक syntax, अधिक size handling, duplicate field names की अधिक जगह और gateway द्वारा एक part inspect करने तथा application द्वारा दूसरा चुनने की अधिक संभावना होती है।

एक आम खराब design file के साथ JSON `metadata` part स्वीकार करता है और फिर top-level form fields को metadata override करने देता है:

```text
Content-Disposition: form-data; name="metadata"

{"project":"alpha","visibility":"private"}

Content-Disposition: form-data; name="visibility"

public
```

एक component `metadata.visibility` के आधार पर authorization कर सकता है, जबकि दूसरा बाद वाले form part को handler के `visibility` parameter में bind कर सकता है। एक security-sensitive property के लिए request में दो values हैं, और वे दो grammars में व्यक्त की गई हैं।

Multipart endpoints को अलग-अलग काम वाले named parts के आसपास design करें। उदाहरण के लिए, ठीक एक `file` part और ठीक एक `manifest` part स्वीकार करें। `manifest` को अपने strict schema वाले JSON के रूप में अनिवार्य करें। Upload contract में न दिए गए part names अस्वीकार करें, singleton parts के duplicates अस्वीकार करें, total body size और file size के लिए अलग limits रखें और तय करें कि part-level `Content-Type` values अनिवार्य हैं या नहीं।

Filename को path, MIME claim को file classification या multipart parser के temporary-file behavior को security control न मानें। ये अलग upload समस्याएं हैं। Parser-confusion rule सरल है: authorization inputs एक named, validated source से आने चाहिए। अगर `manifest.project` तय करता है कि file कहाँ जाएगी, तो कोई दूसरा part, query parameter या header उस project को नहीं बदल सकता।

जब command में file नहीं है, तो multipart स्वीकार न करें। हर अतिरिक्त media type components के बीच disagreement का एक और रास्ता है।

## Empty body validation की अनुपस्थिति नहीं, एक contract है

कुछ authenticated actions को arguments की ज़रूरत नहीं होती। Current session revoke करना, server-generated nonce rotate करना या fixed event acknowledge करना empty request body के साथ हो सकता है। ऐसे मामलों में emptiness को enforce करें।

No-body contract वाला endpoint इन सभी को अस्वीकार करे:

```http
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/json
Content-Length: 2

{}
```

```http
POST /v1/sessions/revoke HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

scope=other
```

```http
POST /v1/sessions/revoke HTTP/1.1
Transfer-Encoding: chunked

0

```

आखिरी उदाहरण में content नहीं है, लेकिन फिर भी framing mechanism इस्तेमाल हो रहा है जिसे आपका no-body contract रोक सकता है। इसे reject करना है या नहीं, यह आपके HTTP stack पर निर्भर है, लेकिन edge पर तय करके test करें। Proxy को ऐसी framing pass न करने दें जिसे application अलग तरह से समझे।

No-body route के लिए business logic से पहले ये rules लागू करें:

- Request में कोई content bytes न हों।
- Compatibility rule स्पष्ट रूप से अनुमति न दे तो route `Content-Type` स्वीकार न करे।
- Route query parameters को command में merge न करे, जब तक हर permitted query name अपने schema में मौजूद न हो।
- Server action को argument-free के रूप में record करे, न कि generic request object log करे जिसे बाद में पढ़ने वाले input समझ लें।

RFC 9110 request content को method semantics के अनुसार बताता है। केवल POST इस्तेमाल होने से body का universal अर्थ नहीं बन जाता। वह अर्थ आपके resource contract से आता है।

असुविधाजनक स्थिति वह है जब कोई client library हमेशा `{}` भेजती हो। केवल उसे accommodate करने के लिए endpoint को व्यापक न बनाएं। Client ठीक करें या उसके लिए अलग documented route दें। आज जिसका कोई प्रभाव नहीं है, वह बाद में handler बदलने पर accidental input channel बन सकता है।

## Authorization से पहले validate करें और validated command से execute करें

सबसे सुरक्षित request pipeline एक ही दिशा में चलती है। Raw bytes आती हैं। Route एक permitted parser चुनती है। Parser typed value बनाता है। Validation canonical command बनाती है। Authorization उसी command का मूल्यांकन करती है। Executor को वही command मिलती है।

```text
raw HTTP request
  -> route and media-type check
  -> bounded body read
  -> one strict parser
  -> schema and semantic validation
  -> canonical command
  -> authorization
  -> execution and audit record
```

बीच के stages का क्रम उलटें नहीं। Authorization को अक्सर project ID, environment, recipient या command mode जैसे fields चाहिए होते हैं, इसलिए teams loosely parsed input को जल्दी inspect करने लगती हैं। इससे pre-authorization parser बन जाता है जिसका behavior हमेशा final decoder जैसा ही रहना चाहिए। बहुत कम systems इस promise को लंबे समय तक निभा पाते हैं।

Canonical command diagrams का pattern नहीं, एक व्यावहारिक boundary है। इसमें केवल executor को चाहिए values हों और raw body text, form collections, framework request objects तथा aliases न हों। अगर executor को `target_environment` मिला है, तो target missing या inconvenient होने पर उसे बाद में `req.query.environment` नहीं देखना चाहिए।

इससे audit records भी बेहतर होते हैं। Authenticated principal, endpoint, accepted media type, request digest, सुरक्षित रूप से रखने योग्य canonical command fields, authorization decision और result log करें। Raw request bodies को default रूप से log करने से दूसरी समस्या पैदा होती है, क्योंकि bodies में credentials, uploaded files और user data हो सकते हैं। Digest preserved evidence से event correlate करने देता है, बिना logs को secret store बनाए।

Request signing में भी यही अनुशासन चाहिए। अगर client bytes sign करता है लेकिन server normalized object authorize करता है, तो signed representation rules और canonicalization rules दोनों record करें। अगर client canonical object sign करता है, तो signature verify करने से पहले alternate encodings अस्वीकार करें। वरना दो byte sequences एक business request का अर्थ रख सकती हैं, या parsing के बाद एक sequence का अर्थ बदल सकता है।

## केवल happy parser नहीं, disagreement test करें

एक valid JSON fixture deserialize करने वाले unit tests parser agreement के बारे में लगभग कुछ साबित नहीं करते। आपका test target वही public request path होना चाहिए जो production में है: load balancer या reverse proxy, gateway, framework middleware, route handler और body को फिर से parse करने वाली कोई service।

हर authenticated operation के लिए compact negative corpus बनाएं। इसे CI में disposable environment के विरुद्ध चलाएं और response के साथ side effects की अनुपस्थिति भी assert करें। अगर queue message, audit event या partial file write पहले ही हो गई, तो `400` response पर्याप्त नहीं है।

इस shell harness से शुरुआत करें। यह generated client पर निर्भर होने के बजाय raw bodies भेजता है, जो malformed inputs भेजने से मना कर सकता है:

```bash
base=https://api.test.example
bearer='test-token'

send() {
  name=$1
  type=$2
  body=$3
  code=$(curl -sS -o "/tmp/${name}.out" -w '%{http_code}' \
    -X POST "$base/v1/deployments/promote" \
    -H "Authorization: Bearer $bearer" \
    -H "Content-Type: $type" \
    --data-binary "$body")
  printf '%-28s %s\n' "$name" "$code"
}

send valid_json 'application/json' \
  '{"environment":"staging","release":"2026.07.22"}'
send duplicate_json 'application/json' \
  '{"environment":"staging","environment":"production","release":"2026.07.22"}'
send form_body 'application/x-www-form-urlencoded' \
  'environment=production&release=2026.07.22'
send false_json 'application/json' \
  'environment=production&release=2026.07.22'
```

Expected output में एक success और तीन client rejections होने चाहिए:

```text
valid_json                   200
duplicate_json               400
form_body                    415
false_json                   400
```

आपका actual status convention syntactically valid लेकिन schema में असफल request के लिए `422` लौटा सकता है। महत्वपूर्ण distinction बनाए रखें: media-type mismatch कभी fallback parser तक न पहुंचे और duplicate JSON member कभी authorization तक न पहुंचे।

Components के बीच boundaries को target करने वाले cases भी जोड़ें:

| Case | What must happen |
|---|---|
| Nonempty body के साथ missing `Content-Type` | Parsing से पहले reject |
| Unknown field वाला JSON object | Reject करें या documented compatibility behavior record करें |
| Repeated form scalar | Reject |
| JSON value से conflict करता query value | Route contract के अनुसार reject करें या query ignore करें |
| दो `manifest` parts वाला multipart | Reject |
| No-body route को `{}` मिले | Reject |

इसके बाद audit trail देखें। हर rejected input में route और rejection class बताने वाला trace होना चाहिए, बिना sensitive request contents log किए। हर accepted input से एक canonical command बननी चाहिए। यदि logs दिखाएं कि gateway ने एक target देखा और handler ने दूसरा record किया, तो 2xx response मिलने पर भी disagreement मिल गया।

## Proxies और middleware भी parsers हैं

Teams अक्सर application parser पर ध्यान देती हैं और उससे पहले के components भूल जाती हैं। Reverse proxies headers normalize कर सकते हैं। API gateways rule लागू करने के लिए JSON inspect कर सकते हैं। WAF form data parse कर सकता है। Observability middleware body पढ़कर उसे फिर से बना सकता है। Framework route handler के चलने से पहले query, form और JSON fields भर सकता है।

OWASP की HTTP request smuggling guidance इस समस्या का बड़ा रूप बताती है: intermediaries और backend servers request boundaries को अलग-अलग समझ सकते हैं, खासकर protocol translation और framing के आसपास। Content-type confusion खतरनाक होने के लिए request smuggling ज़रूरी नहीं है, लेकिन दोनों failures अलग-अलग layers को incompatible parsing decisions लेने देने से पैदा होते हैं।

Action path में हर body reader की inventory बनाएं। हर एक के लिए लिखें कि वह कौन-से media types parse करता है, duplicate values रखता है या नहीं, content decompress करता है या नहीं, size limit लगाता है या नहीं और body rewrite कर सकता है या नहीं। अगर कोई इन सवालों का जवाब नहीं दे सकता, तो endpoint agent credentials के लिए तैयार नहीं है।

Gateway की भूमिका सीमित रखें। वह route-level body limits लागू कर सकता है और ऐसे media types block कर सकता है जिन्हें route कभी स्वीकार नहीं करती। वह application से पहले malformed headers भी reject कर सकता है। लेकिन form data को JSON में बदलने या duplicate fields को “clean” करने के लिए gateway transformation का इस्तेमाल न करें। Application को execution की exact semantics के अनुसार ambiguity खुद reject करनी होगी।

Production में इस्तेमाल होने वाले HTTP versions और deployment paths test करें। Local development server के विरुद्ध सही व्यवहार करने वाली request तब बदल सकती है जब HTTP/2 client ऐसे proxy तक पहुंचे जो application को HTTP/1.1 forward करता है। लक्ष्य attack research lab बनाना नहीं है। लक्ष्य यह साबित करना है कि production chain हर accepted request के लिए एक command object बनाती है।

## Agent gateways को boundary सुरक्षित रखनी चाहिए

Agent gateway को credentials को model से दूर रखना और action का record बनाए रखना चाहिए, लेकिन वह permissive target API को अपने-आप safe नहीं बना सकता। Gateway को वही representation भेजनी होगी जिसे target route स्पष्ट रूप से support करती है और target को authority evaluate करने से पहले उस representation को validate करना होगा।

Sallyport का HTTP channel credentials inject करता है और API keys को agent से बाहर रखता है, इसलिए agent secret पाए बिना action request कर सकता है। यह उपयोगी credential boundary है। इसके साथ ऐसे endpoint contracts भी रखें जो ambiguous bodies reject करें, क्योंकि protected credentials उस request को अब भी authorize करते हैं जो API तक पहुंचती है।

Sensitive systems के लिए generic “make any HTTP request” action देने के बजाय agents को contract के अनुरूप tools दें। Promotion tool को typed `environment` और `release` arguments लेने चाहिए। उसका implementation एक JSON object serialize करे, एक media type set करे और ऐसे tool inputs reject करे जो API schema पूरी न कर सकें। Receiving service को validation फिर से करनी होगी। Tool schemas mistakes घटाती हैं, लेकिन server-side distrust की जगह नहीं लेतीं।

जब agent को upload चाहिए, तो named file और named manifest वाला अलग tool बनाएं। जब उसे no-argument action चाहिए, तो body field ही न दें। ये छोटे constraints agent के intended request को inspect, approve, test environment में replay और बाद में audit करना आसान बनाते हैं।

Vague capability approve करके parsers से missing precision भरने की अपेक्षा न करें। Endpoint को एक ही अर्थ स्वीकार करने दें, agent को वही अर्थ भेजने दें और credential के authorization से पहले हर alternate spelling reject करें।
