# क्या URL userinfo आपका API गंतव्य छिपा सकता है?

किसी भी URL को स्वीकार करने वाले एजेंट को ऐसे स्थान पर भेजा जा सकता है जहां उसका ऑपरेटर उसे नहीं भेजना चाहता था। URL userinfo इस गलती को आसान बनाता है, क्योंकि यह `@` के पहले परिचित hostname दिखाता है, जबकि असली गंतव्य उसके बाद होता है। अगर आपकी approval screen, allowlist या audit view पूरी string को host-जैसे label की तरह मानती है, तो अनुरोध मंजूर दिख सकता है लेकिन किसी दूसरे server पर जा सकता है।

Outbound API actions के लिए userinfo को अस्वीकार्य input मानें, जब तक उसे support करने की कोई सीमित और documented compatibility वजह न हो। Boundary पर URL को एक बार parse करें, request में credentials शामिल होने से पहले nonempty userinfo field को reject करें, और बाद के सभी निर्णय parsed fields से लें। यह कोई दुर्लभ URL चाल नहीं है। यह सामान्य syntax है, जो ऐसे review surface से मिलता है जिसमें लोगों को बहुत सारे punctuation जल्दी पढ़ने पड़ते हैं।

## Hostname अंतिम at sign के बाद होता है

Absolute HTTP URL में authority, `//` और अगले `/`, `?` या `#` के बीच होती है। RFC 3986 के अनुसार authority में वैकल्पिक userinfo, फिर `@`, फिर host, फिर वैकल्पिक port होता है। इसलिए host, `//` के बाद दिखने वाला पहला टेक्स्ट नहीं है।

इस अनुरोध को देखें:

```text
https://api.example.com@collector.invalid/v1/charges
```

शुरुआत को जल्दी पढ़ने वाला व्यक्ति `api.example.com` देखकर वहीं रुक सकता है। अनुरूप URL parser इसे इस तरह अलग करता है:

```text
scheme:   https
userinfo: api.example.com
host:     collector.invalid
port:     443
path:     /v1/charges
```

TCP connection और TLS hostname verification `collector.invalid` का इस्तेमाल करते हैं। `@` से पहले की string remote server की पहचान नहीं करती। यह userinfo है, URL grammar का पुराना हिस्सा, जिसका कभी नाम और password के लिए उपयोग होता था।

यही समस्या अधिक परिचित दिखने वाले रूप में भी आती है:

```text
https://billing.example.com:wrong@evil.invalid/invoices
```

`@` से पहले का सब कुछ userinfo है, जिसमें colon और उसके बाद का टेक्स्ट भी शामिल है। Remote host फिर भी `evil.invalid` है। Raw URL में पहले hostname-जैसे substring से reviewer सुरक्षित ढंग से गंतव्य नहीं समझ सकता।

Reviewers को केवल यह character पहचानना सिखाकर समस्या हल करने की कोशिश न करें। लोग incident review के दौरान, लंबे दिन के अंत में, और जब एजेंट बहुत सारे action requests देता है, ऐसी गलती करते हैं। जिस control का भरोसा पूरी तरह सही visual parsing पर हो, वह कमजोर control है।

Browsers और कई runtimes का इस्तेमाल किया जाने वाला URL Standard HTTPS जैसी special schemes के लिए व्यावहारिक रूप से यही परिणाम देता है: parsed username और password fields, hostname से अलग होते हैं। Malformed input और escaping के मामले में parser का सटीक व्यवहार अलग हो सकता है। इसी वजह से उसी runtime से parse करें जो request भेजेगा। एक library से validate न करें और दूसरी library से execute न करें, जो अलग strings स्वीकार करती हो।

नियम सरल रखें: केवल parsed hostname ही तय करे कि request कहां जा सकती है। Raw URL text सबूत है, अधिकार नहीं।

## Userinfo नेटवर्क समस्या से पहले review समस्या बनाता है

