# क्या custom authentication headers को सुरक्षित रूप से inject किया जा सकता है?

क्रेडेंशियल डालने वाले गेटवे को HTTP हेडर को सुविधाजनक string field नहीं, बल्कि protocol syntax मानना चाहिए। अगर agent, configuration screen, vault import या template इस field में कोई अनपेक्षित byte डाल सकता है, तो गेटवे ऐसी request भेज सकता है जिसका अर्थ approval screen, audit record या destination service के बताए अर्थ से अलग हो।

सुरक्षित नियम सीमित और स्पष्ट है: हर authentication field को गेटवे के लिए reserved रखें, request बनाने से ठीक पहले अंतिम bytes को validate करें और malformed input को साफ़ करके स्वीकार करने के बजाय अस्वीकार करें। इससे किसी credential के local storage से बाहर जाने से पहले line breaks, छिपे controls, Unicode lookalikes और duplicate field names पकड़े जाते हैं।

## Injection boundary के लिए byte contract ज़रूरी है

Custom authentication header का contract सामान्य HTTP parsing से अधिक सख्त होना चाहिए। सामान्य parsing को बड़े और अव्यवस्थित internet का साथ देना पड़ता है। Vault से secret लेकर request में डालने वाले गेटवे का काम छोटा है, इसलिए उसे कम input स्वीकार करना चाहिए।

सबसे पहले उन चार चीज़ों को अलग करें जिन्हें टीमें अक्सर एक ही «header value» मान लेती हैं:

1. Configured field name, जैसे `X-Api-Key`।
2. उस field के लिए store किए गए secret bytes।
3. किसी खास action के लिए agent द्वारा दिए गए request headers।
4. HTTP client को दी जाने वाली अंतिम outbound header list।

हर चीज़ का मालिक अलग है। Administrator या developer नाम configure करता है। Secret का मालिक vault है। Agent HTTP operation मांग सकता है और सामान्य application headers दे सकता है। अंतिम protected field को जोड़ने का काम केवल गेटवे करता है।

यह अंतर महत्वपूर्ण है, क्योंकि agent को secret कभी नहीं मिलना चाहिए कि वह उसे header label के साथ जोड़ सके। इससे एक अधिक सूक्ष्म गलती भी रुकती है: agent को `x-api-key` देने देना और बाद में गेटवे का `X-Api-Key` जोड़ना। HTTP field names case insensitive होते हैं, इसलिए ये दो अलग headers नहीं, बल्कि एक ही field के competing instances हैं। RFC 9110 field names को case insensitive बताता है और उन fields का वर्णन करता है जिनमें एक ही member अपेक्षित होता है।

Credential header के लिए byte contract साफ़ लिखें:

- नाम में केवल HTTP token character set हो और comparison के लिए उसे canonical lowercase रूप में रखा जाए।
- मान `0x21` से `0x7e` तक के printable ASCII bytes की nonempty sequence हो।
- मान में space, tab, CR, LF, NUL या कोई अन्य control byte न हो।
- गेटवे protected name का ठीक एक instance जोड़े।
- Agent request में protected field का नाम आते ही action विफल हो। उसे चुपचाप बदला न जाए।

यह नीति कुछ ऐसे generic field content को भी जानबूझकर अस्वीकार करती है जो वैध हो सकते हैं। यही उद्देश्य है। API key, bearer token या vendor credential को custom header में सुरक्षित रूप से भेजने के लिए tab, शुरुआती space या accented character की ज़रूरत नहीं होनी चाहिए। अगर किसी provider का credential format arbitrary bytes मांगता है, तो storage से पहले provider के documented transport representation में encode करें। जब protocol printable token मांगता हो, तब base64url आम विकल्प है। गेटवे पर कोई lossy conversion न बनाएं।

