# Approval targets के लिए IPv6 address normalization

ऐसी approval screen जो raw IPv6 string दिखाती है, वह समय के दबाव में किसी व्यक्ति से parser का काम करवाती है। यह डिजाइन की गलती है। सिस्टम को अनुरोध को typed endpoint में parse करना चाहिए, अस्पष्टता को अस्वीकार करना चाहिए, typed value की तुलना करनी चाहिए और एक ऐसा स्थिर रूप दिखाना चाहिए जिसे व्यक्ति अगले अनुरोध पर पहचान सके।

IPv6 notation हमलावरों और सामान्य software, दोनों को एक ही destination को अलग दिखाने के कई तरीके देता है: compressed zero fields, leading zeros, URLs में जरूरी brackets, embedded IPv4 tail और interface zones। इससे IPv6 संदिग्ध नहीं हो जाता। इसका मतलब बस इतना है कि approval target को उस field से अधिक सख्ती से संभालना होगा जिसमें संयोग से hostname आ गया हो।

## एक पते के कई टेक्स्ट रूप हो सकते हैं

`2001:db8:0:0:0:0:0:9`, `2001:0db8::9` और `2001:db8::9` एक ही unscoped IPv6 address पहचानते हैं। अगर आपका approval card एक string स्टोर करता है और बाद का अनुरोध दूसरा रूप देता है, तो string comparison उन्हें अलग बताएगा। अगर आपकी allowlist एक spelling स्वीकार करती है, लेकिन audit search दूसरे रूप की अपेक्षा करती है, तो जरूरत के समय operators को रिकॉर्ड का सिरा नहीं मिलेगा।

IPv6 में आठ 16-bit fields होते हैं। हर field में लेखक शुरुआती शून्य छोड़ सकता है, फिर सभी-शून्य fields की एक लगातार श्रृंखला को `::` से बदल सकता है। `::` marker इंसानों के लिए सुविधाजनक है, मगर इससे यह जानकारी हट जाती है कि लेखक ने कितने fields छोड़े। Parser छूटे fields फिर से बनाता है और equality decision के लिए मायने रखने वाला एकमात्र रूप लौटाता है: address के 16 bytes।

यह अंतर अक्सर धुंधला हो जाता है: canonical text समीक्षा के लिए है, जबकि parsed bytes comparison के लिए हैं। Canonicalization अपने आप authorization नहीं है। यह बस सुनिश्चित करता है कि व्यक्ति को दिखाया गया रूप कॉलर की spelling पर निर्भर न हो।

इन्हें अलग data items मानें:

- `raw_input`: कॉलर से मिला exact host text और उसके आसपास का authority syntax
- `address`: strict parsing के बाद 16 bytes
- `scope_id`: input में वैध रूप से दिया गया interface scope
- `display_host`: typed address से बनाया गया canonical string
- `port`, `scheme` और request details: असल कार्रवाई को बताने वाले fields

Audit record में raw input रखें। इससे पता चलता है कि agent ने क्या मांगा था। इसे comparison token की तरह इस्तेमाल न करें और न इसे screen पर दिखने वाली अकेली चीज बनाएं।

## URI brackets syntax हैं, host का हिस्सा नहीं

URI authority के भीतर IPv6 literal को brackets में लिखना जरूरी है, क्योंकि colons पहले ही host और port को अलग करते हैं। RFC 3986 यह रूप बताता है: `https://[2001:db8::9]:8443/v1/jobs`। पता `2001:db8::9` है, brackets URI parser को बताते हैं कि host कहां समाप्त होता है।

इस नियम से एक आम विफलता पैदा होती है। कोई developer `[2001:db8::9]` को address parser में देता है, rejection मिलता है, वह काम चलने तक characters हटाता है और बाद में malformed authority स्वीकार हो जाती है क्योंकि दोनों parsers की सहमति नहीं रहती। इसी bug का दूसरा रूप address के साथ brackets भी स्टोर कर देता है, इसलिए एक record में `[2001:db8::9]` और दूसरे में `2001:db8::9` होता है। वे कभी एक ही storage field तक पहुंचने ही नहीं चाहिए थे।

जिस grammar boundary पर value आई थी, वहीं parse करें। HTTP target के लिए पहले पूरे URI को standards-aware URI parser से parse करें। उसी parser के अनुसार उसका host, port, scheme, path और query निकालें। फिर URI brackets हटाकर केवल host value को IPv6 parser में भेजें। सीधे SSH host argument के लिए SSH command interface में बताए गए grammar का प्रयोग करें, उसे URI मानने का दिखावा न करें।

क्रम मायने रखता है। इस अनुरोध को देखें:

```text
https://[2001:0db8:0:0:0:0:0:9]:8443/admin
```

सही internal result का रूप यह है:

```text
kind: ipv6
address: 20010db8000000000000000000000009
scope_id: null
display_host: 2001:db8::9
scheme: https
port: 8443
path: /admin
```

अब approval UI `https://[2001:db8::9]:8443/admin` कह सकता है। उसे इसे चुपचाप `2001:db8::9` में नहीं बदलना चाहिए, क्योंकि port और path उस चीज का हिस्सा हैं जिसका उपयोगकर्ता मूल्यांकन कर रहा है। उलटे, सिर्फ इसलिए कि वह पहले आया था, कॉलर की padded spelling भी नहीं रखनी चाहिए।

Bare literal में brackets नहीं होते। IPv6 literal वाली URI authority में brackets होते हैं। इस नियम को सीमित और अनुमान लगाने योग्य रखें।

## Canonical display को RFC 5952 का पालन करना चाहिए

RFC 5952 lowercase hexadecimal, field में कोई leading zero नहीं और zero fields की सबसे लंबी लगातार श्रृंखला के लिए `::` की सलाह देता है। जब दो zero runs की लंबाई बराबर हो, तो यह पहले को चुनता है। यह यह भी कहता है कि zero का एक अकेला field `::` का इस्तेमाल न करे। ये नियम मानव-पठनीय रूप को स्थिर परिणाम देते हैं।

उदाहरण के लिए, इन values को इस तरह normalize करें:

```text
2001:0DB8:0000:0000:0000:0000:0000:0009  ->  2001:db8::9
2001:db8:0:1:0:0:0:1                    ->  2001:db8:0:1::1
2001:db8:0:1:0:0:0:0                    ->  2001:db8:0:1::
2001:db8:0:1:0:0:0:2                    ->  2001:db8:0:1::2
0:0:0:0:0:0:0:1                         ->  ::1
```

Standard की सलाह दिखने से अधिक उपयोगी है। यह operators को logs में खोजने के लिए एक ही spelling देता है और कॉलर को `2001:0DB8::9` को नया destination दिखाने से रोकता है, जब `2001:db8::9` पहले ही मंजूर हो चुका हो।

Colons पर split करके और empty strings गिनकर अपना formatter न लिखें। IPv4 tail form, malformed double compression और scope syntax इस तरीके को नाजुक बना देते हैं। जिस language में कार्रवाई होती है, उसमें परीक्षण किया हुआ IPv6 parser इस्तेमाल करें, उसका 16-byte output रखें और उन्हीं bytes से format बनाएं। Formatter को RFC 5952 examples और बराबर लंबाई वाले zero runs के cases से जांचें।

RFC 5952 के बारे में जरूरत से ज्यादा दावा भी न करें। यह सुझाया गया text representation बताता है। यह तय नहीं करता कि `::ffff:192.0.2.7` और `192.0.2.7` को समान authorization मिलना चाहिए या नहीं। यह वास्तविक परिणामों वाला product decision है।

## IPv4-mapped forms के लिए comparison rule स्पष्ट होना चाहिए

`::ffff:192.0.2.7` एक IPv4-mapped IPv6 address है। अंतिम 32 bits में IPv4 address होता है और उससे पहले का pattern mapped form पहचानता है। जब application IPv6 socket पर IPv4 connections स्वीकार करता है, तो operating systems अक्सर यह रूप दिखाते हैं। यह logs में इतना दिखता है कि इसे अजीब edge case मानना आगे चलकर भ्रम की गारंटी है।

दो उचित internal models हैं। हर authorization boundary के लिए एक चुनें और उसे UI में लिखें।

पहला model address families को अलग रखता है। `::ffff:192.0.2.7` kind `ipv6` वाला 16-byte IPv6 address रहता है, जबकि `192.0.2.7` kind `ipv4` वाला four-byte IPv4 address रहता है। उनकी तुलना कभी समान नहीं होती। जब approval किसी खास रूप में मांगे गए network connection को बताता है, तब यह अधिक सुरक्षित default है, क्योंकि यह परिवारों के बीच decision को चुपचाप फैलने से रोकता है।

दूसरा model सीमित peer-identity use case के लिए mapped addresses को IPv4 में project करता है। इस model में parser original family और `embedded_ipv4` value दोनों रिकॉर्ड करता है। Comparison code जानबूझकर कहता है कि इस एक उद्देश्य के लिए mapped form अपने embedded IPv4 address के बराबर है। Audit record फिर भी raw form रखता है और बताता है कि comparison में projection का इस्तेमाल हुआ।

Accidental projection विफल होता है। कई standard libraries ऐसा convenience method देती हैं जो mapped address को IPv4 में बदल देता है और यह अंतर नहीं बताता कि वह किस रूप में आया था। यह connection logging के लिए उपयोगी है, मगर developer इसे approval token के लिए दोबारा इस्तेमाल करे तो खतरनाक है। तब `192.0.2.7` के लिए लिखा नियम बिना किसी निर्णय के `::ffff:192.0.2.7` को मंजूरी दे सकता है।

ऐसे test pairs इस्तेमाल करें जिनसे चुनाव साफ दिखे:

```text
input A: 192.0.2.7
input B: ::ffff:192.0.2.7

strict endpoint comparison: different
explicit peer-identity projection: same IPv4 peer, if the product says so
```

Dotted tail वाली हर IPv6 string को mapped न मानें। Parser को पूरे prefix और IPv4 portion की position validate करनी चाहिए। RFC 4291 IPv4-mapped addresses को परिभाषित करता है और IPv6 text में IPv4-compatible notation की भी अनुमति देता है। आपके formatter को पर्याप्त typed information रखनी चाहिए ताकि हर dotted-tail address को एक ही चीज न कहा जाए।

## Zone identifiers स्थानीय interface से जुड़े होते हैं

Zone identifier किसी अन्यथा अस्पष्ट scoped address को काम का स्थानीय destination बनाता है। `fe80::1%en0` का मतलब interface `en0` पर link-local address है। Zone के बिना, एक से अधिक network interfaces वाला host नहीं जान सकता कि कॉलर किस link की बात कर रहा है।

RFC 4007 इसे address में जोड़ी गई सजावट नहीं, scope zone concept कहता है। Interface name या index का अर्थ केवल उस host के लिए है जो इसे resolve करता है। किसी दूसरी machine पर `en0` किसी अलग interface का नाम हो सकता है या हो ही नहीं सकता। इसलिए zone identifier portable approval target के लिए खराब विकल्प है।

सीधे local socket action के लिए zone तभी स्वीकार करें जब platform parser उसे validate करे और कार्रवाई उसी machine पर चले। अगर OS देता है तो comparison value के रूप में normalized numerical interface index स्टोर करें। Audit trail के लिए दिया गया interface name भी रख सकते हैं, मगर hardware और network configuration बदलने पर names बदल सकते हैं।

HTTP URI के लिए नियम अधिक सख्त हैं। RFC 6874 के अनुसार bracketed literal में zone identifier शुरू करने वाला percent sign `%25` के रूप में encoded होता है। इसलिए URI में ऐसा रूप होता है:

```text
http://[fe80::1%25en0]/status
```

URI parser को इसे सही चरण में decode करना चाहिए। Parse करने से पहले पूरे URL को percent-decode न करें, क्योंकि सामान्य decoder delimiters बदलकर कॉलर के दिए हुए URL से अलग URL बना सकता है। पहले URI parse करें, host literal निकालें, फिर उस component के लिए जरूरी scoped-literal rules लागू करें।

Agent-facing HTTP approval flows में अधिकतर scoped literals को अस्वीकार करना चाहिए और कॉलर को कारण बताना चाहिए: link-local addresses केवल किसी खास local interface के साथ ही अर्थ रखते हैं। Agent से stable DNS name, unscoped address या जानबूझकर configured local endpoint इस्तेमाल करने को कहना ऐसी मंजूरी बनाता है जिसे दूसरा व्यक्ति समझ सके। अपवाद केवल वहीं दें जहां product वास्तव में local network equipment के साथ काम करता हो और interface scope को साफ तौर पर दिखाता हो।

