Base64 और URL एन्कोडिंग: क्यों ज़रूरी और कब ग़लत इस्तेमाल
सारांश (TL;DR)
echo -n "Hi" | base64 ठीक चार अक्षर देता है (SGk=)। इनपुट के 2 बाइट (16 बिट) को 6-बिट इकाइयों में काटकर आउटपुट 4 अक्षर बनते हैं, और अंतिम स्थान एक पैडिंग = से भरता है — बिट अंकगणित वहीं ख़त्म। यह सरल नियम जैसे ही “सुरक्षा के लिए सब कुछ लपेट दें” में फैलता है, लागत आ जाती है। इनपुट के हर 3 बाइट पर आउटपुट 4 अक्षर — मतलब ठीक लगभग 33% की फूलन। एक प्रोडक्शन ट्रैफ़िक में 1.2 MB की JPEG Base64 में लपेटने पर लगभग 1.64 MB दर्ज हुई। एन्कोडिंग न तो एन्क्रिप्शन है, न कंप्रेशन — यह बस “एक अक्षर-सेट वाले डेटा को ऐसे चैनल से गुज़रने लायक़ बनाने का नियम है जो उसे सीधे नहीं झेल पाता”। Base64 (RFC 4648 §4) ईमेल बॉडी, JSON स्ट्रिंग फ़ील्ड, इनलाइन इमेज जैसे टेक्स्ट-ओनली चैनल के लिए है, और Base64URL (उसी RFC का §5) दो अक्षर बदलता है (+→-, /→_) ताकि मान URL, फ़ाइल नाम और JWT में सुरक्षित रूप से बैठ सकें। परसेंट एन्कोडिंग (RFC 3986) अलग टूल है, जो URL सिंटैक्स में अर्थ रखने वाले ASCII अक्षरों (?, &, #, स्पेस, नॉन-ASCII) को %XX में लपेटकर यह संकेत देता है कि यह डेटा है। चैनल के हिसाब से चुनें। बाइनरी समर्थित है तो सीधे भेजें; टेक्स्ट चैनल है तो Base64; URL संदर्भ है तो Base64URL; URL के अंदर है तो परसेंट एन्कोडिंग। और याद रखें — अल्फ़ाबेट सार्वजनिक है, इसलिए उजागर Base64 स्ट्रिंग को कोई भी डिकोड कर सकता है। इसे गोपनीयता का साधन मत मानें।
पृष्ठभूमि
टेक्स्ट-ओनली चैनल इमेज से बहुत पहले से मौजूद हैं। ईमेल ऐतिहासिक रूप से 7-बिट ASCII मानता था और उच्च-बिट बाइट्स को कभी-कभी बिगाड़ देता था, HTTP हेडर आज भी कुछ अक्षर निषिद्ध रखते हैं, और JSON स्ट्रिंग फ़ील्ड UTF-8 हैं पर कच्चे कंट्रोल बाइट्स (0x00–0x1F) सुरक्षित नहीं रख सकते। Base64, जो RFC 4648 में मानकीकृत है, 3 बाइट (24 बिट) इनपुट को 6 बिट की चार इकाइयों में बाँटकर चार आउटपुट अक्षरों में मैप कर देता है। अल्फ़ाबेट 64 अक्षर है: A-Z (0–25), a-z (26–51), 0-9 (52–61), + (62), / (63), और लंबाई को 4 का गुणज बनाने वाला पैडिंग =।
URL-safe वेरिएंट बस इंडेक्स 62 और 63 बदलता है — +→-, /→_। पुराने फ़ॉर्म सबमिशन में + को स्पेस के रूप में पार्स किया जाता है और / पथ विभाजक है। URL-safe संदर्भों में = पैडिंग अक्सर छोड़ी भी जाती है — JWT (RFC 7519) यही तरीक़ा इस्तेमाल करता है ताकि हेडर.पेलोड.सिग्नेचर Authorization: Bearer ... हेडर में कॉम्पैक्ट रूप से बैठे।
परसेंट एन्कोडिंग (अक्सर URL एन्कोडिंग) अलग है। RFC 3986 ASCII को दो समूहों में बाँटता है: अनारक्षित अक्षर (A-Z, a-z, 0-9, -, ., _, ~) जो सीधे इस्तेमाल हो सकते हैं, और आरक्षित अक्षर (:, /, ?, #, [, ], @, !, $, &, ', (, ), *, +, ,, ;, =) जिनका डेटा के रूप में उपयोग करने पर एन्कोडिंग चाहिए। इस समूह के बाहर हर बाइट — UTF-8 के नॉन-ASCII बाइट्स सहित — हेक्स में %XX के रूप में लिखा जाता है। JavaScript का encodeURIComponent आरक्षित अक्षरों को भी आक्रामक तरीक़े से एन्कोड करता है ताकि मान एक कंपोनेंट में सुरक्षित बैठे, जबकि encodeURI पूरे URL को मानकर ज़्यादा क्षमाशील है।
तीनों एन्कोडिंग आपस में अदला-बदली नहीं हैं। परसेंट-एन्कोडेड क्वेरी पैरामीटर अभी भी ASCII अक्षरों से ASCII अक्षर दर्शाता है, इसलिए उसे Base64 डिकोडर में डालने पर व्यर्थ बाइट्स निकलेंगे। मानक Base64 स्ट्रिंग को सीधे URL पथ में चिपका दिया तो अंदर के / के कारण पथ ग़लत जगह कटेगा — मेरे एक सहकर्मी ने एक पूरी दोपहर इसी एक अक्षर में गँवाई।
तुलना और डेटा
| पहलू | Base64 (मानक) | Base64URL | परसेंट एन्कोडिंग |
|---|---|---|---|
| उपयोग | बाइनरी-अस्वीकृत टेक्स्ट चैनल (MIME बॉडी, JSON में बाइट्स) | वही लेकिन URL·फ़ाइल नाम·JWT में | URL के एक कंपोनेंट के आरक्षित·नॉन-ASCII अक्षर |
| अल्फ़ाबेट | A–Z a–z 0–9 + / = | A–Z a–z 0–9 - _ (पैडिंग वैकल्पिक) | अनारक्षित + बाक़ी %XX |
| ओवरहेड | लगभग 33% (3 बाइट → 4 अक्षर), MIME में 76-अक्षर लाइन ब्रेक जुड़ते हैं | लगभग 33%, आमतौर पर बिना पैडिंग | परिवर्तनीय — UTF-8 का 1 बाइट %XX = 3 अक्षर |
| टूटता है | URL में +·/ डालना, सख़्त डिकोडर में पैडिंग गायब | +/=-आधारित मानक डिकोडर में डालना | डबल-एन्कोडिंग (पहले से एन्कोडेड मान को दोबारा एन्कोड) |
| आम उपयोग | MIME अटैचमेंट, data URI, JSON में इनलाइन बाइनरी | JWT, URL-safe ID, छोटे लिंक | क्वेरी पैरामीटर, पथ सेगमेंट, फ़ॉर्म बॉडी |
आँकड़े नज़रअंदाज़ नहीं किए जा सकते। 1 MB इमेज Base64 में लगभग 1.37 MB, और MIME 76-अक्षर लाइन ब्रेक जोड़ने पर 2% और। HTTP रिस्पॉन्स में यह फूलन सर्वर बैंडविड्थ और क्लाइंट पार्सर दोनों पर असर डालती है। परसेंट एन्कोडिंग सामान्य टेक्स्ट पर कम आक्रामक है, लेकिन CJK या देवनागरी टेक्स्ट में बाइट्स काफ़ी बढ़ जाती हैं। “नमस्ते” का प्रत्येक अक्षर UTF-8 में 3 बाइट है, यानी ASCII में 9 बाइट (%XX × 3)।
वास्तविक परिदृश्य
परिदृश्य 1 — ईमेल अटैचमेंट। PDF MIME पार्ट में Content-Transfer-Encoding: base64 के साथ जाता है। मेल क्लाइंट फ़ाइल को Base64 में एन्कोड करता है, 76-अक्षर पर लाइन ब्रेक डालता है और भेज देता है। प्राप्तकर्ता उलटी प्रक्रिया से फ़ाइल बहाल करता है। SMTP की “टेक्स्ट मान्यता” ने Base64 को यहाँ व्यावहारिक डिफ़ॉल्ट बना दिया।
परिदृश्य 2 — JSON Web Token। JWT (RFC 7519) हेडर.पेलोड.सिग्नेचर है, और हर सेगमेंट बिना पैडिंग के Base64URL में है। URL-safe अल्फ़ाबेट के कारण टोकन Authorization हेडर, क्वेरी पैरामीटर और लॉग लाइनों में बिना री-एस्केप बैठ सकता है। एक चेतावनी: JWT को डिकोड करने वाला कोई भी claim पढ़ सकता है। एन्कोडिंग सुरक्षा नहीं है; HMAC/RSA सिग्नेचर सुरक्षा है। पेलोड में गोपनीय जानकारी न डालें।
परिदृश्य 3 — छोटी इमेज के लिए data URI। background-image: url("data:image/png;base64,iVBORw0K...") PNG को सीधे CSS में इनलाइन करता है। छोटे आइकन के लिए राउंड-ट्रिप बचा लेता है, पर 33% ओवरहेड और ब्राउज़र कैशिंग व समानांतर अनुरोधों की हानि के साथ यह केवल तब ठीक है जब एसेट इतना छोटा हो कि HTTP राउंड-ट्रिप ज़्यादा महँगा हो। मेरे अनुभव में ब्रेक-ईवन लगभग 1–2 KB पर है।
परिदृश्य 4 — यूज़र अवतार अपलोड। आम एंटी-पैटर्न है FileReader.readAsDataURL से फ़ाइल पढ़ना, data:image/png;base64,... स्ट्रिंग बनाना, और उसे JSON फ़ील्ड में POST करना। काम करता है पर लगभग ग़लत है। पेलोड 33% बढ़ता है, सर्वर को डिस्क पर लिखने से पहले फिर डिकोड करना पड़ता है, और मेमोरी क्लाइंट व सर्वर दोनों पर खाती है। एक मामले में मैंने देखा कि 5 MB इमेज 6.7 MB JSON बन जाती थी और मोबाइल लाइन पर टाइमआउट हो जाती थी — multipart/form-data में बदलते ही 5 MB जैसे-की-तैसे अपलोड हुई।
आम ग़लतफ़हमियाँ
“Base64 एन्क्रिप्शन है।” नहीं है। मैपिंग RFC 4648 में खुली है, अल्फ़ाबेट स्थिर है, और कोई भी डिकोडर मूल बाइट्स लौटा देता है। एन्कोडिंग संचरण की रक्षा करती है, सामग्री की नहीं।
“URL एन्कोडिंग केवल नॉन-ASCII के लिए चाहिए।” ASCII के आरक्षित अक्षरों को डेटा के रूप में इस्तेमाल करते समय भी एन्कोडिंग चाहिए। & वैसे ही छोड़ दिया तो सर्वर नया पैरामीटर मान लेगा, # छोड़ा तो उसके बाद सब फ़्रेग्मेंट में चला जाएगा।
“HTTP पर जाते समय हर चीज़ Base64 करना सुरक्षित है।” HTTP बाइनरी बॉडी ख़ुशी से भेजता है — Content-Type: application/octet-stream, chunked transfer, कोई भी बाइट वैल्यू। Base64 उन चैनलों के लिए है जो ऐसा नहीं कर सकते। पहले से बाइट-सक्षम चैनल पर 33% ओवरहेड शुद्ध कर है।
“Base64 और Base64URL आपस में बदले जा सकते हैं।” नहीं। +/ की उम्मीद करने वाले सख़्त Base64 डिकोडर में URL-safe स्ट्रिंग डालने पर विफलता या ख़राब आउटपुट मिलेगा।
चेकलिस्ट
- चैनल 7-बिट-ओनली या स्ट्रक्चर्ड टेक्स्ट है? ईमेल बॉडी, JSON में बाइट्स, CSS
data:URI → Base64। - मान URL, फ़ाइल नाम या JWT में जा रहा है? Base64URL; पैडिंग की अनुमति उपभोक्ता के अनुसार।
- मान ख़ुद URL कंपोनेंट है? केवल आवश्यक हिस्से पर परसेंट एन्कोडिंग।
- डेटा बड़ा है? पहले यह जाँचें कि चैनल सचमुच टेक्स्ट माँगता है। कच्चे बाइनरी बॉडी से 33% बच सकता है।
- डिकोडर सख़्त है या क्षमाशील? एन्कोडर और डिकोडर को एक-दूसरे से मैच रखें।
- गोपनीयता चाहिए? पहले एन्क्रिप्ट करें। केवल एन्कोडिंग कभी रहस्य नहीं बनती।
संबंधित टूल
Patrache Studio का Base64 एन्कोडर·डिकोडर मानक और URL-safe दोनों वेरिएंट लोकल प्रोसेस करता है, इसलिए इनपुट बाइट्स ब्राउज़र नहीं छोड़ते। अगर पेलोड JWT है तो JSON फ़ॉर्मैटिंग·वैलिडेशन·JSON Schema में डिकोडेड claim की साफ़ जाँच का तरीक़ा देखें। Base64URL में एन्कोडेड UUID-व्युत्पन्न छोटे ID पर UUID v1·v4·v7 तुलना और DB प्राइमरी-की डिज़ाइन उपयोगी है।
संदर्भ
- IETF RFC 4648, “The Base16, Base32, and Base64 Data Encodings” — https://datatracker.ietf.org/doc/html/rfc4648
- IETF RFC 3986, “Uniform Resource Identifier (URI): Generic Syntax” — https://datatracker.ietf.org/doc/html/rfc3986
- MDN, “Base64” glossary — https://developer.mozilla.org/en-US/docs/Glossary/Base64
- MDN, “encodeURIComponent()” — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent