# DNS rebinding सुरक्षा के लिए execution-time lookup जरूरी है

`api.example.test` के लिए मंजूरी का मतलब यह नहीं कि बाद में यह नाम जिस भी पते पर लौटे, उसकी भी मंजूरी है। अगर कोई एजेंट प्रमाणित HTTP अनुरोध कर सकता है, तो DNS rebinding पुराने hostname निर्णय को स्थानीय सेवा, cloud metadata endpoint या आंतरिक control plane तक पहुंचने वाले रास्ते में बदल सकता है। Credential injection बिल्कुल तय तरीके से काम कर सकता है। गंतव्य चुनने में गलती हुई है।

मैंने टीमों को बहुत सावधानी से approval screen बनाते देखा है, फिर HTTP library को बिना उत्तर जांचे आखिरी संभव क्षण पर नाम resolve करने दिया है। यह कमी नजरअंदाज करना आसान है, क्योंकि सामान्य DNS शालीनता से काम करता है। हमलावर को सामान्य DNS का साथ नहीं चाहिए। उसे अपने नियंत्रण वाला नाम, कम cache lifetime और hostname को स्थायी गंतव्य समझने वाला क्लाइंट चाहिए।

DNS rebinding से बचाव के लिए execution-time lookup जरूरी है। कनेक्शन खोलने से ठीक पहले resolve करें, पूरा परिणाम जांचें, अनुमत पता चुनें और उसी पते से कनेक्ट करें। HTTP और TLS validation के लिए मूल hostname बनाए रखें। जब भी क्लाइंट नया कनेक्शन खोले, redirect follow करे या protocol बदले, यह काम फिर से करें।

## मंजूरी गंतव्य के प्रकार के लिए होती है, हमेशा के लिए IP पते के लिए नहीं

Hostname resolve करने का निर्देश है, network peer की स्थायी पहचान नहीं। यह अंतर मामूली लग सकता है, जब तक एजेंट को किसी issue, build log, tool response या दूसरी service से URL न मिले। एजेंट `reports.partner.test` को कॉल करने की मंजूरी मांग सकता है और मंजूरी देने वाले को एक विश्वसनीय सार्वजनिक गंतव्य दिखता है। कुछ मिनट बाद वही नाम `127.0.0.1`, IPv6 loopback address या केवल मशीन के नेटवर्क के भीतर पहुंचने वाला पता लौटा सकता है।

सामान्य rebinding क्रम में दो चरण होते हैं। पहले चरण में authoritative DNS server सार्वजनिक पता लौटाता है। क्लाइंट या reviewer hostname स्वीकार कर लेता है, कभी browser जैसी preflight request के बाद। दूसरे चरण में हमलावर उत्तर बदल देता है और expiry, reconnect, redirect, retry या दूसरे अनुरोध से एक और lookup होने पर निर्भर रहता है। Target service को स्थानीय मशीन से अनुरोध दिखता है, जहां network firewall अक्सर public internet की तुलना में ज्यादा पहुंच देते हैं।

Agent gateway में यह समस्या और तीखी होती है। किसी व्यक्ति की मंजूरी के बाद वह bearer token, basic credentials या custom authorization header जोड़ सकता है। अगर gateway एजेंट process की जांच करे, लेकिन peer address की नहीं, तो वह असली credential गलत जगह पहुंचा सकता है। स्थानीय administrative service को उस credential की जरूरत भी नहीं होगी। हमलावर अनुरोध का उपयोग केवल मशीन से ऐसा response पढ़वाने के लिए कर सकता है जो फिर एजेंट को मिल जाए।

पहले resolve किए गए IP पते को हमेशा के लिए दर्ज करके समस्या हल न करें। Public services load balancing, address changes और IPv6 इस्तेमाल करती हैं। सुरक्षित दावा सीमित है: हर connection को उस पते का उपयोग करना चाहिए जो connection शुरू होने के समय validation से गुजरा हो। पिछली मंजूरी hostname और credential use को अनुमति दे सकती है। वह बाद में blocked network से जुड़े पते को स्वीकार्य नहीं बना सकती।

## Socket खुलने से ठीक पहले resolve करें

Operations का क्रम तय करता है कि जांच का कोई अर्थ है या नहीं। Hostname resolve करें, resolver का उत्तर validate करें, एक पता चुनें और वही पता सीधे connection routine को दें। अगर आप एक पते को validate करके convenience API को कॉल करते हैं, जो hostname फिर से resolve करती है, तो आपने एक गंतव्य जांचा और दूसरे से जुड़ गए।