सबसे आकर्षक विकल्प arbitrary text स्वीकार करना, उसे trim करना, controls को spaces से बदलना और बाकी काम HTTP library पर छोड़ देना है। इससे एक credential की कई representations बनती हैं। UI में एक string दिख सकती है, log में दूसरी हो सकती है और library तीसरी भेज सकती है। Security control को एक बार तय करना चाहिए कि action valid है या नहीं। उसे action को बदलकर किसी और चीज़ में नहीं बदलना चाहिए।

## CR, LF और NUL request बनने से पहले अस्वीकार होने चाहिए

Protected header value में कहीं भी carriage return (`0x0d`), line feed (`0x0a`) और NUL (`0x00`) को अस्वीकार करें। अकेले CR या अकेले LF को भी उतनी ही दृढ़ता से अस्वीकार करें जितनी परिचित CRLF sequence को।

HTTP/1.1 field lines को अलग करने के लिए CRLF इस्तेमाल करता है। RFC 9112 कुछ परिस्थितियों में recipients को bare LF पहचानने की अनुमति भी देता है। यही वजह है कि outbound gateway यह मानकर नहीं चल सकता कि हर downstream component एक जैसा व्यवहार करेगा। इसमें कहा गया है कि senders protocol elements में bare CR नहीं बना सकते और पुराने folded field lines को हतोत्साहित किया गया है।

RFC 9110 field values के बारे में और स्पष्ट है: CR, LF और NUL अमान्य और खतरनाक हैं, क्योंकि implementations उन्हें अलग-अलग तरह से parse कर सकती हैं। इसमें recipients को आगे processing से पहले इन्हें अस्वीकार करने या spaces से बदलने की बात कही गई है। बदलना receiver recovery rule है, अच्छा gateway design नहीं। गेटवे जानता है कि वह नई request बना रहा है, इसलिए उसे ambiguity अपनाने के बजाय अस्वीकार करना चाहिए।

मान लें कि request builder तक ऐसा मान पहुंचता है:

```text
team-secret\r\nX-Approval: bypassed
```

सही validator दो forbidden bytes देखता है और action रोक देता है। केवल literal characters `\\r\\n` खोजने वाला कमजोर validator harmless backslashes देखेगा। Template expansion से पहले चलने वाला validator `${TOKEN}` देखेगा और expanded value की जांच नहीं करेगा। Line break को space से बदलने वाला validator स्पष्ट denial को malformed और बदले हुए credential वाली request में बदल देगा।

उपयोगी check केवल final byte sequence पर चलता है, decoding और interpolation के बाद तथा HTTP library द्वारा headers स्वीकार किए जाने से पहले। इसका परिणाम इस तरह हो सकता है:

```text
reject header value: field=x-api-key reason=forbidden-byte byte=0x0a offset=11
```

यह message local action record के लिए है, external caller के लिए नहीं। इसमें configured field और खराब byte की पहचान हो, लेकिन value copy न हो। Secret का quoted, JSON-escaped, base64-encoded या normalized रूप भी दर्ज न करें। ये सभी secret material हैं और अक्सर request से अधिक समय तक logs में रहते हैं।

इसे केवल HTTP/1.1 तक सीमित न करें। HTTP/2 fields को text lines के बजाय compressed blocks में ले जाता है, लेकिन RFC 9113 field value में हर position पर NUL, LF और CR को फिर भी मना करता है। यह खास तौर पर चेतावनी देता है कि unvalidated fields intermediary द्वारा HTTP/1.1 में translate किए जाने पर request smuggling करा सकते हैं। HTTP/3 भी उन fields के बारे में यही बात कहता है जो बाद में text-based protocol में जा सकते हैं।

## Header names के लिए ASCII ownership rules रखें

Non-ASCII header names को अस्वीकार करें। उन्हें normalize, transliterate या «काफी करीब» मानने की कोशिश न करें।