## Host comparison कार्रवाई की मंजूरी से छोटा होता है

Address normalize करने से धोखे की एक सीमित श्रेणी ठीक होती है। इससे `https://[2001:db8::9]/` और `https://[2001:db8::9]:9443/delete` बराबर नहीं हो जाते, और परिचित दिखने वाले host text से SSH destination सुरक्षित नहीं बनता।

Typed approval target में वे boundaries बनी रहनी चाहिए जिन्हें raw string छिपा देती है। HTTP के लिए आम तौर पर कम से कम scheme, host kind और bytes, default-port resolution के बाद port, method और path को उजागर करने वाला request description शामिल होना चाहिए। Headers और body digest decision का हिस्सा होंगे या नहीं, यह कार्रवाई पर निर्भर है। अगर एक bearer credential कई असंबंधित APIs को call कर सकता है, तो credential identity भी prompt में होनी चाहिए।

SSH के लिए network endpoint और remote command में अंतर करें। SSH connection खोलने की मंजूरी अपने आप `sudo` चलाने, deployment files बदलने या port forward करने की अनुमति नहीं बताती। अगर कोई tool एक connection के बाद कई operations कर सकता है, तो UI को बताना चाहिए कि session authorization किसे cover करता है और हर command का per-call record रखना चाहिए।

यहीं साफ data model का लाभ मिलता है। यह एक आकर्षक मगर गलत सलाह को रोकता है: canonical hosts को flat allowlist में डालें और काम पूरा समझ लें। Flat host lists लोकप्रिय हैं क्योंकि उन्हें समझाना आसान है। वे विफल होती हैं क्योंकि host कार्रवाई का केवल एक हिस्सा है और बाद का DNS answer, port, path या command असर बदल सकता है।

उपयोगी approval record ऐसा दिख सकता है:

```text
transport: https
host_kind: ipv6
host_bytes: 20010db8000000000000000000000009
host_display: 2001:db8::9
scope_id: null
port: 8443
method: POST
path: /v1/releases
credential_ref: deploy-api
raw_authority: [2001:0db8::9]:8443
```

Raw authority अनुरोध का दस्तावेज है। Typed fields comparison चलाते हैं। Display field व्यक्ति को पढ़ने के लिए स्थिर वाक्य देता है। इनमें से किसी एक field को दूसरों का विकल्प बनाकर दोबारा इस्तेमाल न करें।

## Rejection rules साधारण और सख्त होने चाहिए

जब input अपनी position के grammar में फिट न हो, parser को fail closed करना चाहिए। Error message मददगार हो सकता है, मगर system को कॉलर की मंशा का अनुमान लगाकर address ठीक नहीं करना चाहिए। लगभग-वैध input स्वीकार करने वाला normalizer दूसरा grammar बना देता है, जिसे security reviewers के लिए फिर से समझना कठिन होता है।

IP-literal request को अस्वीकार करें अगर उसमें इनमें से कोई दोष हो:

- एक से ज्यादा `::` compression marker हों या expansion के बाद बहुत ज्यादा fields हों
- hexadecimal field में non-hexadecimal characters हों
- IPv4 tail, IPv6 text syntax की स्वीकृत position के बाहर हो
- bare address parser को brackets दिए गए हों, या URI authority में brackets न हों
- ऐसे context में zone identifier हो जहां कार्रवाई local interface से bind नहीं हो सकती

ऐसे literal को भी अस्वीकार करें जिसे आपका URI parser और socket parser अलग तरह से समझते हों। यह सैद्धांतिक बारीकी नहीं है। समय के साथ अलग libraries ने percent encoding, असामान्य IPv4 forms और host parsing को लेकर अलग फैसले किए हैं। एक component text approve करे और दूसरा component किसी और जगह connection बनाए, तो authorization gap बनता है।

छोटा cross-layer test corpus बनाएं। हर case को उसी URI parser, host extractor, IP parser, formatter, comparator और transport builder से गुजारें जो production में इस्तेमाल होते हैं। Accepted input के लिए canonical display और वास्तविक socket destination, दोनों assert करें। Rejected input के लिए assert करें कि कोई transport object नहीं बनता।