पूरे अनुरोध में ये मान अलग रखें:

- `origin_host` वह hostname है जिसे उपयोगकर्ता ने मंजूरी दी है और जिसे certificate को cover करना चाहिए।
- `peer_ip` इस socket के लिए चुना गया एकमात्र validated address है।
- `port` वह requested port है जिस पर क्लाइंट ने allowed-port rules लागू कर दिए हैं।
- `scheme` तय करता है कि क्लाइंट को TLS चाहिए या नहीं।

HTTPS के लिए TCP socket को `peer_ip` से जोड़ें, लेकिन HTTP Host header में `origin_host` भेजें और SNI तथा certificate verification के लिए `origin_host` इस्तेमाल करें। IP address के लिए certificate उसका विकल्प नहीं है। अगर certificate hostname से मेल न खाए, तो अनुरोध विफल करें। Address pinning आसान बनाने के लिए certificate validation बंद करना, routing की एक गलती की जगह उससे कहीं बड़ी गलती ले आता है।

कनेक्शन की न्यूनतम सीमा ऐसी दिख सकती है:

```text
origin_host = parse_url(request_url).host
answers = resolver.lookup_all(origin_host)
allowed = [ip for ip in answers if public_routable(ip)]
if allowed is empty:
    fail("DNS answer contains no permitted address")

peer_ip = choose_one(allowed)
socket = tcp_connect(peer_ip, request_port)
tls = tls_handshake(socket, server_name=origin_host, verify_name=origin_host)
send_http(tls, host_header=origin_host)
```

इस रूपरेखा में जानबूझकर `lookup_all` लिखा है। केवल एक address lookup आम समस्या छिपा देता है: resolver अनुमत public IPv4 address के साथ IPv6 loopback address भी लौटाता है। Library IPv4 जांचे जाने पर भी IPv6 को प्राथमिकता दे सकती है। या तो किसी भी blocked उत्तर पर hostname अस्वीकार करें, या ऐसा सख्त selection rule तय करें जो connector तक केवल validated addresses पहुंचाए। Mixed answers को अस्वीकार करना audit के लिए आसान है और हमलावर को connection-racing behavior का फायदा उठाने की कम गुंजाइश देता है।

«तुरंत» शब्द महत्वपूर्ण है। Socket खुलने से पहले resolve करें, उस समय नहीं जब एजेंट अनुरोध का मसौदा बनाता है, मंजूरी कार्ड दिखता है या उपयोगकर्ता credential सहेजता है। Lookup और connect के बीच छोटा अंतर रहता है, लेकिन क्लाइंट पहले ही पता चुन चुका होता है और उस socket के लिए DNS से फिर पूछने की जरूरत नहीं होती।

## Private IPv4 filtering केवल पहली दीवार है

RFC 1918 IPv4 ranges को private internets के लिए reserve करता है। इसमें तीन परिचित blocks हैं: `10.0.0.0/8`, `172.16.0.0/12` और `192.168.0.0/16`। Internet-facing client के लिए इन्हें block करना जरूरी है, लेकिन इस सूची को पूरा बचाव मानना आम और महंगी गलती है।

Destination filter को उन address categories को अस्वीकार करना चाहिए जिन्हें सुरक्षित रूप से public नहीं माना जा सकता। कम से कम loopback, unspecified addresses, link-local addresses, multicast, private-use addresses और IPv6 unique local addresses अस्वीकार करें। IPv4-mapped IPv6 forms को normalize करने के बाद अस्वीकार करें, क्योंकि `::ffff:127.0.0.1` व्यवहार में फिर भी loopback है। Remote URLs के लिए IPv6 zone identifiers को invalid मानें। Textual spelling से सुरक्षा तय न करें, पते को binary form में parse करें और वहीं classify करें।

कुछ ranges के लिए अनजाने default के बजाय साफ product decision चाहिए। `100.64.0.0/10` में carrier-grade NAT addresses globally reachable नहीं होते। Documentation और benchmarking ranges सामान्य production destinations के रूप में नहीं दिखने चाहिए। कुछ cloud setups में IPv4 link-local range metadata services तक पहुंच सकती है। IPv6 link-local addresses को interface scope चाहिए और वे public hostname request से कभी नहीं आने चाहिए। बाहरी action gateway के लिए सबसे सुरक्षित default यह है कि केवल सामान्य global unicast addresses को अनुमति मिले, अलग internal deployment mode के लिए नामित exceptions हों।