HTTP field names tokens होते हैं। Generic rule spaces, controls, delimiters और non-ASCII characters को बाहर रखता है। HTTP/2 में transport rule भी है कि names lowercase होने चाहिए और non-visible ASCII, uppercase letters तथा `0x7f` से ऊपर के bytes अस्वीकार किए जाते हैं। गेटवे इससे भी सरल नियम अपना सकता है: configured name को ASCII HTTP token के रूप में validate करें, ASCII rules से lowercase करें और original spelling केवल display के लिए रखें।

Comparison operation byte based होना चाहिए। अगर protected name `x-api-key` है, तो ASCII lowercasing के बाद agent द्वारा दिए गए ये सभी names उससे collide होने चाहिए:

```text
X-Api-Key
x-api-key
X-API-KEY
```

इन names को repair करने के बजाय name validation में सीधे अस्वीकार करें:

```text
x-api‐key       // Unicode hyphen
x-api-kеy       // Cyrillic e
x-api-key\n
x api key
```

Fixture में दिया गया comment महत्वपूर्ण है। किसी व्यक्ति को ASCII hyphen और Unicode hyphen या ASCII `e` और Cyrillic `е` में अंतर दिखाई नहीं दे सकता। Parser को typography समझने में माहिर बनाने की ज़रूरत नहीं होनी चाहिए। सभी non-ASCII header names अस्वीकार करें और ambiguity खत्म हो जाएगी।

Ownership syntax से अलग है। कोई name syntactically valid हो सकता है, फिर भी इसलिए forbidden हो सकता है क्योंकि उसका मालिक गेटवे है। Protected-name set को lowercase ASCII में रखें। Header collections को मिलाने से पहले agent के हर request header की तुलना इस set से करें। फिर outbound list को नए सिरे से बनाएं:

```text
ordinary agent headers that passed validation
+ gateway owned credential header
+ gateway owned request metadata, if any
```

Agent की list से शुरू करके चुनी हुई entries को overwrite न करें। कुछ HTTP libraries repeated values बचाती हैं, कुछ उन्हें coalesce करती हैं और कुछ headers को ऐसे map के रूप में देती हैं जिसमें original duplication खो जाती है। Protected field गलत collection में पहुंच जाने के बाद साधारण overwrite पर भरोसा करना देर हो चुकी होती है।

## Unicode normalization repair operation नहीं है

Injection के समय authentication values को Unicode normalize न करें। जिन systems में text equality define की गई हो वहां normalization की वैध भूमिका है, लेकिन secret आम तौर पर opaque byte sequence होता है और उसका अर्थ issuer तय करता है।

Unicode Normalization Forms, जिन्हें Unicode Standard Annex #15 में बताया गया है, canonically या compatibly equivalent text representations के बीच transformations का वर्णन करते हैं। इससे यह साबित नहीं होता कि वे credential के लिए सुरक्षित transformations हैं। `café` और `café` एक जैसे दिख सकते हैं, जबकि उनके code point sequences अलग हैं। Provider एक को अस्वीकार, एक को स्वीकार, एक को hash या दोनों को अलग secret मान सकता है। गेटवे यह अनुमान नहीं लगा सकता कि provider ने कौन सा व्यवहार चुना है।

Normalization से जुड़े दो अलग सवाल हैं:

### Identity fields को तभी normalize करें जब protocol इसे परिभाषित करता हो

Product यह तय कर सकता है कि usernames, display labels या application metadata की तुलना कैसे होगी। उस rule को वहीं लागू करें जहां product उस अर्थ का मालिक है और चुनी गई representation का स्पष्ट record रखें। HTTP credential injection में उसी rule का उपयोग केवल इसलिए न करें कि दोनों inputs strings के रूप में आए हैं।

Header names आसान हैं। वे user prose नहीं, protocol identifiers हैं। Non-ASCII input अस्वीकार करें और comparison के लिए ASCII lowercase इस्तेमाल करें। इस निर्णय में NFC, NFD, NFKC, NFKD, Unicode case folding और confusable skeletons का कोई स्थान नहीं है।

### Protected headers को मिलाने के लिए confusable matching कभी न करें