Compact corpus में `::`, `::1`, fully expanded address, बराबर लंबाई के दो zero runs, port वाला bracketed URI literal, mapped IPv4 value, malformed dotted tail, scoped link-local literal और URI में encoded `%25` zone शामिल होने चाहिए। अपने environment में agents ने जो भी input shapes वास्तव में बनाए हैं, उन्हें जोड़ें। वास्तविक approvals से बने regression tests parser tricks से कम चमकदार हैं, लेकिन अगली गलती पकड़ने की संभावना कहीं ज्यादा रखते हैं।

## Approval card को normalization दिखाना चाहिए

अगर card बदला हुआ हिस्सा छिपाता है, तो व्यक्ति endpoint का आकलन नहीं कर सकता। Canonical target को प्रमुखता से दिखाएं, फिर spelling अलग होने पर submitted spelling दिखाएं। `Submitted as [2001:0DB8:0:0:0:0:0:9]:8443` जैसी शांत पंक्ति reviewer को fields मन में decode कराए बिना सबूत देती है।

Card को special forms भी साफ बताने चाहिए। Mapped address को सामान्य IPv6 literal की तरह दिखाने के बजाय `IPv4-mapped IPv6` label दें। Scoped address पर interface name के साथ label लगाएं और कहें कि वह उसी interface के लिए local है। अगर action policy mapped address को IPv4 में project करती है, तो card में वह निर्णय लिखें। Chup equivalence से reviewers चौंकते हैं।

उच्च-परिणाम वाली calls के लिए व्यक्ति से केवल canonical host नहीं, पूरी कार्रवाई मंजूर कराएं। Compact presentation में भी जरूरी विवरण आ सकता है:

```text
POST https://[2001:db8::9]:8443/v1/releases
Uses credential: deploy-api
Submitted host spelling: 2001:0DB8:0:0:0:0:0:9
```

Sallyport उसी जगह यह अलगाव निभाता है जहां इसकी जरूरत है: agent को कभी API या SSH secret नहीं मिलता, जबकि app कार्रवाई करता है और उसका परिणाम लौटाता है। इसका per-session authorization यह तय कर सकता है कि run किसने शुरू किया, और इसके per-call controls संवेदनशील actions को दिखाई देते रहने देते हैं, उन्हें पहले की मंजूरी का blank check नहीं मानते।

## Audit records को raw text और normalized meaning, दोनों चाहिए

Incident review में दो ऐसे जवाब चाहिए जिनमें सिर्फ एक representation बचाने पर अक्सर टकराव होता है: agent ने वास्तव में क्या भेजा और transport ने किस destination का उपयोग किया? दोनों सहेजें। फिर साफ बताएं कि decision किस value से लिया गया।

Audit event में raw input, parsed host kind, canonical display form, unambiguous encoding में address bytes, मौजूद होने पर scope identity और approval में शामिल सभी action fields होने चाहिए। Serialization के बाद event hash करना तभी उपयोगी है जब serialization की स्थिर परिभाषा हो। वरना formatter बदलने से एक ही semantic event अलग records बना सकता है।

Display form निकालने के बाद noncanonical input मिटाएं नहीं। वह किसी buggy client, bypass की कोशिश या किसी हानिरहित library quirk का संकेत हो सकता है, जो बाद में incident समझाने में मदद करे। उसे evidence की तरह रखें, मगर search dashboards उसे उसी endpoint की भ्रामक दूसरी identity न बनाने दें।

Sallyport के Activity journal और Sessions journal एक encrypted, hash-chained audit log से बनते हैं, और `sp audit verify` उस chain को ciphertext पर offline जांचता है। यह संरचना खास तौर पर तब उपयोगी है जब reviewer को असामान्य spelling और अलग executed action में फर्क करना हो।

पूरी approval layer को नए सिरे से डिजाइन करने से पहले एक test से शुरू करें: एक ही target को fully expanded और RFC 5952 form में submit करें। अगर आपका system अलग approval identities, अलग audit search results या अलग allow decisions देता है, तो parser boundary अभी भी गलत जगह है।