String prefix check का उपयोग न करें। `127.1`, `127.0.0.1`, permissive parser द्वारा स्वीकार किए जाने वाले integer formats, IPv6 compression और mapped forms इसे अविश्वसनीय बनाते हैं। URL host को DNS name या IP literal की तरह parse करें। अगर वह IP literal है, तो सीधे classify करें। अगर वह DNS name है, तो उसे resolve करें और लौटे हर address को classify करें। Resolution से पहले malformed names अस्वीकार करें।

यहीं टीमें public DNS और public reachability को मिला देती हैं। Resolver ऐसा public address लौटा सकता है जो corporate proxy, VPN या split-horizon network path से route होता हो। DNS answer केवल एक input है, पूरे route का वादा नहीं। Address filtering सीधे rebinding को साफ तौर पर स्थानीय ranges में जाने से रोकती है। Network egress rules को उन access paths को भी रोकना होगा जिन तक host पहुंच सकता है लेकिन product को उनका उपयोग नहीं करना चाहिए।

## CNAMEs और mixed answers के लिए एक निर्णय लें

CNAME किसी नाम को सुरक्षित नहीं बनाता। वह अंतिम answer दूसरे नाम को सौंपता है, जो खुद बदल सकता है या mixed answer set लौटा सकता है। Resolver आमतौर पर application को A और AAAA records देने से पहले उस chain को follow करता है, इसलिए application को मिले अंतिम addresses पर निर्णय लेना चाहिए। अगर resolver API chain दिखाता है, तो diagnosis के लिए उसे log करें, लेकिन enforcement इस आधार पर न करें कि पहला नाम परिचित लगता था या नहीं।

एक मजबूत default सरल है: अगर requested hostname के किसी भी final answer की category blocked है, तो अनुरोध अस्वीकार करें। इससे ऐसी service अस्वीकार हो सकती है जो public address के साथ पहुंच से बाहर private address भी publish करती हो, लेकिन ऐसा configuration पहले ही clients के लिए अनिश्चित व्यवहार बनाता है। Credential भेजने वाले gateway को चुपचाप सुविधाजनक answer चुनने के बजाय service owner से DNS record ठीक करने को कहना चाहिए।

कुछ टीमें «किसी भी allowed answer का उपयोग करें» पसंद करती हैं। यह नियम इसलिए लोकप्रिय है कि ज्यादा integrations काम करती रहती हैं। इससे दोहराना मुश्किल behavior भी बनता है: resolver का एक क्रम काम करता है, दूसरा blocked जगह से जुड़ता है और Happy Eyeballs implementation अलग-अलग समय पर IPv6 और IPv4 attempts शुरू करता है। अगर आप यह रास्ता चुनते हैं, तो connection layer को केवल चुने हुए allowed addresses मिलने चाहिए और उसे कभी मूल hostname पर fallback नहीं करना चाहिए। Generic HTTP library अक्सर इसकी गारंटी नहीं देती।

अस्वीकृति समझाने के लिए पर्याप्त evidence दर्ज करें। Request log में मूल hostname, port, resolved address set, यदि कोई हो तो selected peer address, policy outcome और request correlation identifier शामिल होना चाहिए। Debugging आसान बनाने के लिए credentials या response bodies log न करें। DNS safety failures आमतौर पर केवल address list से स्पष्ट हो जाते हैं।

## Connection pools तभी सुरक्षित हैं जब वे peer को बनाए रखें

Reuse किया गया TLS connection नया DNS lookup नहीं करता और उसे इसकी जरूरत भी नहीं होनी चाहिए। Socket के खुलने पर उसका peer address चुना और validate किया जा चुका है। Normal HTTP origin rules और certificate checks की अनुमति हो तो क्लाइंट उसी origin के दूसरे अनुरोध के लिए वही exact connection दोबारा इस्तेमाल कर सकता है।

नया connection अलग है। Pools अक्सर idle timeout, transport failure, HTTP/2 stream limit या proxy state बदलने के बाद reconnect छिपा देते हैं। अगर pool operating system को hostname से फिर connect करने को कहता है, तो उसने rebinding window खोल दी है। Resolver और connector को pool के नीचे रखें, पहले request के पास नहीं।

Connection coalescing में सावधानी चाहिए। HTTP/2 एक TLS connection को एक से अधिक hostnames के लिए reuse कर सकता है, जब certificate उन्हें cover करे और peer उपयुक्त हो। सामान्य browser यह tradeoff स्वीकार कर सकता है। Action gateway को हर origin के लिए गंतव्य का निर्णय स्पष्ट रखना चाहिए। केवल certificate में दोनों names होने के कारण यह न मानें कि एक approved hostname के लिए खुला socket दूसरे के लिए credentialed request ले जा सकता है। Approval scope, HTTP authority और connection reuse अलग निर्णय हैं।