Unicode Technical Standard #39 security reviews के लिए confusable detection data देता है। यह स्पष्ट रूप से कहता है कि confusable skeleton mappings identifiers का normalization नहीं बननी चाहिए। चेतावनी सही है: suspicious label को flag करने वाली mapping protocol input को चुपचाप बदलने का सुरक्षित rule नहीं है।

अगर मानव ने अजीब label लिखा हो तो चेतावनी देने के लिए configuration UI में confusable detection इस्तेमाल कर सकते हैं। Request path में इसका उपयोग न करें। Request path को इतना ही कहना चाहिए: यह field name ASCII और reserved है, या invalid है। यह value printable ASCII और exact है, या invalid है।

ASCII credential policy के बावजूद Unicode normalization की testing ज़रूरी है। इससे interfaces और storage layers में छिपे conversions सामने आते हैं। Decomposed value, उसका composed equivalent, nonbreaking space, zero width character और right to left control हर input route से चलाएं। Custom authentication value का सही परिणाम rejection होना चाहिए और generic category यही होनी चाहिए: non-ASCII byte या character। परिणाम इस पर निर्भर नहीं होना चाहिए कि text settings screen, imported file या agent argument से आया है।

## Duplicate fields authorization failure हैं

Protected authentication name के लिए duplicate field action को रोकना चाहिए, भले दोनों values समान हों। एक field owner audit और approval के लिए सरल है और destination behavior पर बहुत कम निर्भर करता है।

लोग अक्सर कहते हैं कि values समान हों तो duplicates harmless हैं। वे authorization के बजाय data cleanup के बारे में सोच रहे होते हैं। Proxy fields को reorder करने से पहले या बाद में duplicate आ सकता है। Destination first instance चुन सकता है, last instance, instances concatenate कर सकता है, request reject कर सकता है या field-specific rule लगा सकता है। Intermediary अलग विकल्प चुन सकता है। तब action approval किसी एक unambiguous request का वर्णन नहीं करता।

यह मत मानें कि `Authorization` special है और हर `X-...` header casual। Vendor अपने custom credential field को single-valued परिभाषित कर सकता है और अधिकतर authentication schemes ऐसा ही करते हैं। Gateway को हर connection या credential binding के लिए protected names की स्पष्ट list रखनी चाहिए। इसमें `authorization`, `x-api-key`, `x-api-token` या developer द्वारा configured vendor field शामिल हो सकता है। यह naming convention पर निर्भर नहीं होना चाहिए।

Duplicate detection HTTP client library द्वारा header list को अपनी representation में बदलने से पहले होनी चाहिए। कई convenience APIs case insensitive name से string slice का map बनाती हैं। सावधानी से इस्तेमाल करने पर यह duplicates बचा सकता है, लेकिन ordering और source ownership अस्पष्ट हो सकती है। कुछ APIs `set` method से एक value replace और `add` method से दूसरी append करती हैं। ये operations तभी ठीक हैं जब गेटवे agent को protected names छूने से पहले ही रोक चुका हो।

हर agent-supplied header के लिए यह decision table अपनाएं:

| Condition | Gateway result |
| --- | --- |
| Invalid field name | Action अस्वीकार करें |
| किसी भी ASCII case में protected name | Action अस्वीकार करें |
| Destination protocol द्वारा अनुमत duplicate ordinary name | केवल स्पष्ट per-name rule के अनुसार preserve करें |
| अज्ञात semantics वाला duplicate ordinary name | Action अस्वीकार करें या connection-specific rule मांगें |
| Gateway credential field | Agent headers की validation के बाद ठीक एक बार जोड़ें |

तीसरी row जानबूझकर सीमित है। `Accept` में list semantics हो सकती है। Custom application field में ऐसा न हो। अगर गेटवे को पता नहीं कि repetition अर्थ बदलती है या नहीं, तो उसे कोई rule नहीं गढ़ना चाहिए। यहीं generic «pass through all headers» feature चुपचाप मानव के intent को bypass करने का तरीका बन जाता है।

