# क्या HTTP request injection line break से शुरू हो सकता है?

क्रेडेंशियल injector API key को एजेंट के context से बाहर रख सकता है, फिर भी उस key को गलत request में भेज सकता है। समस्या तब होती है जब gateway एजेंट के input को बेअसर text मानता है और credential layer का काम पूरा होने के बाद उसे HTTP syntax में चिपका देता है।

मैंने टीमों को vault storage, approval prompt और audit log पर सचमुच मेहनत करते देखा है, फिर भी एजेंट और network के बीच string formatter छोड़ दिया जाता है। वह formatter सुरक्षा सीमा का हिस्सा बन जाता है। अगर वह destination, custom header name या एजेंट से आए header value में carriage return या newline स्वीकार करे, तो डेटा request structure बना सकता है।

इसका उपाय जानबूझकर साधारण है: request render करने से पहले carriage return और newline अस्वीकार करें, string से जोड़ने के बजाय structured input parse करें, और process से निकलने वाले bytes की जांच करें। इसे trimming या replacement से sanitize करने की कोशिश न करें। अस्वीकार किया गया request ईमानदार होता है। बदला हुआ request कोई दूसरा request हो सकता है।

## क्या HTTP request injection line break से शुरू हो सकता है?

हां। HTTP/1.1 में line break message के हिस्सों को अलग करता है। यदि code उस सीमा को लागू किए बिना अविश्वसनीय text को request line या header line में रखता है, तो हमलावर CRLF, यानी carriage return के बाद newline, का इस्तेमाल वर्तमान line खत्म करके नई line शुरू करने के लिए कर सकता है।

सरल बनाया गया कमजोर renderer अक्सर बेअसर दिखता है:

```text
GET {path} HTTP/1.1\r\n
Host: {host}\r\n
Authorization: Bearer {vault_token}\r\n
{custom_name}: {agent_value}\r\n
\r\n
```

मान लें कि एजेंट यह value देता है:

```text
blue\r\nX-Forwarded-Host: internal.example
```

अब rendered bytes में एक अतिरिक्त header है। credential एजेंट के prompt में लीक नहीं हुआ। फिर भी एजेंट ने authenticated request को ऐसे प्रभावित किया, जैसा designer ने नहीं चाहा था।

इसी तरह का बग destination string में भी आता है, जब gateway पहली line खुद बनाता है, या जब एक layer URL parse करती है और दूसरी बाद में path, host या proxy target को text formatting से जोड़ती है। सटीक नतीजा HTTP library और बीच के components के व्यवहार पर निर्भर करता है। यह अनिश्चितता बचाव नहीं है। message boundary पर अलग components का मतभेद ही छोटी validation कमी को security incident बनाता है।

RFC 9110 में HTTP field line को field name, colon और field value से बना syntax बताया गया है। इसमें line break को भी message framing माना गया है, field के भीतर का सामान्य content नहीं। यह फर्क «user input को escape करें» जैसी आम चेतावनी से ज्यादा महत्वपूर्ण है। इस काम के लिए header field में line break का कोई उपयोगी escaped रूप नहीं है। उसे अस्वीकार करें।

## Vault credential की रक्षा करता है, request shape की नहीं

Credential injection और request injection अलग समस्याएं हल करते हैं। पहला secret को एजेंट से दूर रखता है। दूसरा यह पक्का करता है कि जिस request को secret मिलता है, उसका shape वही हो जिसे इंसान ने मंजूर किया था।

ऐसे tool पर विचार करें जो एजेंट को vault में रखे bearer token के साथ billing API call करने देता है और वैकल्पिक custom header स्वीकार करता है। डेवलपर सोच सकता है कि token tool arguments में कभी आता ही नहीं, इसलिए header का जोखिम कम है। यह सोच निकटता को नजरअंदाज करती है। Inject की गई line forwarding header जोड़ सकती है, content length बदल सकती है, application header की duplicate बना सकती है या downstream में इस्तेमाल होने वाले audit label को दूषित कर सकती है। credential गुप्त रहता है, लेकिन कार्रवाई असुरक्षित हो जाती है।