Network client को आमतौर पर पता होता है कि वह कहां जा रहा है। विफलता उससे पहले होती है, जब कोई व्यक्ति या control उस गंतव्य के गलत representation को मंजूरी देता है।

सामान्य agent flow में कई जगहों से raw text निकल सकता है: tool call argument, approval card, session journal, error message और notification। इनमें से कोई view अगर `Calling api.example.com` कहता है क्योंकि वह `@` से पहले का टेक्स्ट निकालता है, तो वह operator को गलत जानकारी देता है। अगर कोई दूसरा view पूरी string log करता है, लेकिन तय width के बाद उसे काट देता है, तो असली host पूरी तरह गायब हो सकता है।

यह तब भी महत्वपूर्ण है जब एजेंट के पास target के लिए कोई credential न हो। Outbound request में user data, signed request body, किसी व्यापक rule से चुना bearer token हो सकता है, या वह किसी ऐसे internal address तक पहुंच सकती है जहां एजेंट traffic कभी नहीं जाना चाहिए। Credential की कहानी पर ध्यान जाता है क्योंकि वह ठोस है। Destination integrity के लिए भी उतनी ही सावधानी चाहिए।

लोग अक्सर URL display safety और URL transport safety के अंतर को मिला देते हैं। HTML view में `@` escape करने से page कम भ्रमित कर सकता है, लेकिन यह तय नहीं करता कि HTTP client कहां connect करेगा। इसके उलट parser network call सही कर सकता है, फिर भी खराब approval view किसी व्यक्ति को गलत host मंजूर करने के लिए प्रेरित कर सकता है। सही transport decision और ईमानदार display, दोनों चाहिए।

Stored request में `@` को किसी साधारण character से बदलकर आगे न बढ़ें। इससे उस input को छिपा दिया जाता है जिसकी वजह से denial हुआ और बाद की जांच कठिन हो जाती है। मूल string को raw input के रूप में सुरक्षित रखें, request को rejected चिह्नित करें और embedded credentials को पढ़े जा सकने वाले journal में लिखे बिना parsed reason दर्ज करें।

Display की शुरुआत अलग destination field से करें, जैसे `Host: collector.invalid`, और उसके नीचे original URL रखें। यह क्रम punctuation वाली पहेली को सीधे कथन में बदल देता है। Reviewer के पास requested credential या intended integration से मिलाने के लिए एक स्थिर field भी रहता है।

## Userinfo को अस्वीकार करना उसे सुधारने से अधिक सुरक्षित है

Agent action gateway के लिए साफ default यह है कि हर उस outbound HTTP URL को reject किया जाए जिसका parsed username या password field nonempty हो। अधिकतर API integrations credentials को पहले ही request headers में भेजते हैं या credential injector का उपयोग करते हैं। URL userinfo को support करना सामान्य API जरूरत पूरी किए बिना attack surface बढ़ाता है।

Validation का क्रम महत्वपूर्ण है। पहले original string parse करें। Host allowlisting, approval prompts, redirects, DNS resolution या credential selection से पहले malformed URLs, unsupported schemes और userinfo को reject करें। यह क्रम ऐसे अनुरोध को eligible दिखाने से बचाता है जिसका कोई हिस्सा आप बाद में फेंक देंगे।

यह pseudocode नियम बताता है:

```text
u = parse_absolute_url(raw_url)

if u.scheme not in {"https", "http"}:
    deny("unsupported scheme")

if u.username != "" or u.password != "":
    deny("URL userinfo is not accepted")

host = normalize_hostname(u.hostname)
if host == "":
    deny("missing hostname")

if not destination_is_allowed(u.scheme, host, u.port):
    deny("destination is not allowed")

send(u)
```