## हर transformation boundary पर validate करें, फिर अंतिम परिणाम भी

एक शुरुआती validation check पर्याप्त नहीं है, क्योंकि request values पहली जांच के बाद अपना रूप बदल सकती हैं। Bytes बनाने, decode करने, जोड़ने या substitute करने वाली हर transformation boundary को local invariant चाहिए और अंतिम request builder को निर्णायक validation करनी चाहिए।

व्यावहारिक flow में पांच boundaries होती हैं:

1. Header name configure करते समय उसकी ASCII token syntax validate करें और lowercase form reserve करें।
2. Secret storage में आते समय credential contract validate करें और exact approved bytes store करें।
3. Agent operation देते समय ordinary request header names और values validate करें, फिर protected names अस्वीकार करें।
4. Templates, file reads, environment expansion और structured request decoding के बाद values फिर validate करें, क्योंकि इन प्रक्रियाओं ने नए bytes बनाए हो सकते हैं।
5. HTTP library call से ठीक पहले पूरी field list validate करें और assert करें कि हर protected name एक बार मौजूद है।

अंतिम check authoritative है, क्योंकि वही वास्तविक outbound object देखता है। पहले के checks अच्छे error messages देने और bad state को system में आने से रोकने के लिए हैं। वे अंतिम check का विकल्प नहीं हैं।

इसीलिए validation gateway core में होनी चाहिए, MCP tool description, prompt या agent-side helper में नहीं। वे intended request बता सकते हैं, socket तक पहुंचने वाली चीज़ की guarantee नहीं दे सकते। Credential injection का मालिक gate ही अंतिम byte check का मालिक होना चाहिए।

Sallyport इस boundary के उपयोगी हिस्से का पालन करता है: app खुद HTTP credential injection करता है और agent को secret के बजाय परिणाम मिलता है। Custom header credential के लिए action layer को injection point तक पहुंचने से पहले agent द्वारा दिए गए reserved field की copy भी अस्वीकार करनी चाहिए।

Validation errors deterministic रखें। अगर request में byte 8 पर CR और duplicate protected header दोनों हों, तो fixed ordering के अनुसार earliest failure report करें, जैसे invalid name, protected-name collision, invalid value और फिर final cardinality। Determinism tests को reliable बनाता है और attacker को error differences के जरिए stored credentials के बारे में अतिरिक्त जानकारी पाने से रोकता है।

## Validator को audit के लिए छोटा रखें

सख्त contract वाला छोटा validator exceptions की लंबी list वाले general sanitizer से अधिक सुरक्षित है। नीचे दिया गया Go example custom authentication value में केवल printable ASCII bytes स्वीकार करता है। यह input को trim, normalize, decode या rewrite नहीं करता।

```go
package authheader

import (
    "fmt"
    "strings"
)

func canonicalName(name string) (string, error) {
    if name == "" {
        return "", fmt.Errorf("empty field name")
    }
    for i := 0; i < len(name); i++ {
        b := name[i]
        ok := b == '!' || b == '#' || b == '$' || b == '%' || b == '&' ||
            b == '\'' || b == '*' || b == '+' || b == '-' || b == '.' ||
            b == '^' || b == '_' || b == '`' || b == '|' || b == '~' ||
            ('0' <= b && b <= '9') || ('A' <= b && b <= 'Z') ||
            ('a' <= b && b <= 'z')
        if !ok {
            return "", fmt.Errorf("invalid field-name byte 0x%02x at offset %d", b, i)
        }
    }
    return strings.ToLower(name), nil
}

func validateCredentialValue(value string) error {
    if len(value) == 0 {
        return fmt.Errorf("empty credential value")
    }
    for i := 0; i < len(value); i++ {
        b := value[i]
        if b < 0x21 || b > 0x7e {
            return fmt.Errorf("invalid credential byte 0x%02x at offset %d", b, i)
        }
    }
    return nil
}