Destination को भी इसी संदेह से देखें। Data जैसा दिखने वाला hostname authority syntax बन सकता है। Path request target बन सकता है। Proxy selector client के कनेक्ट होने की जगह बदल सकता है। यदि एजेंट इनमें से किसी value को प्रभावित कर सकता है, तो gateway को credential जोड़ने से पहले तय करना होगा कि कौन से रूप valid हैं।

इसीलिए approved hostname की allowlist उपयोगी है, पर पर्याप्त नहीं। वह parsed URL की मंजिल सीमित करती है। इससे साबित नहीं होता कि हर component को वही parsed URL मिला, न ही यह user info, header override या allowlist check के बाद जोड़े गए string में line break रोकती है। सीमा को control characters अस्वीकार करने चाहिए और HTTP client तक structured values बनाए रखनी चाहिए।

## किसी request object के बनने से पहले CR और LF अस्वीकार करें

सबसे सुरक्षित नियम एक वाक्य में कहा जा सकता है: एजेंट के नियंत्रण वाले destination field, custom header name और custom header value में decoding के बाद न `\r` होना चाहिए, न `\n`।

URL बनाने, header बनाने, client option चुनने या ऐसे log में लिखने से पहले यह नियम लागू करें जो बाद में request retry को feed करता हो। Literal control characters पकड़ने के लिए original string जांचें। Input contract के अनुसार एक बार decode करें, फिर decoded string फिर जांचें। जब तक कुछ न बदले, तब तक बार-बार decode न करें। बार-बार decoding साफ contract को अनुमान में बदल देती है और हैरान करने वाले नतीजे दे सकती है।

छोटे validation function का सही रूप वह है जो input सुधारने से मना करे:

```text
validateRequestText(field, value):
  if value contains "\r" or "\n":
    fail(field + " contains a line break")
  return value
```

असल code में भाषा की वास्तविक string value को validate करें, उसके printed representation को नहीं। JSON payload में `"\n"` हो सकता है, जो JSON parsing के बाद एक newline character बन जाता है। दिखाई देने वाले backslash और n, इन दो characters को जांचने से खतरनाक value छूट जाती है।

`trim()` न चलाएं और आगे न बढ़ें। अधिकतर implementations में trimming केवल किनारों को हटाती है, इसलिए बीच का line break बचा रहता है। Line break को spaces से बदलना दूसरे तरीके से और खराब है: इससे मांगी गई कार्रवाई बदल जाती है और operator के पास इस बात का स्पष्ट record नहीं रहता कि एजेंट ने निषिद्ध syntax मांगा था। Input को invalid मानें और network I/O से पहले रुक जाएं।

Rejection response में field की पहचान होनी चाहिए, पर sensitive content नहीं दोहराना चाहिए। `custom header value contains a line break` caller को पर्याप्त बताता है। पूरी value echo करने से credentials या private data terminal transcript में आ सकते हैं।

## Header name के लिए value से संकरा contract चाहिए

Header value में वैध रूप से spaces और कई visible characters हो सकते हैं। Header name में नहीं होने चाहिए। एजेंट को मनमाने नाम चुनने देना, अधिकांश action gateway की जरूरत से अधिक अस्पष्टता पैदा करता है।

Name के लिए रूढ़िवादी नियम अपनाएं: केवल ASCII letters, digits और hyphen, साथ में उचित length limit। Colon, whitespace, control characters और non-ASCII bytes अस्वीकार करें, जब तक किसी खास extension को support करने का documented कारण न हो। उद्देश्य internet के हर permissive parser की नकल करना नहीं है। उद्देश्य एक स्पष्ट request बनाना है।

ऊपरी तौर पर सामान्य दिखने वाली value के बावजूद यह input अस्वीकार होना चाहिए:

```json
{
  "name": "X-Trace\r\nAuthorization",
  "value": "debug"
}
```

यह भी अस्वीकार होना चाहिए:

```json
{
  "name": "X-Trace: injected",
  "value": "debug"
}
```

Colon renderer का हिस्सा है, दिए गए name का नहीं। यदि gateway इसे स्वीकार करे, तो एक parser उसे name और दूसरा पूरा field line मान सकता है। Leading whitespace वाला header name भी इसी तरह के मतभेद को न्योता देता है।