Parser को structured fields लौटाने चाहिए। `@` पर split करना, prefix हटाना या hostname substring खोजना, सामान्य variations में भी विफल होता है। Raw input की authority में एक से अधिक `@` हो सकते हैं। Parser तय करता है कि कौन सा delimiter syntax का हिस्सा है और पहले के characters userinfo के हैं या नहीं। वह bracketed IPv6 literals, explicit ports, percent encoding और empty components को हाथ से लिखे string logic से अधिक एकरूपता से संभालता है।

Userinfo को reject करने से caller को साफ सुधार मिलता है: `https://api.example.com/path` इस्तेमाल करें, फिर तय credential path से HTTP authentication दें। इससे caller को यह अनुमान नहीं लगाना पड़ता कि आपने `https://name@host` को चुपचाप `https://host` में बदल दिया या नहीं।

एक compatibility case को स्वीकार करना उचित है। कुछ पुराने URLs में Basic authentication इस तरह होती है: `https://name:secret@host/path`। अगर migration में ऐसे URLs संभालने हों, तो उन्हें one-time import path में संभालें, credentials को protected storage में निकालें, parsed host की पुष्टि करें और policy अनुमति दे तो import record से source string मिटा दें। Runtime action API को इन्हें हमेशा स्वीकार न करने दें। अस्थायी compatibility code अक्सर स्थायी attack surface बन जाता है।

## Host allowlists को parsed labels चाहिए, अच्छे दिखने वाले strings नहीं

Destination allowlist को normalized parsed hostnames की तुलना करनी चाहिए, raw URL पर substring search नहीं चलानी चाहिए। नियम `raw_url.includes("api.example.com")` `https://api.example.com@evil.invalid` और `https://api.example.com.evil.invalid`, दोनों स्वीकार कर लेता है। इनमें से कोई भी `api.example.com` का अनुरोध नहीं है।

Exact host matching सबसे सहज नियम है। अगर integration को केवल `api.example.com` चाहिए, तो उसी नाम की अनुमति दें और बाकी हर hostname को reject करें। अगर उसे सच में subdomains चाहिए, तो DNS labels की तुलना करें: `example.com` और `.example.com` पर खत्म होने वाले names को अनुमति दें, लेकिन `badexample.com` और `example.com.evil.invalid` को reject करें।

स्पष्ट implementation इस तरह दिख सकता है:

```text
function allowedHost(host, root) {
  const h = host.toLowerCase().replace(/\.$/, "")
  const r = root.toLowerCase().replace(/\.$/, "")
  return h === r || h.endsWith("." + r)
}
```

यह code मानता है कि URL parser पहले ही hostname दे चुका है और caller userinfo reject कर चुका है। इसे पूरा URL नहीं मिलना चाहिए। जिम्मेदारियां अलग रखने से बाद का caller port, username या `@` sign वाली authority string पास नहीं कर पाएगा।

Internationalized domains के लिए भी निर्णय चाहिए। Browsers अक्सर IDNA processing से hostnames को ASCII में serialize करते हैं, जबकि user को Unicode text दिख सकता है। तुलना के लिए वही canonical form इस्तेमाल करें जो आपका request client करता है। जहां दोनों अलग हों, वहां canonical host और पढ़ने योग्य form, दोनों दिखाएं। Proportional font में दो strings समान दिखती हैं, इसलिए उन्हें एक ही domain न मान लें।

IP addresses के लिए अलग नियम बनाएं। Hostname allowlist से IP literal अपने-आप सुरक्षित नहीं हो जाता और approval के बाद DNS resolution से hostname जिस address तक पहुंचता है, वह बदल सकता है। अगर आपके threat model में local services तक पहुंच शामिल है, तो साफ तय करें कि private, loopback, link-local और IPv6 local addresses की अनुमति है या नहीं। Userinfo reject करना जरूरी है, लेकिन केवल उससे server-side request forgery हल नहीं होती।

Ports का भी अर्थ होता है। `https://api.example.com:8443` मान्य partner endpoint हो सकता है या अनपेक्षित administration service। Effective port दर्ज करें और जहां integration किसी एक port तक सीमित है, उसे approval decisions में शामिल करें। केवल host label पूरे network destination का वर्णन नहीं करता।