Retries का ढांचा भी यही है। Connection failure के बाद retry नया outbound attempt है। फिर से resolve करें, classify करें, पता चुनें और नया socket खोलें। जिस connection को अनुमति मिली थी, उससे ज्यादा समय के लिए allowed result cache न करें। Repeated work घटाने के लिए blocked results थोड़ी देर cache कर सकते हैं, लेकिन उस cache को बिना समीक्षा वाला routing authority न बनने दें।

Call के बीच बदलाव हमलावर को स्थापित TCP connection को नए IP पर ले जाने नहीं देते। TCP पहले ही अपना peer चुन चुका है। खतरा उस code path में है जो replacement connection बनाता है, redirect follow करता है, protocol upgrade करता है या पहले response के बाद दूसरा request भेजता है। केवल एक successful request test करने से वे paths छूट जाते हैं जिन्हें production clients load में इस्तेमाल करते हैं।

## Redirect अलग outbound requests हैं

Redirect target URI बदल देता है। इसे नया action मानें, भले ही HTTP library इसे convenience feature कहती हो। `Location` value parse करें, unsupported schemes अस्वीकार करें, उसके hostname को execution time पर resolve करें, उसके answers validate करें और connect करने से पहले उसका port जांचें। Origin बदलने पर authorization headers अपने आप आगे ले जाना अलग credential-leak bug है, इसलिए जब तक नए origin के लिए अलग explicit authorization decision न हो, उन्हें हटा दें।

Redirects को छोटी fixed count तक सीमित रखें। Rebinding test server हर hop पर names और answers बदल सकता है और unlimited redirects साधारण outbound action को अस्पष्ट chain में बदल देते हैं। हर hop को source origin, destination origin, address set, selected peer, status code और किसी भी rejection के कारण के साथ log करें।

DNS checks उन protocol features के साथ भी जरूरी हैं जिन्हें भूलना आसान है। HTTP proxy बदल देता है कि शुरुआती TCP connection किसे मिलता है, इसलिए proxy address validate करें और फिर तय करें कि proxy क्या resolve कर सकता है। वरना CONNECT tunnel hostname lookup को ऐसे component में ले जा सकता है जो यही checks नहीं चलाता। Webhooks, callback URLs, object-storage endpoints और package registries भी यही treatment मांगते हैं, जब एजेंट उनके destination को प्रभावित कर सकता है।

Private DNS answers को block करने वाला rule arbitrary URLs को सुरक्षित नहीं बनाता। वह public server को dangerous command जारी करने, बहुत बड़ा response लौटाने या मंजूर लेकिन hostile service की ओर redirect करने से नहीं रोकता। DNS rebinding defense एक boundary है। इसे इतना सटीक रखें कि लोग इसे general-purpose policy language न समझें।

## Resolver और connector को एक unit की तरह test करें

Address classifier में `127.0.0.1` डालने वाले unit tests जरूरी हैं, पर पर्याप्त नहीं। Failure तब होता है जब client hostname को कई layers से गुजारता है और कोई layer बिना सुरक्षा के resolution करती है। आपके tests को वास्तविक socket destination देखना चाहिए।

`flip.test` जैसे नाम से controlled authoritative test zone बनाएं। पहले lookup पर ऐसा public test endpoint लौटाएं जो harmless request record करे। अगले lookup पर blocked address लौटाएं। फिर response के बाद पहला endpoint बंद करके दूसरा connection मजबूर करें। अपेक्षित परिणाम logs में warning के साथ सफल दूसरा request नहीं है। Connector को blocked address पर socket खोलने से पहले ही मना कर देना चाहिए।

ऐसा test matrix इस्तेमाल करें जिसमें हर बार एक ही condition बदले:

1. Public IPv4 answer लौटाएं, फिर low TTL के बाद loopback IPv4 answer लौटाएं।
2. एक ही answer set में `::1` के साथ allowed IPv4 address लौटाएं।
3. ऐसा CNAME लौटाएं जिसका final answer public से IPv6 unique local में बदलता हो।
4. दूसरे hostname पर redirect लौटाएं जो blocked address पर resolve होता हो।
5. Pooled connection बंद करें और सत्यापित करें कि retry को नया validation मिलता है।