CRLF के अलावा header value के लिए अपनी policy बनाएं। तय करें कि gateway repeated headers स्वीकार करता है या नहीं, commas की अनुमति है या नहीं, और एजेंट किन headers को override कर सकते हैं। Duplicate names को text जोड़कर merge न करें, जब तक उस header के semantics इसकी साफ अनुमति न दें। `Set-Cookie`, `Content-Length`, `Host`, `Authorization` और forwarding headers को खास handling या पक्की रोक चाहिए। मुझे वे gateway बेहतर लगते हैं जो protocol और credential headers को पूरी तरह reserve रखते हैं, फिर एजेंट के लिए application headers का छोटा set देते हैं।

कभी-कभी इसका विरोध होता है क्योंकि arbitrary custom headers से generic HTTP tool बहुत flexible लगता है। Flexibility लोकप्रिय है क्योंकि API को एक और header चाहिए तो tool option जोड़ने की जरूरत नहीं पड़ती। फिर भी credential वाले agent traffic के लिए यह गलत default है। `idempotency_key` या `request_id` जैसे named option का format और owner साफ होता है। Unrestricted header bag में दोनों नहीं होते।

## Destination एक बार parse करें और उसके हिस्सों को structured रखें

Gateway द्वारा स्वीकार किए जाने के बाद destination कोई string नहीं रहता। वह scheme, host, port, path, query और कभी-कभी user info या fragment वाला structured value है। इसे standards-aware URL parser से एक बार parse करें, निषिद्ध components अस्वीकार करें, फिर template string में वापस बदले बिना parsed value को HTTP client को दें।

सटीक allowlist कार्रवाई पर निर्भर करती है। किसी एक vendor API को call करने वाला tool HTTPS, एक hostname और port 443 मांग सकता है। व्यापक tool कई preconfigured origins स्वीकार कर सकता है। हर स्थिति में embedded credentials, fragments, malformed hostname और decoded carriage return या newline को request बनने से पहले अस्वीकार करें। Fragments wire पर नहीं जाते, पर उन्हें स्वीकार करने से logs और approval screens में बेवजह उलझन बढ़ती है।

उपयोगी request boundary ऐसी दिखती है:

```text
input destination
  -> decode once
  -> reject CR and LF
  -> parse URL
  -> require HTTPS
  -> require allowed host and allowed port
  -> reject user info and fragment
  -> pass parsed URL to HTTP client
```

क्रम जानबूझकर ऐसा है। Raw string पर लगी host allowlist को `https://allowed.example@other.example/` जैसे user info से धोखा दिया जा सकता है। केवल library द्वारा malformed value normalize करने के बाद लगी line-break जांच, पहले parser द्वारा स्वीकार की गई चीज को छोड़ सकती है। अर्थ के लिए पहले parse करें, लेकिन किसी component के उन्हें syntax की तरह render करने से पहले निषिद्ध control characters अस्वीकार करें।

`scheme + "://" + host + path` को जोड़कर HTTP request न बनाएं। इस pattern में आप उस समय structured value को फिर text बना देते हैं जब आपको parser की गारंटी सबसे अधिक चाहिए। यदि library को अलग host और path arguments चाहिए, तो हर argument को उसी control-character नियम के तहत validate करें और library का structured API इस्तेमाल करें।

## जब layers में मतभेद हो तो encoding bypass बनती है

Literal CRLF वह test है जो सबको याद रहता है। Encoded CRLF वह test है जो असली बग ढूंढता है।

एजेंट URL component में `%0d%0a` भेज सकता है। अगर gateway raw string जांचता है और बाद में custom renderer इस्तेमाल करने से पहले URL-decode करता है, तो जांच ने गलत representation की रक्षा की है। Validation के बाद line break दिखाई देता है। Double encoding, जैसे `%250d%250a`, तब महत्वपूर्ण हो जाती है जब अलग layers अलग से decode करती हों।

एक स्पष्ट normalization boundary चुनें। उदाहरण के लिए, JSON को एक बार parse करें, केवल वहीं percent-decode करें जहां URL standard मांगता है, उस representation को validate करें जिसे renderer इस्तेमाल करेगा, और बाद में generic decoding रोक दें। हर transition के लिए test रखें। यह बड़े sanitizer जितना आकर्षक नहीं, पर reviewers के पास समझने योग्य चीज रहती है।

Test table में हर agent-controlled field के लिए कम-से-कम ये input होने चाहिए:

- literal `\r`, literal `\n`, और इनका जुड़ा हुआ CRLF रूप
- जहां percent encoding स्वीकार है, वहां `%0d`, `%0a` और `%0d%0a`
- अगर दूसरा component बाद में decode कर सकता है, तो `%250d%250a`
- colon या whitespace वाला header name
- user info, अनपेक्षित port या unapproved host वाला destination

केवल error message का test न करें। पुष्टि करें कि अस्वीकार किए गए input पर client transport कभी चलता ही नहीं। ऐसा validator जो request बनाने या queue करने के बाद error बताता है, उस सुरक्षा गुण में पहले ही विफल हो चुका है जिसकी आपको परवाह है।

Configuration formats में भी ऐसी ही समस्या है। Environment variables, YAML, JSON और shell arguments, सभी के अपने escaping rules हैं। Test fixture में दिखने वाला `\n` दो बेअसर characters हो सकता है, जबकि production JSON decoding उसे newline में बदल देती है। Tests को उसी parsing path से बनाएं जो असली agent calls स्वीकार करता है।

## खराबी आम तौर पर convenience code में छिपती है

खतरनाक code अक्सर शुरुआती security review के बाद आता है। कोई नई API के लिए custom headers जोड़ता है, debug proxy को configurable बनाता है या logging के लिए request फिर से बनाने वाला retry function लिखता है। हर बदलाव अपने-आप में ठीक लगता है। मिलकर वे original gateway के सुरक्षित client APIs के बाद फिर string rendering ला सकते हैं।

मैंने इसे input से socket तक एक action trace करके पाया है, केवल `header` शब्द खोजकर नहीं। `Host:`, `Authorization:`, `Cookie:`, `Content-Length:`, request target, proxy commands, raw HTTP test helpers और log replay utilities के आसपास string interpolation ढूंढें। Decode functions भी खोजें। Entry point के पास सुरक्षित validator का कोई लाभ नहीं अगर बाद की path कोई दूसरी representation स्वीकार करती है।

एक प्रतिनिधि विफलता के चार हिस्से हैं। Gateway allowlist के मुकाबले destination host validate करता है। वह destination का बाकी हिस्सा low-level client के लिए text रूप में रखता है। Retry helper logs को पढ़ने में आसान बनाने के लिए उस text को percent-decode करता है। फिर helper retry के लिए raw request line लिखता है और `%0d%0aX-Test:%20yes` message syntax बन चुका होता है।

इनमें से कोई भी line «request injection की अनुमति दें» नहीं कहती। दोष वह गायब invariant है: जब code HTTP message render करे, तो अविश्वसनीय content में कभी line-break characters नहीं होने चाहिए। इस invariant को एक shared validator में रखें और raw rendering को ऐसा exception बनाएं जिसे tests में उचित ठहराना पड़े।

यदि आप higher-level HTTP library इस्तेमाल करते हैं, तो अंत तक उसी स्तर पर रहें। किसी अजीब endpoint को support करने के लिए raw socket पर वापस न जाएं। अगर किसी API को सच में असामान्य wire format चाहिए, तो उसके integration को अलग implementation और संकरा input model दें। उसके लिए generic path को कमजोर न करें।

## Logs में अस्वीकृत इरादा दिखे, secret नहीं

जब validation request अस्वीकार करे, तो जांच के लिए पर्याप्त context दर्ज करें: समय, agent session या process identity, action name, field category, rejection reason और दी गई value का सुरक्षित digest या length। पूरे header value को default रूप से न रखें। Headers में अक्सर token, personal data या signed material होते हैं, जो logs में copy होने के बाद secret management की अलग समस्या बन जाते हैं।

प्रयास की गई कार्रवाई के audit event को चली हुई कार्रवाई के event से अलग रखें। अस्वीकार request failed network call जैसा नहीं दिखना चाहिए। वह network तक पहुंचा ही नहीं। Incident response में यह फर्क मदद करता है और operators को ऐसी remote service के पीछे समय गंवाने से बचाता है जिसने request कभी देखा ही नहीं।

Sallyport एजेंट runs के लिए Sessions journal और calls के लिए Activity journal रखता है। दोनों उसके encrypted hash-chained audit log से project होते हैं। इससे gateway को अस्वीकृत action दिखाने की उचित जगह मिलती है, लेकिन input rule फिर भी HTTP rendering से पहले होना चाहिए।