## Redirects को उसी gate से गुजरना चाहिए

अनुमति प्राप्त शुरुआती URL से हर redirect target को अनुमति नहीं मिल जाती। HTTP redirects remote server के दिए नए destination instructions हैं और agent gateway को हर एक को follow करने से पहले parse और authorize करना चाहिए।

मान लें कि एजेंट `https://api.example.com/export` मांगता है। वह host इस location के साथ 302 response लौटाता है:

```text
https://api.example.com@receiver.invalid/download?id=42
```

जो client redirects को अपने-आप follow करता है, वह अगला connection `receiver.invalid` से करेगा। अगर gateway ने केवल पहला URL मंजूर किया था, तो उसकी allowlist और approval screen अब हुई network action का वर्णन नहीं करतीं।

Redirects को client द्वारा चुनी गई limit वाले loop की तरह संभालें। हर location value के लिए current approved URL के सापेक्ष relative reference resolve करें, बनी absolute URL को parse करें, उसी scheme, userinfo, host, port और address rules को लागू करें, फिर आगे बढ़ने का निर्णय लें। Source response और parsed redirect destination दोनों दर्ज करें।

Default रूप से credentials को अलग hosts पर forward न करें। HTTP client libraries में अंतर होता है कि cross-host redirect के बाद वे `Authorization` header बनाए रखती हैं या नहीं, और custom headers का व्यवहार फिर अलग हो सकता है। सबसे सुरक्षित gateway behavior किसी credential को किसी खास approved destination से जोड़ता है और redirect destination के authorization के बाद ही नया outbound request बनाता है। एक vendor-owned host से दूसरे पर redirect अपेक्षित हो सकता है, लेकिन वह library defaults का संयोग नहीं, स्पष्ट rule होना चाहिए।

Method handling पर भी ध्यान दें। 303 response अक्सर आगे का request GET में बदल देता है, जबकि 307 और 308 method और body को बनाए रखते हैं। अगर एजेंट sensitive body post करता है, तो preserved-method redirect उसे कहीं और भेज सकता है। हर hop पर इस्तेमाल हुआ method दर्ज करें और action result में final destination दिखाएं।

Client को redirects follow न करने के लिए भी configure किया जा सकता है। सीमित API tools के लिए यह समझदारी भरा विकल्प है। Redirect response एजेंट को लौटाएं और उससे अगला destination स्पष्ट रूप से मांगने को कहें। इससे एक और approval event बनता है, लेकिन operator को host change पर विचार करने का साफ अवसर मिलता है। व्यापक HTTP support के लिए automatic redirects तभी स्वीकार्य हैं, जब हर hop उसी gate से गुजरे।

## Destination validation के बाद credentials चुनें

खतरनाक क्रम समझना आसान है: raw URL में परिचित service name देखकर credential चुनें, फिर URL parse करें और request भेज दें। `@` वाली चाल में परिचित text userinfo बन सकता है और चुना गया secret attacker-controlled host तक जा सकता है।

सुरक्षित क्रम भी उतना ही स्पष्ट है। पहले URL parse और validate करें। फिर उसकी scheme, host, port और redirect state को authorize करें। उसके बाद ही उस approved destination से बंधा credential खोजें और outgoing request में जोड़ें। Secret को tool arguments, agent memory और return values से बाहर रखें।

इससे कम नाटकीय लेकिन आम configuration error भी हल होता है। `api.example.com` से जुड़ा credential अपने-आप `uploads.example.com` को नहीं जाना चाहिए, भले ही दोनों नाम एक ही parent domain के तहत हों। अलग hosts के ownership, TLS termination, logging या permission scopes अलग होते हैं। Exact host binding से शुरू करें। Match को केवल तब व्यापक बनाएं, जब integration बता सके कि इसकी जरूरत क्यों है।