Network boundary पर assertion जोड़ें। Fake connector को मिले numerical IP और port record करने चाहिए। अगर उसे hostname मिलता है तो test fail हो, क्योंकि तब validation के बाद भी दूसरा resolver चल सकता है। Integration tests के लिए blocked loopback port पर listener रखें और assert करें कि उसमें zero connections दर्ज हों। Listener तक पहुंचा हुआ rejected request उस boundary को पहले ही पार कर चुका है जिसे आप बचाना चाहते थे।

Short TTL tests महत्वपूर्ण हैं, क्योंकि वे अनपेक्षित जगहों के caches दिखाते हैं: application resolver, operating system, proxy, language runtime या HTTP library। ऐसे resolver का भी test करें जो TTL मानता हो और ऐसे का भी जो बदलता answer तुरंत लौटाता हो। Safety इस पर निर्भर नहीं हो सकती कि कोई खास cache धीमा है या तेज।

## मंजूरी और address validation अलग सवालों के जवाब देते हैं

Per-session approval का उत्तर है «इस run में कौन सा agent process कार्रवाई कर सकता है?» Per-call approval का उत्तर है «क्या इस credential use के लिए अभी इंसान का निर्णय चाहिए?» DNS validation का उत्तर है «इस connection को कौन सा network peer मिल सकता है?» इन्हें एक approval prompt में मिला देने से interface सरल दिखता है, मगर व्यक्ति ने वास्तव में किस बात की मंजूरी दी, उसमें महत्वपूर्ण बदलाव छिप जाता है।

Approval surface पर hostname और port दिखाएं, लेकिन hostname को immutable peer address जैसा पेश न करें। किसी व्यक्ति के कुछ सेकंड पहले prompt देखने पर भी implementation को execution time पर address validate करना चाहिए। अगर result blocked हो, तो action deny करें और hostname तथा classified address category बताएं। उपयोगकर्ता गलत DNS record ठीक कर सकता है, लेकिन वह अदृश्य rebinding race की उपयोगी मंजूरी नहीं दे सकता।

Sallyport का vault gate, session authorization और per-call credential approvals नियंत्रित करते हैं कि कौन action invoke कर सकता है और व्यक्ति को पुष्टि कब करनी होगी। इसके HTTP action path को फिर भी execution-time destination discipline चाहिए, क्योंकि जो vault agent को कभी secret नहीं देता उसे उस DNS answer को भी secret भेजने से बचना चाहिए जिस पर उपयोगकर्ता ने भरोसा करने का इरादा नहीं किया था।

इस सीमित समस्या को हल करने के लिए free-form rules engine न जोड़ें। Core behavior deterministic रह सकता है: external HTTP actions के लिए non-public answers अस्वीकार करें, connection time पर resolve करें, socket को चुने हुए address से bind करें और हर नए connection पर फिर से जांचें। Testable boundary वाला छोटा rule, exceptions से भरे ऐसे पेज की तुलना में सही रखना आसान है जिसे कोई समझा नहीं सकता।

## Logs में वही destination दिखना चाहिए जो वास्तव में इस्तेमाल हुआ

केवल hostname वाला audit record incident के बाद सबसे जरूरी सवाल का जवाब नहीं दे सकता: socket कहां गया? हर allowed connection के लिए requested origin और selected peer address दोनों store करें। Denied attempts के लिए returned answer set और denial कराने वाली classification store करें। Timestamp को connection creation के करीब रखें ताकि investigator उसे resolver behavior से जोड़ सके।

Log जिस certainty को support नहीं कर सकता, उसका दावा न करें। अगर HTTP library को केवल hostname मिलता है, तो audit record कह सकता है कि application ने hostname मांगा था, वह numerical peer साबित नहीं कर सकता। Audit entry को बेहतर बनाने से पहले connector ठीक करें। Evidence उसी layer से आना चाहिए जो socket को नियंत्रित करती है।

Tamper-evident audit trail के लिए DNS decision records को authorization, connection, redirect और response events के समान क्रम में शामिल करें। Sallyport अपने Sessions और Activity journals को write-blind encrypted, hash-chained audit log से project करता है और `sp audit verify` ciphertext पर उस chain को offline verify कर सकता है। यह तभी उपयोगी है जब action record में सुकून देने वाले लेकिन अधूरे hostname की जगह resolved destination शामिल हो।

Connection factory से शुरुआत करें। हर वह path खोजें जो hostname लेता है, उसे validated numerical peer लौटाने दें और socket API को raw hostnames अस्वीकार करने दें। यह invariant बन जाने पर short TTLs और answers में बदलाव security surprise बनने के बजाय साधारण test cases रह जाते हैं।