यहां offline verification property महत्वपूर्ण है। `sp audit verify` vault key के बिना ciphertext पर chain verify कर सकता है, जिससे पता चल सकता है कि दर्ज events बदले नहीं गए हैं। इससे यह नहीं पता चलता कि validator जरूरत से ज्यादा permissive था या नहीं। Event schema और tests में अस्वीकृत field category दिखनी चाहिए, खतरनाक data को बचाकर नहीं रखना चाहिए।

## केवल validator नहीं, serialized request को test करें

`contains('\r') || contains('\n')` के unit tests जरूरी हैं, लेकिन पर्याप्त नहीं। वे साबित करते हैं कि एक function दो characters को अस्वीकार करता है। वे नहीं बताते कि actual transport decoding, defaults, retries या custom adapter से आया line break स्वीकार नहीं कर सकता।

ऐसा local test server या recording transport इस्तेमाल करें जो बाहर जाने वाले request object को और, जहां आपका stack अनुमति दे, उसके serialized bytes को capture करे। Approved input को उसी tool entry point से भेजें जिसका एजेंट इस्तेमाल करते हैं। Method, authority, path और पूरा header set assert करें। फिर rejected cases दें और assert करें कि recording transport ने zero requests देखे।

लंबे और अस्पष्ट security tests के बजाय छोटी test matrix अधिक regressions पकड़ती है:

```text
field                 input                         expected result
custom header value   "ok\r\nX-Added: yes"          reject before transport
custom header name    "X-Mode: injected"            reject before transport
destination path      "/v1/a%0d%0aX-Added:%20yes"   reject after decoding
allowed destination   "https://api.example/v1/a"    one request, expected host
```

अगर भाषा में व्यावहारिक हो तो property tests जोड़ें। हर स्वीकार किए गए character class के आसपास control characters generate करें और फिर rejection assert करें। Fuzzing तभी उपयोगी है जब contract स्पष्ट हो। Fuzzer अजीब parser cases खोज सकता है, लेकिन वह यह नहीं बता सकता कि arbitrary header names product decision थे या गलती।

जब test विफल हो, validator बदलने से पहले अंतिम bytes देखें। मैंने टीमों को case pass होने तक और replacements जोड़ते देखा है, फिर पता चला कि client ने whitespace fold किया था या दो बार decode किया था। सही सुधार आम तौर पर उस formatter या decoder को हटाना है जिसने raw syntax संभव बनाया।

## इंसानी मंजूरी malformed action को सुरक्षित नहीं बनाती

Per-session approval यह जवाब देती है कि कोई खास agent process कार्रवाई कर सकता है या नहीं। Per-call approval यह जवाब देती है कि protected credential के इस इस्तेमाल के लिए इंसानी फैसला चाहिए या नहीं। इनमें से कोई भी ambiguous request को सुरक्षित नहीं बनाती। कोई व्यक्ति लंबे destination या header value में छिपे control characters भरोसे से नहीं देख सकता, और approval fatigue सूक्ष्म prompt को rubber stamp बना देगी।

Approval data को भी structured रखें। Normalized host, method, path, named header categories और कार्रवाई में credential जुड़ने का साफ संकेत दिखाएं। Approval card दिखने से पहले malformed input अस्वीकार करें। किसी व्यक्ति से invalid request मंजूर करवाना parser bug को user interface में पहुंचा देता है।

Sallyport का vault gate, session authorization और per-call key setting इंसानी नियंत्रण के अलग-अलग क्षण देते हैं। HTTP action boundary को फिर भी strict renderer चाहिए, क्योंकि approval decision एक सुव्यवस्थित action के लिए होना चाहिए, ऐसी string के लिए नहीं जिसका अर्थ click के बाद बदल जाए।

पहला व्यावहारिक बदलाव कोई बड़ा rewrite नहीं है। एजेंट के नियंत्रण वाला destination text या custom headers स्वीकार करने वाली हर path ढूंढें, rendering से पहले बिना शर्त CRLF rejection जोड़ें, और ऐसा transport test लिखें जो साबित करे कि rejected input कुछ नहीं भेजता। फिर HTTP को strings से फिर बनाने वाला convenience code हटाएं। यही वह जगह है जहां यह दोष बार-बार लौटता है।