URL userinfo की तुलना में header injection बेहतर है, क्योंकि वह destination को authentication से अलग रखता है। Request record कह सकता है कि authorization header inject हुआ था, बिना उसका value store किए। एजेंट को काम जारी रखने के लिए जरूरी response मिलता है, दोबारा इस्तेमाल होने वाला secret नहीं।

Sallyport API और SSH credentials को अपने encrypted vault में रखकर और उन्हें एजेंट के सामने लाए बिना outbound action चलाकर इस separation को अपनाता है। इस model वाले किसी भी gateway में credential lookup से पहले userinfo reject करना operator के intended destination और request पाने वाले host के बीच की कमी को बंद करता है।

गंतव्य पहचानने के लिए एजेंट के दिए request header पर भी भरोसा न करें। `Host` header, HTTP/2 authority, URL host, proxy configuration और TLS server name client-specific तरीकों से मिलकर काम कर सकते हैं। Gateway को connection settings का मालिक होना चाहिए और उन्हें validated parsed URL से बनाना चाहिए। अगर custom headers की अनुमति दें, तो उन्हें request content मानें, routing बदलने की अनुमति नहीं।

## Approval cards में parsed destination पहले दिखाएं

Approval card को URL को फिर से बनाने के लिए पाठक को मजबूर किए बिना तीन ठोस सवालों का जवाब देना चाहिए: किस process ने मांगा, कौन सी operation चलेगी और उसे कौन सा host मिलेगा। Parsed hostname और port को अलग destination line में रखें। HTTP method और path को पास में दिखाएं। Original URL को supporting evidence की तरह दिखाएं, अकेले destination signal की तरह नहीं।

पहले इस्तेमाल किए गए खराब दिखने वाले request के लिए उपयोगी card ऐसा होगा:

```text
Process: signed agent process
Action:  POST /v1/charges
Host:    collector.invalid:443
Result:  blocked because URL userinfo is present
Input:   https://api.example.com@collector.invalid/v1/charges
```

Authority के बाएं हिस्से से निकले `api.example.com` को badge के रूप में न दिखाएं। केवल `External HTTP request` भी न कहें, क्योंकि इससे व्यक्ति के पास निर्णय लेने का कोई सार्थक आधार नहीं होता।

Per-session approval और per-call approval अलग मानवीय समस्याओं का समाधान करते हैं। Session approval कहती है कि कोई खास running process अपने lifetime में gateway इस्तेमाल कर सकता है। Per-call approval कहती है कि खास तौर पर संवेदनशील credential या action के लिए नया मानवीय निर्णय चाहिए। इनमें से कोई भी basic URL validation का विकल्प नहीं है। जिस process पर आप भरोसा करते हैं, उसे भी किसी untrusted issue comment, package metadata field या generated configuration file से manipulate किया जा सकता है।

Sallyport की decision ladder में locked vault पूरी तरह रोकता है, फिर session authorization और वैकल्पिक per-secret confirmation आते हैं। यह ढांचा तब सबसे अच्छा काम करता है जब malformed destinations approval card तक पहुंचने से पहले विफल हो जाएं, क्योंकि operator को यह तय नहीं करना चाहिए कि URL punctuation के किसी हिस्से ने endpoint बदल दिया है या नहीं।

Denial messages स्पष्ट रखें। `Userinfo is not allowed in outbound URLs` एजेंट developer को बताता है कि क्या ठीक करना है। `Invalid request` retries, ad hoc escaping और validation कमजोर करने का दबाव पैदा करता है। अगर parser ने password निकाला हो तो उसे दोहराने से बचें। Message निषिद्ध component का नाम ले सकता है, उसका content नहीं।

## Tests में केवल अच्छे URLs नहीं, भ्रामक inputs भी हों

