- HTTP कुकी वेब पर state बनाए रखने का बुनियादी साधन हैं, लेकिन browser·server·standard library अनुमत characters और error handling में अलग-अलग व्यवहार करते हैं, जिससे वास्तविक outages हो सकते हैं
- RFC 6265 परिवार में server द्वारा भेजे जाने वाले
Set-Cookievalue और browser द्वारा स्वीकार किए जाने वाले value की शर्तें अलग हैं, औरdocument.cookieसे बने value server-side parser की धारणाओं से टकराते हैं - Firefox, Chromium, Safari whitespace, quotes, commas, backslashes और Unicode handling में एक-दूसरे से अलग हैं, और Safari प्रतिबंधित character मिलने पर पूरी कुकी नहीं बल्कि सिर्फ शुरुआती हिस्सा सहेजता है
- Go browser द्वारा स्वीकार की गई JSON कुकी को चुपचाप छोड़ सकता है, Python
SimpleCookieअपनी समझ से बाहर की कुकी के बाद loading रोक सकता है, और PHP·Ruby·Rust में भी अनुमत दायरा अलग-अलग है - एक ही Unicode कुकी Facebook, Netflix, Okta, WhatsApp, AWS, Apple Support जैसी बड़ी sites पर 400/500 errors या partial outages पैदा कर सकती है, इसलिए कुकी spec और library व्यवहार को अधिक स्पष्ट रूप से align करना ज़रूरी है
ऐसी कुकी जिसे browser स्वीकार करता है, लेकिन Go पढ़ नहीं पाता
- कुकी JavaScript के
document.cookieया HTTP server द्वारा सेट किया गया data है, और expiry से पहले scope मिलते रहने तक संबंधित HTTP requests में लगातार शामिल रहती है - उदाहरण JavaScript JSON string को ज्यों का त्यों session cookie value के रूप में सहेजता है
- value
{"ginger":"snap","peanutButter":"chocolate chip","snicker":"doodle"}के रूप में है - कुकी में JSON डालते समय अक्सर base64 serialization की जाती है, लेकिन browser इस value को बिना समस्या सेट कर देता है और
Cookieheader में भेजता है
- value
- समस्या तब आती है जब यह कुकी Go standard library इस्तेमाल करने वाले code तक पहुँचती है
- Go parser उस कुकी को parse नहीं कर पाता
- failure stack के ऊपरी स्तरों तक chain होकर फैल जाती है
RFC के भीतर दो मानदंडों का mismatch
- कुकी को RFC 2109, RFC 2965, RFC 6265 के जरिए परिभाषित किया गया है, और अभी एक updated draft version मौजूद है
- RFC कुकी value को दो क्षेत्रों में अलग ढंग से संभालता है
- Section 4.1.1 में server के
Set-Cookievalue से control characters, whitespace, double quotes, commas, semicolons, backslashes आदि को बाहर रखा गया है - Section 5.6 में browser को
Set-Cookiestring parse करते समय control characters को छोड़कर कहीं अधिक व्यापक input स्वीकार करने दिया गया है
- Section 4.1.1 में server के
- मुख्य टकराव यह है कि server को कौन-सी value भेजनी चाहिए और browser को कौन-सी value स्वीकार करनी चाहिए — ये दोनों aligned नहीं हैं
- अगर browser सिर्फ वही कुकी स्वीकार करे जो server ने खुद set की हो, तो प्रभाव छोटा होगा, लेकिन
document.cookieभी कुकी बना सकता है - standard यह साफ़ नहीं करता कि
Cookieheader संभालने वाली standard libraries को user agent जितना उदार होना चाहिए या server जितना सख्त
- अगर browser सिर्फ वही कुकी स्वीकार करे जो server ने खुद set की हो, तो प्रभाव छोटा होगा, लेकिन
अलग-अलग browsers में cookie value स्वीकारने का अंतर
-
Firefox
- Firefox की cookie value validation, RFC 6265 द्वारा निषिद्ध कुछ characters को अनुमति देती है
- RFC द्वारा exclude किए जाने की सिफारिश वाले जिन characters को यह स्वीकार करता है, वे हैं
0x09horizontal tab0x20space0x22double quote0x2Ccomma0x5Cbackslash
- यह व्यवहार पुरानी Chrome compatibility के लिए जोड़ा गया था और दोनों codebases में बचा हुआ है
network.cookie.blockUnicodesetting0x80या उससे ऊपर के values को reject कर सकती है, और संबंधित काम bug 1797231 में track हो रहा है0x7Fअनुमति वाली समस्या bug 1797235 में Firefox 108 में ठीक की गई
-
Chromium
- Chromium cookie value में control characters और semicolon को ही reject करता है
- यह Firefox से थोड़ा अधिक सख्त है, इसलिए
0x09horizontal tab स्वीकार नहीं करता - RFC के विपरीत, यह whitespace, double quotes, commas, backslashes और Unicode characters को स्वीकार और वापस भेज सकता है
-
Safari / WebKit
- Safari का cookie storage code closed-source
CFNetworkके भीतर है, इसलिए उसे सीधे verify करना कठिन है - JavaScript से
0x00से0xFFतक cookie values सेट कर जाँचने पर, Safari निम्न values स्वीकार करता है0x09horizontal tab0x20space0x22double quote0x5Cbackslash
- Safari
0x7Fdelete और0x80-FFhigh ASCII / Unicode characters को स्वीकार नहीं करता - RFC कहता है कि control character मिलने पर पूरी कुकी ignore की जानी चाहिए, लेकिन Safari प्रतिबंधित character तक के पहले वाला हिस्सा स्वीकार कर लेता है
-- , --value सेट करने पर comma के आसपास के spaces हटाने वाला Safari bug भी देखा गया
- Safari का cookie storage code closed-source
भाषाओं और standard libraries में parsing का अंतर
-
Go
- Go का cookie code server द्वारा
Set-Cookieमें भेजी जाने वाली value पर RFC के wording के अपेक्षाकृत करीब चलता है - वास्तविक उपयोग में आम whitespace और commas स्वीकार करता है, लेकिन double quotes, semicolons और backslashes को नहीं
- अगर उदाहरण
Cookieheader में JSON cookie शामिल हो, तो Go केrequest.Cookies()result में सिर्फcookie1=fooऔरcookie3=barबचते हैं - browser द्वारा स्वीकार की गई
cookie2बिना exception या explicit error के चुपचाप गायब हो जाती है
- Go का cookie code server द्वारा
-
PHP
- PHP में native cookie parsing function नहीं है, इसलिए सटीक अनुमत दायरे पर निश्चित बात कहना कठिन है, लेकिन test results में control character handling असंगत दिखती है
0x00-0x09और0x0Dcarriage return जैसे values काम करते हैं0x10data link escape या0x7Fdelete इस्तेमाल करने पर PHP 400 Bad Request error देता है- Unicode cookie भी test output में दिखाई देती है
-
Python
- Python का
http.cookies.SimpleCookieJSON cookie मिलने पर उसके बाद की cookies की loading चुपचाप रोक देता है - उदाहरण input में output में सिर्फ
cookie1=fooबचता है - अगर कोई subdomain base domain पर problematic cookie set कर सके, तो वह एक कुकी पूरी site की cookie handling बिगाड़ सकती है
- control character handling भी अनियमित है
- कुछ control characters खाली value के रूप में load होते हैं
- value के आगे-पीछे
aaजोड़ने पर control character cookie load नहीं होती
- Python का
-
Ruby
- Ruby का
CGI::Cookie.parseparsing में काफी उदार दिखता है - यह control characters, tab, double quotes, commas, backslashes,
0x7F, Unicode characters स्वीकार करता है, और cookie jar से निकालते समय percent-encoding लागू करता है - यह तरीका cookie दुनिया में लगभग सबसे बेहतर हो सकता है, लेकिन
document.cookieसे set किया गया code percent-encoded reflected value की अपेक्षा नहीं कर सकता
- Ruby का
-
Rust
- Rust default cookie handling नहीं देता, इसलिए लोकप्रिय
cookiecrate के आधार पर जाँच की गई - default settings वाला
cookiecrate सबसे उदार implementations में से एक के करीब है, और pass की गई UTF-8 string को स्वीकार करता दिखता है
- Rust default cookie handling नहीं देता, इसलिए लोकप्रिय
वास्तविक websites पर दिखा असर
- यह समस्या एक test site पर third-party library update को manually verify करते समय मिली
- इसे automated tests में पकड़ना कठिन था
- अगर यह वैसे ही deploy हो जाता, तो बाद में आने वाले visitors broken cookie पाते, और update rollback व cookie deletion से पहले अनजानी errors में फँस सकते थे
- यह समस्या सिर्फ छोटी sites या किसी खास framework तक सीमित नहीं है
- browser console में domain पर इस तरह Unicode cookie सेट करने से कई बड़ी sites टूट सकती हैं
document.cookie="unicodeCookie=🍪; domain=.grayduck.mn; Path=/; SameSite=Lax"
- देखे गए उदाहरण इस प्रकार हैं
- Facebook: error page दिखता है और images भी टूट जाती हैं
- Instagram और Threads: साधारण 500 error आता है
- Netflix:
NSES-500error लौटाता है और help page भी टूट जाता है - Okta: सभी login pages 400 error लौटाते हैं
- WhatsApp: “whatsapp error” दिखता है
- Amazon: अधिकांश हिस्सा काम करता है, लेकिन कुछ features बेतरतीब ढंग से टूटते हैं
- AWS: login console 400 error लौटाकर रुक जाता है
- Apple Support: device list load नहीं कर पाता
- Best Buy: navigation काम करता है, लेकिन search feature काम नहीं करता
- eBay: अधिकांश हिस्सा ठीक किया जा चुका है, लेकिन कुछ भाग अब भी 400 error देते हैं
- Home Depot: fix की योजना है
- Intuit: error का कारण पहचानने वाली एकमात्र site
- Outlook: 400 error का एक और मामला दिखा
standard और compatibility के बीच fix की कठिनाई
- 30 साल पुराने foundational spec की समस्या को ठीक करना बहुत कठिन है, और संभव है कि इसका कोई अच्छा समाधान ही न हो
- browser side पर ऐसी cookies block करने का तरीका Mozilla और Google दोनों ने देखा और उस पर काम किया
- Mozilla: bug 1797235, CVE-2023-5723, bug 1797231
- Google: bug 40061459
- एकतरफा blocking compatibility समस्याओं की वजह से जटिल है
- non-ASCII cookies पूरी cookie population का 0.01% से भी कम हैं, इसलिए बहुत आम नहीं हैं
- telemetry बताती है कि Argentina, Mexico, Finland जैसे देशों में ये काफी अधिक बार दिखती हैं
- Mozilla ने जल्दी enable की जा सकने वाली
network.cookie.blockUnicodesetting लागू की, लेकिन Chromium के साथ behavior compatibility समस्या के कारण इसे enable नहीं किया
- server side fixes भी संभव हो सकती हैं, लेकिन मामला लाखों websites और भाषाओं·frameworks के अंदरूनी error handling तक फैला है
- Facebook या Netflix जैसे स्थान mitigation कर सकते हैं, लेकिन औसत site operator के पास इसे ठीक करने का समय या क्षमता होना कठिन है
- मूल समाधान यह है कि IETF HTTP Working Group cookie spec को अंदर से align करे और cookie handling systems को कैसे काम करना चाहिए यह सख्ती से तय करे
- non-ASCII characters की अनुमति server side और user agent दोनों में एक जैसी होनी चाहिए
- browser, language और framework cookie को किस चरण में कैसे process करें, यह Content Security Policy जैसे आधुनिक W3C standards की तरह explicit होना चाहिए
- एक गलत कुकी के कारण दूसरी cookies की processing भी रुक जाना, कई तरह के अप्रत्याशित outages ला सकता है, इसलिए इसे स्वीकार करना कठिन है
प्रस्तावित cookie handling प्रक्रिया
field-valueसे शुरू करके;और,पर split करraw-cookie-pairकी सूची बनाई जाए, लेकिन comma को semicolon का synonym न माना जाए- हर
raw-cookie-pairको इस क्रम में process किया जाए- अगर
=नहीं है, तो अगले pair पर जाएँ - आगे-पीछे की whitespace हटाएँ
- पहले
=से पहले वाले हिस्से कोcookie-name-octetsऔर बाद वाले हिस्से कोcookie-value-octetsमाना जाए - अगर value double quote से शुरू होती है, तो शुरुआती double quote एक हटाएँ, और अंत में double quote हो तो एक हटाएँ
- अगर name या value server द्वारा स्वीकार्य form में नहीं है, तो उस pair को skip करें
- बचे हुए
[cookie-name-octets, cookie-value-octets]tuple को server-defined तरीके से process करें
- अगर
- server के लिए अतिरिक्त रूप से यह सुझाव है कि वह उन tuples को reject करे जिनका cookie name token न हो, और cookie value में
cookie-octetके बाहर के octets हों
1 टिप्पणियां
Hacker News की राय
कुकीज़ अजीब जालों और असुविधाजनक व्यवहारों से भरी हैं, लेकिन 99.95% मामलों में ठीक चलती हैं। मेरी पसंदीदा कुकी माइनफ़ील्ड cookie shadowing है: अगर एक ही नाम से, लेकिन domain/path जैसे मुख्य attributes अलग रखकर कुकी सेट करें, तो लगभग एक जैसी कई कुकीज़ एक साथ बन जाती हैं, और backend या JS में यह पहचानने का कोई तरीका नहीं होता कि कौन सी कौन है
https://example.com/somepath पर जाकर browser console में नीचे टाइप करके देख सकते हैं
document.cookie = "foo=a";document.cookie = "foo=b; domain=.example.com";document.cookie = "foo=c; path=/somepath";document.cookieमेरे मामले में नतीजा
'foo=c; foo=a; foo=b'थासच में बहुत बड़ी गलती है
/somepathपर हैं, तो तीनों values में सबसे specific value C मिलना काफ़ी तर्कसंगत लगता है। सभी values क्रम में लौटती हैं, इसलिए path-specific value और global value दोनों पता चल जाते हैं; यह सबसे अच्छा समझौता जैसा लगता हैहालांकि जादुई
document.cookiesetter मुझे पसंद नहीं है, लेकिन यह लगभग 30 साल पुरानी चीज़ है, इसलिए क्या किया जा सकता हैहाल में jshttp/cookie में validation कड़ा करने पर यह समस्या फिर सामने आई: https://github.com/jshttp/cookie/pull/167
उस PR के बाद validation को लेख में बताए गए browser code जैसा फिर थोड़ा नरम कर दिया गया
मूल बदलाव हमारे code में एक bug खोजने से शुरू हुआ था, जहाँ बिना encoding के strings जोड़कर cookie header बनाया जा रहा था। कभी-कभी value में whitespace आ जाता था और request टूट जाती थी। इससे बचने के लिए हम developers को jshttp/cookie का
serialize()इस्तेमाल करने का सुझाव देना चाहते थे, लेकिन पता चला कि उस function का validation हमारे देखे bug को पकड़ने के लिए पर्याप्त नहीं थाजब fix प्रस्तावित किया गया, तो किसी और ने पाया कि validation इतना ढीला है कि cookie के name field में JS घुसाया जा सकता है, और कहीं और वह value की तरह interpret हो सकता है। यह काफ़ी अनोखा code injection path बन गया
लेख में Rust approach का ज़िक्र है, लेकिन दूसरी languages के उलट Rust standard library में cookie handling functionality शामिल नहीं है। असल में आप third-party
cookiecrate का व्यवहार देख रहे होते हैं, और इसमें Ruby की तरह percent encoding करने का option भी है: https://docs.rs/cookie/0.18.1/cookie/HTTP protocol के अंदर मानो असल में दस हज़ार अलग-अलग protocols घुसे हुए हैं। browsers और web servers ने हर तरह की capabilities जोड़ दीं, और हर एक की specification और de facto specification है, और यह सब लगभग एक generic HTTP umbrella के नीचे भेजा जाता है
client यह specify भी नहीं कर सकता कि वह इन दस हज़ार गैर-spec versions में से किसके साथ compatible है, और server भी नहीं। specification को upgrade न कर पाने की वजह यह है कि बाकी clients उसे समझ नहीं पाएँगे, और backward compatibility भी नहीं है
इसलिए ऐसा random chaos बचा है जिस पर कोई सहमत नहीं हो सकता और जिसे सुधारा भी नहीं जा सकता। planned deprecation भी नहीं है, इसलिए अतीत के खराब फैसलों को घसीटते रहना पड़ता है
लगभग 10 साल पहले एक project में cookie-based session implement किया था, और authentication Safari में चलता था लेकिन Chrome में नहीं, यह debug करने में बहुत परेशानी हुई। ठीक कौन सा browser था याद नहीं, लेकिन एक browser format सही न होने पर cookie सेट ही नहीं करता था
हमने कोई खास अजीब काम भी नहीं किया था; याद के मुताबिक शायद
-और_का फर्क थाSet-Cookieheader भी हो सकता हैपहले इस issue की वजह से cookie key में
camelCaseइस्तेमाल नहीं कर पाया थाsearch करने पर भी exact issue ठीक से नहीं मिल रहा
cookies के introduce होने के तुरंत बाद से ही शायद समझदारी भरा उपयोग यही माना गया कि उनमें सिर्फ़ opaque token रखा जाए, जिससे server अगली बार उसी client को पहचान सके, और बाकी सब server side पर store किया जाए
मुझे समझ नहीं आता कि client सैद्धांतिक रूप से ऐसे values handle कर सकता है जिन्हें server कभी भेजेगा ही नहीं, यह समस्या क्यों है। बस ऐसी values मत भेजिए, और “अगर वह भेज दिया तो क्या होगा?” जैसी पहेलियों की चिंता करने की ज़रूरत नहीं
फिर भी opaque token store करने की यही एकमात्र जगह है, इसलिए authentication के लिए इसका इस्तेमाल करना पड़ता है
Cookie header parsing पूरी तरह अव्यवस्थित है। “standard” असल दुनिया में मौजूद व्यवहार को ठीक से नहीं दर्शाता, backend servers, libraries और frameworks अलग-अलग formats स्वीकार करते हैं, और browsers फिर कुछ और ही करते हैं
अगर frontend और backend पूरी तरह आपके control में हों तो यह बड़ी समस्या नहीं है, लेकिन जैसे ही अलग-अलग चीज़ों को integrate करना पड़ता है, स्थिति बहुत जल्दी बेहद मूर्खतापूर्ण बन जाती है
Cookies किसी बड़े और जटिल गड़बड़झाले जैसी दिखती हैं, और साथ ही backward compatibility की वजह से उन्हें बदलना लगभग असंभव भी है। ऐसे में शायद पूरी तरह अलग कोई नया mechanism बनाना ही सही होगा
उदाहरण के लिए NewCookie जैसा कोई mechanism नए सिरे से specify किया जा सकता है, और शुरुआत से ही consistent behavior के लिए फिर से design किया जा सकता है। Modern security measures built-in हों, अधिक सख्त specification हो और proper Unicode support भी जोड़ा जा सके
Set-Cookie2header मौजूद है: https://stackoverflow.com/q/9462180/3474615कम से कम कुछ use cases में ऐसा है, हालांकि यह headers के साथ सीधे integrate नहीं होता
चूंकि cookies पहले से मौजूद हैं, इसलिए हम cookies से बंधे हुए हैं
iOS Safari द्वारा customers के control वाले domains की cookies को मनमाने ढंग से खा जाने की समस्या का पीछा करते हुए मैंने पूरा एक महीना बिताया। Google, Twitter, Facebook जैसे domains पर session state को इस तरह गायब होते कभी नहीं देखा
थोड़ा और गंभीरता से कहें तो, cookie शब्द से बचना और पूरी तरह अलग नाम रखना बेहतर होगा। cookie शब्द के साथ बहुत ज़्यादा baggage जुड़ा हुआ है
लेखक ने शुरुआत
JSON.stringifyके result को cookie में डालने से की थी, लेकिन हैरानी की बात यह थी कि वजह यह नहीं थी कि किसी ने stringify हो रहे JSON के अंदर semicolon डाल दिया थाcookies के आसपास की ज़्यादातर परेशानियां तब पैदा होती लगती हैं जब arbitrary user input को cookie में डालने की कोशिश की जाती है। ऐसा नहीं करना चाहिए। authentication tokens में इस्तेमाल होने वाली fixed-length alphanumeric ASCII string जैसी चीज़ों तक सीमित रहें तो ठीक है
मैं सहमत हूं कि यह काफ़ी minefield है
developer के तौर पर workaround यह है कि values को URL-safe Base64 में encode किया जाए। इससे raw byte value मिलती है और internal representation आप अपनी मर्ज़ी से इस्तेमाल कर सकते हैं। हालांकि लेख में भी कहा गया है, इस पर 100% control नहीं हो सकता। user agent है, इसलिए होना भी यही चाहिए
काश ज़्यादा user agents “wire पर bytes और दुआ” की बजाय standards compliance चुनते। screenshot में 400 responses specification के मुताबिक सही responses हैं। बेहतर होता अगर headers शुरू से UTF-8 होते, या पहले ASCII होते और बाद में UTF-8 allow कर दिया जाता। हालांकि पहला causal reasons से मुश्किल था, और दूसरा भी उन values को legal बना देता जो originally illegal थीं, इसलिए फिर भी problems पैदा कर सकता है
base64urlencoding,base64पर URL encoding जोड़ने से लगभग 3% cases में compatible नहीं होती; development के दौरान यह आसानी से छूट जाता है, लेकिन production में पक्का फटेगा=,/,+characters डाले जा सकते हैं, इसलिए standard Base64 encoding भी इस्तेमाल की जा सकती है :)लेख Postel's law का मज़ाक उड़ाता है, लेकिन अगर cookie set करने वाली side भेजते समय conservative होती, तो ऐसे लेख की ज़रूरत ही नहीं पड़ती
कभी-कभी वे mines सिर्फ bugs नहीं बल्कि बड़े security holes भी बन जाते हैं
अगर client specification के अनुरूप data नहीं भेजता, तो वह bug है और उसे fix किया जाना चाहिए। server द्वारा intent guess करके accept करना कभी भी normal नहीं बनना चाहिए