क्या custom authentication headers को सुरक्षित रूप से inject किया जा सकता है?
Custom authentication headers को gateway में डालने से पहले byte-level validation ज़रूरी है। Line breaks, Unicode traps और duplicate fields को अस्वीकार करें।

क्रेडेंशियल डालने वाले गेटवे को 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» मान लेती हैं:
- Configured field name, जैसे
X-Api-Key। - उस field के लिए store किए गए secret bytes।
- किसी खास action के लिए agent द्वारा दिए गए request headers।
- 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 तक ऐसा मान पहुंचता है:
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 स्वीकार किए जाने से पहले। इसका परिणाम इस तरह हो सकता है:
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 होने चाहिए:
X-Api-Key
x-api-key
X-API-KEY
इन names को repair करने के बजाय name validation में सीधे अस्वीकार करें:
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 को नए सिरे से बनाएं:
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é और café एक जैसे दिख सकते हैं, जबकि उनके 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 होती हैं:
- Header name configure करते समय उसकी ASCII token syntax validate करें और lowercase form reserve करें।
- Secret storage में आते समय credential contract validate करें और exact approved bytes store करें।
- Agent operation देते समय ordinary request header names और values validate करें, फिर protected names अस्वीकार करें।
- Templates, file reads, environment expansion और structured request decoding के बाद values फिर validate करें, क्योंकि इन प्रक्रियाओं ने नए bytes बनाए हो सकते हैं।
- 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 नहीं करता।
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 इस्तेमाल करें:
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
\ndecoding के जरिए आती है। - 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 को अनदेखा रखे।
सामान्य प्रश्न
क्या गेटवे को HTTP हेडर मान में CRLF और अकेली न्यूलाइन, दोनों अस्वीकार करनी चाहिए?
दोनों को अस्वीकार करें। कैरिज रिटर्न और लाइन फीड HTTP/1.1 रिसीवर को फ़ील्ड की सीमाएं अलग तरह से दिखा सकते हैं, और अलग-अलग रिसीवर ऐतिहासिक रूप से अलग लाइन एंडिंग स्वीकार करते रहे हैं। क्रेडेंशियल हेडर में इनमें से किसी भी कैरेक्टर को ठीक करके न भेजें। आउटबाउंड रिक्वेस्ट बनाने से पहले कार्रवाई रोक दें।
क्या custom authentication headers में NUL बाइट रोकना ज़रूरी है?
NUL का प्रमाणीकरण हेडर मान में कोई वैध स्थान नहीं है और HTTP इसे अमान्य मानता है। इसे CR और LF के साथ अस्वीकार करें। ऑडिट इवेंट में सीक्रेट दर्ज किए बिना बाइट ऑफसेट और फ़ील्ड की भूमिका लिखें।
क्या API कुंजी को हेडर में जोड़ने से पहले Unicode normalization करना चाहिए?
इंजेक्शन के समय क्रेडेंशियल को normalize न करें। Unicode normalization दो स्ट्रिंग को बराबर दिखा सकती है, जबकि प्रोवाइडर को अपेक्षित बाइट बदल जाएं। स्वीकृत बाइट को उसी रूप में स्टोर और इंजेक्ट करें, या सीक्रेट के वॉल्ट में जाने से पहले base64url जैसे ASCII transport encoding की मांग करें।
डुप्लिकेट HTTP हेडर नामों की सुरक्षित तुलना कैसे करें?
हेडर फ़ील्ड नामों को ASCII प्रोटोकॉल टोकन मानें और उनके lowercase ASCII रूप की तुलना करें। फ़ील्ड नामों पर Unicode case folding या NFKC न लगाएं। गैर-ASCII नाम को अमान्य मानें, ताकि वह protected field जैसा दिखने वाला रूप न बन सके।
क्या गेटवे को डुप्लिकेट Authorization हेडर हटाना चाहिए या अस्वीकार करना चाहिए?
इंजेक्ट किए जाने वाले authentication field के लिए रिक्वेस्ट अस्वीकार करें। एजेंट की कॉपी को चुपचाप हटाने से authorization की गलती छिपती है और अलग-अलग लेयर में अलग समझ बन सकती है। protected field का एक ही मालिक होना चाहिए, गेटवे।
क्या API key हेडर में tabs या Unicode रखना सुरक्षित है?
सामान्य HTTP parser आपके credential contract से अधिक व्यापक फ़ील्ड सामग्री स्वीकार कर सकता है। Custom authentication के लिए बिना शुरुआती या अंतिम whitespace वाली visible ASCII नीति आम तौर पर टेस्ट करना आसान और अलग HTTP versions तथा intermediaries के बीच अधिक सुरक्षित होती है।
HTTP header injection bugs पकड़ने वाले टेस्ट केस कौन से हैं?
Raw CR, raw LF, CRLF, NUL, शुरुआती और अंतिम spaces, tabs, गैर-ASCII बाइट, decomposed Unicode, composed Unicode और अलग ASCII case वाले डुप्लिकेट नामों की जांच करें। इन्हीं मामलों को हर configuration, vault, template और request-construction path से चलाएं।
Agent gateway में हेडर validation कहां होनी चाहिए?
HTTP library को मान देने से ठीक पहले उसकी जांच होनी चाहिए, क्योंकि decoding, interpolation या serialization से पहले की जांच को बायपास किया जा सकता है। कॉन्फ़िगर किया गया हेडर नाम सेव करते समय validate करें, लेकिन हर outbound action के लिए अंतिम byte sequence को फिर validate करें।
क्या headers में line breaks केवल HTTP/1.1 के लिए खतरनाक हैं?
HTTP/2 में HTTP/1.1 वाला textual CRLF framing नहीं होता, लेकिन field values में CR, LF और NUL फिर भी अमान्य हैं। कोई gateway proxy के जरिए protocol boundaries पार कर सकता है, इसलिए validation मौजूदा transport version पर निर्भर नहीं होनी चाहिए।
हेडर मान validation में विफल होने पर ऑडिट लॉग में क्या दर्ज करना चाहिए?
लॉग में action को denied के रूप में, configured protected field name, rejection category और खराब बाइट की स्थिति दर्ज करें। भेजा गया हेडर मान, normalized copy या credential का escaped रूप न लिखें। Escape notation सीक्रेट लीक होने का दूसरा रास्ता बन सकती है।