Validation test suite में ऐसे examples चाहिए जो human readers और सरल string checks को धोखा देने के लिए बनाए गए हों। `https://api.example.com/v1` का pass हो जाना उस boundary के बारे में लगभग कुछ साबित नहीं करता, जहां एजेंट कोई भी text दे सकता है।

इन जैसे cases से शुरुआत करें और parsed host, decision तथा reason assert करें:

```text
ALLOW  https://api.example.com/v1                 host=api.example.com
DENY   https://api.example.com@evil.invalid/v1    reason=userinfo
DENY   https://name:secret@api.example.com/v1     reason=userinfo
DENY   https://api.example.com.evil.invalid/v1    reason=host
DENY   https://api.example.com:444/v1             reason=port
DENY   https://[::1]/v1                           reason=address
```

एक redirect fixture जोड़ें। Allowed test server को ऐसा redirect देने दें जिसकी location में userinfo हो, और assert करें कि client blocked second hop दर्ज करता है लेकिन destination server को request नहीं भेजता। इससे वह आम गलती पकड़ी जाती है जिसमें initial URL validation एक code path में और redirect handling library callback के भीतर रहती है।

Percent encoding को जानबूझकर test करें, लेकिन यह न मानें कि हर encoded `@` एक जैसा है। कई parsers में path का `%40`, path data ही रहता है, जबकि authority में वास्तविक `@` delimiter का काम करता है। Production में इस्तेमाल होने वाले parser को ठीक वही raw string दें और उसके fields assert करें। महत्वपूर्ण invariant अपनी बनाई decoding rule नहीं है। वह यह है कि nonempty parsed userinfo field sender तक नहीं पहुंच सकती।

Log और approval rendering को भी test करें। Security control सही denial कर सकता है, फिर भी operational record खराब हो सकता है अगर display असली host को काट दे या parsed password text दिखा दे। Destination lines के लिए snapshot tests उपयोगी हैं, क्योंकि visual regressions अक्सर निरापद दिखने वाले design changes से आते हैं।

अंत में denied call के आसपास audit chain और revoke path test करें। Denial जांच के लिए पर्याप्त दिखाई देनी चाहिए, लेकिन उसमें injected secrets कभी नहीं होने चाहिए। उपयोगी record में उचित access controls के तहत raw request, parsed scheme और host, decision reason, calling process identity और यह तथ्य होता है कि कोई outbound action नहीं हुई।

## URL syntax कोई policy language नहीं है

कुछ teams URL edge cases के जवाब में exceptions का बढ़ता ढेर लगाती हैं: इस vendor के लिए username दें, उस environment के लिए special port स्वीकारें, header सही दिखे तो ही redirect पर भरोसा करें और हर incident में string matcher patch करें। यह तरीका लचीला लगता है, क्योंकि अजीब request को मना नहीं करना पड़ता। इसके साथ ऐसे rules बनते हैं जिनकी भरोसेमंद review कोई नहीं कर सकता।

नियम छोटा रखें। Outbound requests का parsed destination होता है। Userinfo अस्वीकार होता है। Allowed scheme, host, port और address class स्पष्ट हैं। Redirects फिर उन्हीं checks में आते हैं। Destination pass होने के बाद ही credentials चुने जाते हैं। हर निर्णय एक ऐसा record बनाता है जो parsed host को साफ बताता है।

इस नियम से कुछ पुराने URLs reject होंगे जिन्हें browser स्वीकार कर सकता है। यह ठीक है। Autonomous agent को browser address bar की हर ऐतिहासिक सुविधा नहीं चाहिए। उसे ऐसा सीमित interface चाहिए जिसमें action, destination और credential का संबंध भ्रमित करना कठिन हो।

अगर आपको वह interface बदलना हो, तो exception को tests और expiration decision के साथ नामित capability बनाएं। उसे URL cleanup code में न छिपाएं। पहला hostile input ऐसे text के अंतर को खोज लेगा जो host जैसा दिखता है और उस host के बीच जिसे आपका client वास्तव में संपर्क करता है।