func rejectProtected(headers [][2]string, protected map[string]struct{}) error {
    for _, h := range headers {
        name, err := canonicalName(h[0])
        if err != nil {
            return err
        }
        if _, found := protected[name]; found {
            return fmt.Errorf("agent supplied protected field %q", name)
        }
    }
    return nil
}
```

यह example value में `:` स्वीकार करता है, क्योंकि printable ASCII में वह शामिल है। कई API tokens के लिए यह ठीक है। अगर destination केवल documented subset स्वीकार करता है, तो उस provider rule को connection-specific validator में लागू करें। Global gateway policy में यह अनुमान न लगाएं कि colon, slash या equals sign संदिग्ध है।

Go में `string` इस्तेमाल करने का अर्थ यह नहीं कि validator Unicode-aware हो गया। Go string को index करने पर bytes मिलते हैं। Multibyte UTF-8 sequence में `0x7e` से ऊपर के bytes होते हैं, इसलिए यह contract उसे साफ़ तौर पर अस्वीकार करता है। अगर आपका runtime Unicode scalar values रखता है, तो पहले उन्हें exact outbound byte representation में encode करें और फिर byte validation चलाएं।

Caller को `validateCredentialValue` को cleanup helper के रूप में expose नहीं करना चाहिए। इसके केवल दो वैध परिणाम हैं: unchanged bytes की approval या error। Review के दौरान `sanitizeHeader`, `cleanHeader` या `normalizeToken` जैसे नाम वाले methods पर संदेह करें, क्योंकि वे callers को secret बदलने के लिए प्रेरित करते हैं और caller समझता है कि वह उसकी सुरक्षा कर रहा है।

## Test matrix में hostile representations होने चाहिए, केवल hostile strings नहीं

Test suite को साबित करना चाहिए कि outbound request तक पहुंचने वाला हर रास्ता उसी final validator से गुजरता है। केवल obvious CRLF input के साथ validator को सीधे call करना पर्याप्त नहीं है।

Raw values के लिए पहले table-driven unit tests इस्तेमाल करें:

```go
cases := []struct {
    name  string
    value string
    want  bool
}{
    {"ordinary token", "mF_9.B5f-4.1JqM", true},
    {"carriage return", "abc\rdef", false},
    {"line feed", "abc\ndef", false},
    {"crlf", "abc\r\ndef", false},
    {"nul", "abc\x00def", false},
    {"leading space", " abc", false},
    {"trailing tab", "abc\t", false},
    {"composed Unicode", "caf\u00e9", false},
    {"decomposed Unicode", "cafe\u0301", false},
}
```

इसके बाद full action path को ऐसे fixtures के साथ test करें जो application code को सामान्य लगते हैं:

- JSON request जिसमें newline `\n` decoding के जरिए आती है।
- Configuration import जिसमें file token के बाद newline पर समाप्त होती है।
- Template result जिसमें missing variable empty string बन जाता है।
- Agent request जिसमें `X-Api-Key` और `x-api-key` दोनों हैं।
- Agent request जिसमें name में non-ASCII lookalike है और जिसे protected-name matching bypass करने के बजाय name syntax पर fail होना चाहिए।

File ending वाला मामला एक वास्तविक आदत पकड़ता है। Developers text file में token copy करते हैं, ध्यान दिए बिना अंतिम newline जोड़ते हैं और ऐसा command substitution या importer इस्तेमाल करते हैं जो उसे बचा लेता है। एक path में shell command trailing newlines हटा सकती है, जबकि दूसरे में file reader उन्हें बचा सकता है। Gateway का सही व्यवहार consistent है: वह trim नहीं करता। Operator को stored credential ठीक करना पड़ता है।

Final builder के आसपास property tests जोड़ें। हर control byte, `0x7f` से ऊपर के bytes और printable ASCII वाले छोटे byte strings generate करें। Assert करें कि केवल nonempty और पूरी तरह allowed range में आने वाली strings pass हों। हर protected field name के case variants generate करें और assert करें कि हर ASCII case variant agent ownership को deny करे। इससे ऐसे errors मिलते हैं जैसे केवल `\r\n` को साथ में जांचना, अकेले LF भूल जाना या protected set में lookup से बाद में lowercase करना।

अंत में integration listener इस्तेमाल करें जो आपके HTTP library को वास्तव में मिलने वाले headers capture करे। उसे malformed protected value कभी नहीं दिखनी चाहिए, क्योंकि gateway को पहले ही deny कर देना चाहिए। Listener construction verify करता है, safety policy नहीं। अगर integration test को दूसरा protected field मिले, तो destination server ने request reject की हो तब भी इसे gateway defect मानें।

## Permissive fallback उस bug को छिपाता है जिसे ठीक करना चाहिए

Forbidden bytes को spaces से बदलना, whitespace trim करना या «last» duplicate रखना लोकप्रिय है, क्योंकि इससे demos चल जाते हैं। इससे outbound credential के source को दोबारा reconstruct करना भी कठिन हो जाता है।

Trim case देखें। Operator file से `token-42\n` import करता है। एक code path इसे trim करके `token-42` भेजता है। दूसरा raw value store करके बाद में reject करता है। Operator को local test में action सफल और gateway में विफल दिखता है। अंततः कोई inconsistency हटाने के लिए gateway में trimming जोड़ देगा। महीनों बाद template `token-42\r\nX-Role: admin` emit करता है। वही trimming code शायद केवल अंतिम newline हटाए और internal CRLF बचा दे। «Helpful» behavior ने source error और security boundary दोनों को छिपा दिया है।

सही repair उबाऊ है। एक accepted credential format तय करें, बाकी सभी values reject करें और import tools को दिखाने दें कि bytes क्यों fail हुए। Configuration screen token उजागर किए बिना `trailing LF at offset 8` दिखा सकती है। Command-line importer वही category print करके nonzero exit कर सकता है। User source ठीक करता है, बाद की हर layer को अनुमान लगाना नहीं सिखाता।

Audit records को authorization denial और transport failure में अंतर रखना चाहिए। दर्ज करें कि agent process ने action मांगा, dispatch से पहले action deny हुआ, कौन सा configured credential binding शामिल था और validation category क्या थी। Destination को call न करें और ऐसा synthetic HTTP response न बनाएं जो remote rejection जैसा लगे। कोई remote request हुई ही नहीं।

Sallyport के अलग session और activity records इस तरह के denial के लिए उपयोगी जगह देते हैं: session बताता है कि किस agent run ने प्रयास किया, जबकि action record दिखा सकता है कि local validation ने dispatch रोक दिया। Record उपयोगी बना रहे, लेकिन rejected secret या उसका escaped approximation उसमें न हो।

## नियम सख्त है, क्योंकि गेटवे secret का मालिक है

सामान्य HTTP client व्यापक input सह सकता है, क्योंकि वह अक्सर वही data भेजता है जो caller के पास पहले से है। Credential gateway की जिम्मेदारी अलग है। वह तय करता है कि agent secret-backed action करा सकता है या नहीं और यह निर्णय ऐसी request पर होना चाहिए जिसका syntax बदल न सके।

Custom authentication values में controls और non-ASCII अस्वीकार करें। Authentication names reserve करें और ASCII lowercase rules से तुलना करें। Duplicate protected fields रोकें। Credential को बाहर भेजते समय कभी normalize या trim न करें। हर input path को validate करें और फिर अंतिम header list को आखिरी बार validate करें।

अगर provider का authentication format इस contract में फिट नहीं होता, तो provider-specific encoding को document करें और secret के request builder तक पहुंचने से पहले उसे लागू करें। किसी integration में messy token मिलने के कारण gateway को ambiguous text स्वीकार करने वाला न बनाएं। पहला malformed value gate पर रुकना चाहिए, ऐसी denial के साथ जो byte problem समझाए और credential को अनदेखा रखे।
