Bash में Base64 डिकोडिंग: एक सम्पूर्ण गाइड
कभी-कभी अक्षरों, अंकों और बीच-बीच में +, / या = वाली एक स्ट्रिंग आपके टर्मिनल में आ गिरती है, और आपको असली चीज़ वापस चाहिए। टिकट में पेस्ट किया हुआ JWT, सपोर्ट ईमेल में अटैच की गई .b64 फ़ाइल, अक्षरों की सूप जैसा पढ़ने वाला Kubernetes सीक्रेट, या HTML data: टैग के अंदर छिपी PNG। यह वह फील्ड गाइड है जो आपको बताती है कि Bash और शेल में बाइट्स वापस कैसे निकालें, ऐसे टूलों के साथ जो आपके पास लगभग निश्चित रूप से पहले से इंस्टॉल हैं।
फ़ॉर्मेट एक साँस में: Base64 राव डेटा के हर तीन बाइट्स को 64 अक्षरों की वर्णमाला (A-Z, a-z, 0-9, साथ में + और /) से चार चरों के रूप में फिर से लिख देता है, और जब बाइट्स की संख्या तीन का गुणज न हो तो आख़िर में एक या दो = साइन जोड़ देता है। डिकोडिंग इस सौदे की सिकुड़ने वाली दिशा है: चार चर अंदर जाते हैं, तीन बाइट्स बाहर आते हैं। इस साइट का होम पेज फ़ॉर्मेट को क़दम-दर-क़दम समझाता है, इसलिए यह लेख अपना वक़्त वहीँ लगाता है जहाँ उसकी जगह है, काम की शेल वाली तरफ़।
यहाँ प्लॉट ट्विस्ट है: base64 कमांड का कोई एक रूप नहीं है। यह नाम एक GNU C प्रोग्राम, एक Rust रीराइट, एक BusyBox अपलेट, एक BSD की पुरानी विरासत, और एक OpenSSL यूटिलिटी साझा करते हैं, जिनके फ़्लैग एक वाक़ई ख़तरनाक ढंग से ओवरलैप करते हैं। सभी वर्णमाला पर तो सहमत हैं, लेकिन ख़राब इनपुट का चेहरा कैसा है, इसमें हमेशा नहीं; और यही फ़र्क है जहाँ स्क्रिप्ट्स मर जाते हैं। तो पहला क़दम यह पता करना है कि जब आप base64 टाइप करते हैं, तो जवाब देने वाला कौन है।
जानिए आप किस डिकोडर से बात कर रहे हैं
सिर्फ़ एक कमांड आपको कहानी का ज़्यादातर हिस्सा सुना देता है:
base64 --version
मशीन के हिसाब से आप इनमें से किसी एक से निपट रहे हैं:
| आपको क्या दिखता है | आपके पास क्या है | डिकोड फ़्लैग |
|---|---|---|
base64 (GNU coreutils) 9.x |
क्लासिक C इम्प्लीमेंटेशन, जो अधिकांश Linux डिस्ट्रीब्यूशन पर आज भी डिफ़ॉल्ट है | -d या --decode |
base64 (uutils coreutils) 0.8.x |
coreutils का Rust रीराइट, जो मौजूदा Ubuntu रिलीज़ का डिफ़ॉल्ट यूज़रलैंड है | -d (और असामान्य रूप से, -D भी काम करता है) |
| BSD-शैली का उपयोग-टेक्स्ट, कोई वर्ज़न फ़्लैग नहीं | macOS और BSDs पर वाला BSD base64, जो पुराने bintrans टूल की संतान है |
-D (इस परिवार में छोटे अक्षर वाला -d डीबग का मतलब रखता है, डिकोड का नहीं) |
BusyBox v1.x |
Alpine Linux और एम्बेडेड सिस्टम की सब-में-एक बिनरी | -d |
उस टेबल से क्या गुम है, नोटिस करें: OpenSSL। openssl base64 बिल्कुल अलग जंतु है, और उसकी -d फ़्लैग का मतलब डिक्रिप्ट है, डिकोड का नहीं। इस लेख की किसी भी अन्य आदत से ज़्यादा ख़ामोशी से खाली आउटपुट फ़ाइलें इसी एक फ़्लैग की वजह से बनती हैं, इसलिए हम फॉलबैक सेक्शन में उससे सही तरीके से मिलेंगे।
अगर आपकी डिस्ट्रीब्यूशन एक से ज़्यादा परिवारों को साथ-साथ देती है (मौजूदा Ubuntu ऐसा ही करती है), तो दो-चार और कमांड आपको पूरा चित्र दिखाएँगे:
command -v base64
base64 --version 2>&1 | head -1
चार वन-लाइनर जो ज़्यादातर दिन काफ़ी हैं
स्टैंडर्ड इनपुट से स्ट्रिंग डिकोड करें। यह वही चाल है जिसे आप हज़ारों बार चलाएँगे, और यहाँ printf शेल को आपके पेलोड को सजाने से रोकता है:
printf '%s' "SGVsbG8sIFdvcmxkIQ==" | base64 -d
फ़ाइल डिकोड करें। हर सही मायने में गंभीर इम्प्लीमेंटेशन FILE आर्गुमेंट मानती है, और यह डेटा को शेल की क्वोटिंग की मशीन से दूर रखने का सबसे साफ़ तरीका है:
base64 -d payload.b64 > payload.bin
here-स्ट्रिंग से डिकोड करें। here-स्ट्रिंग आख़िर में एक नई लाइन जोड़ देती है, लेकिन हर डिकोडर नई लाइन को नज़रअंदाज़ की जा सकने वाली व्हिटेसपेस मानता है, इसलिए छोटे ब्लॉब के लिए यह बिल्कुल सुरक्षित है:
base64 -d <<< "SGVsbG8sIFdvcmxkIQ=="
रैप की गई, मल्टी-लाइन ब्लॉब को heredoc से डिकोड करें। डिलीमीटर को सिंगल क्वोट में रोकने से शेल अंदर की कोई भी चीज़ इंटरप्रेट नहीं करता:
base64 -d <<'EOF'
SGVs
bG8s
IFdv
cmxk
IQ==
EOF
चारों Hello, World! प्रिंट करते हैं। macOS और BSDs पर, ऊपर हर उदाहरण में -d की जगह -D लगाएँ; बाकी सिंटैक्स बिल्कुल वही है।
गंदा इनपुट ही सामान्य बात है
बाहर की दुनिया में मिलने वाला Base64 दुर्लभ ही एक साफ़ लाइन होता है। यह 76 चर पर रैप होकर आता है (MIME का नियम) या 64 पर (PEM का नियम), Windows से CRLF लाइन एंडिंग्स के साथ एक्सपोर्ट हुआ होता है, या किसी चैट विंडो से बीच-बीच में बिखरे हुए स्पेस के साथ कॉपी किया हुआ। अच्छी ख़बर: डिकोडर को फ़र्फ़र नहीं कि लाइन ब्रेक्स कहाँ हैं, बशर्ते वे असली नई लाइन्स हों।
हर रैपिंग शैली का सार्वभौमिक इलाज है: डिकोड करने से पहले लाइन ब्रेक्स धो डालें:
tr -d '\r\n' < blob.b64 | base64 -d
कैरियाज रिटर्न खास केस है। नई लाइन मंज़ूर इनपुट है, लेकिन सख़्त डिकोडरों के लिए \r नई लाइन नहीं है। ऐसा ब्लॉब जिसने किसी Windows सिस्टम का पार किया हो, GNU डिकोडर को पटरी से उतार देगा, जो आधा नतीजा प्रिंट करके फेल हो जाता है; इलाज है कि सबसे पहले कैरियाज रिटर्न हटाएँ:
printf 'SGVs\r\nbG8s\r\n' | tr -d '\r' | base64 -d
अगर आपको Hel जैसे टुकड़ों के बाद एरर दिखे, तो आप CRLF ब्लॉब पर खड़े हैं। वही धुलाई, tr -d '\r\n', डिकोड करने से पहले - यह हर ऐसे इनपुट के लिए पोर्टेबल आदत है जो आपने खुद नहीं बनाया।
वाक़ई ख़राब इनपुट के लिए GNU और uutils -i (--ignore-garbage) फ़्लैग देते हैं, जो वर्णमाला के बाहर के चर छोड़ देता है और जो-जो डिकोड कर सके डिकोड करता है:
printf 'SGVs!bG8' | base64 -di
यह Hello प्रिंट करता है। -i को अपनी डिफ़ॉल्ट आदत बनाने से पहले, जानिए कि स्टैंडर्ड इसके विरुद्ध चेतावनी क्यों देता है: RFC 4648, सेक्शन 3.3, कहता है कि इम्प्लीमेंटेशन को वर्णमाला के बाहर के चरों वाला डेटा रद्द करना ही होगा, क्योंकि नज़रअंदाज़ किए गए चर एक छिपी हुई चैनल के तौर पर इस्तेमाल हो सकते हैं, जिससे ऐसा डेटा ग़ुपचुप पार कर जाता है जो कभी डिकोडेड आउटपुट में नहीं दिखता। -i का सहारा तब लें जब दस्तावेज़ से पेस्ट की गई चीज़ के साथ पंक्तिचिह्न भी आ गए हों, तब नहीं जब आप ऐसी डेटा की जाँच कर रहे हों जो आप भरोसेमंद मानते हैं।
देखिए तीनों मुख्य डिकोडर किनारों पर, 2026 के टूलिंग पर (uutils 0.8.x, GNU coreutils 9.7, BusyBox 1.37), वाक़ई कैसा व्यवहार करते हैं:
| इनपुट | uutils 0.8.x | GNU 9.7 | BusyBox 1.37 |
|---|---|---|---|
SGVsbG8sIFdvcmxkIQ==, साफ़ |
Hello, World!, exit 0 |
Hello, World!, exit 0 |
Hello, World!, exit 0 |
SGVsbG8s, आठ चर, बिना पैडिंग के |
Hello,, exit 0 |
Hello,, exit 0 |
Hello,, exit 0 |
Zg, दो चर, बिना पैडिंग के |
f, exit 0 |
f, exit 0 |
त्रुटि, "truncated input" |
SGV, तीन चर, बिना पैडिंग के |
त्रुटि, कोई आउटपुट नहीं | He प्रिंट हुआ, फिर त्रुटि |
त्रुटि, "truncated input" |
| CRLF-रैप की गई लाइन्स | डिकोड ठीक से होता है, exit 0 | आधा आउटपुट, फिर त्रुटि | डिकोड ठीक से होता है, exit 0 |
SGVs!bG8, बिखरे हुए पंक्तिचिह्न |
त्रुटि (-i के साथ: Hello) |
आधा आउटपुट (-i के साथ: Hello) |
आधा आउटपुट (-i फ़्लैग ही नहीं है) |
SGV=, नॉन-कैनोनिकल स्पेयर बिट्स |
त्रुटि, कोई आउटपुट नहीं | He प्रिंट हुआ, फिर त्रुटि |
He, exit 0 |
TQ==junk, पैडिंग के बाद कचरा |
exit 0, junk डिकोड करते रहता है |
exit 0, junk डिकोड करते रहता है |
M प्रिंट हुआ, फिर "truncated input" (exit 1) |
उस टेबल से तीन बातें ले चलें। पहली: बिना पैडिंग वाले टेल के लिए कोई सार्वभौमिक नियम नहीं है: BusyBox चाहता है कि लंबाई चार का गुणज हो, जबकि GNU और uutils क़ानूनी बाकी हिस्से तो मान लेते हैं, लेकिन बशर्ते बचे हुए बिट्स सभी शून्य हों (इसीलिए Zg पास हो जाता है और SGV नहीं)। दूसरी: GNU और BusyBox उससे पहले जो बाइट्स डिकोड कर चुके होते हैं, उन्हें लिख देते हैं, तब फेल होते हैं, इसलिए कोई स्क्रिप्ट जो फ़ाइल में रीडायरेक्ट करके बाद में एग्ज़िट कोड जाँचती है, आसानी से आधा-डिकोडेड फ़ाइल ही संभाल लेगी। हमेशा एग्ज़िट स्टेटस जाँचें, और किसी फेल हुई डिकोडिंग की छोड़ी गई हर फ़ाइल को शक के नज़रिए से देखें। तीसरी: वह आख़िरी पंक्ति: uutils और GNU junk को == के बाद भी डिकोड करते रहते हैं और exit 0 पर आ जाते हैं, क्योंकि उन्हें बताने वाला कोई नहीं कि स्ट्रीम को खत्म होना चाहिए था; BusyBox ही अपवाद है, जो पैड से पहले का वह एक बाइट प्रिंट करके अधूरा-इनपुट त्रुटि से फेल हो जाता है। अगर आपको आख़िरी कचरा मायने रखता है, तो आउटपुट पर भरोसा करने से पहले इनपुट के शक्ल की जाँच कर लें।
base64url: टोकन और URL की वर्णमाला
RFC 4648 का सेक्शन 5 दूसरी भाषा की परिभाषा देता है: वही 6-बिट गणित, लेकिन अब - और _ हैं जहाँ पहले + और / थे, और पैडिंग हटा दी गई, क्योंकि URL को बस सटीक बाइट लंबाई की घोषणा करने की ज़रूरत नहीं होती। RFC इस बारे में तीखा है: इस एन्कोडिंग को "base64 एन्कोडिंग के समान माना नहीं जाना चाहिए"। अगर आपने कभी भी JWT को देखा है, तो आप इस भाषा से पहले ही मिल चुके हैं, क्योंकि उसके सेगमेंट्स बिना पैडिंग के base64url होते हैं।
शेल की रेसिपी एक दो-कदमी स्वैप है: URL-safe चरों को वापस अपने स्टैंडर्ड रिश्तेदारों में बदलें, फिर डिकोड करें:
printf '%s' "_k-C" | tr '_-' '/+' | base64 -d | xxd -p
यह fe4f82 लौटाता है, तीन राव बाइट्स जो बस URL के कपड़े पहने हुए थे। स्वैप पोज़ीशनल है, इसलिए दिशा मायने रखती है: एन्कोडिंग tr '+/' '-_' की दिशा में जाता है, डिकोडिंग tr '_-' '/+' की दिशा में। दोनों को उलट-पुलट कर लेने पर त्रुटि नहीं आता; यह बस ख़ामोशी से अलग बाइट्स बना देता है, जो बग की सबसे बुरी किस्म है।
अब वह फ़र्सा जो base64url को सादे Base64 की तरह मानने वालों को पकड़ लेता है। इस भाषा में पैडिंग वैकल्पिक है, और लंबाई जिसकी लंबाई चार से भाग देने पर तीन बाकी छोड़ती है, वही शक्ल है जिसे सख़्त डिकोडर जाँच-पड़ताल करते हैं। मज़बूत चाल है कि सबसे पहले गायब = चरों को बहाल किया जाए, जो एक छोटा फ़ंक्शन है:
b64url_decode () {
local s=$1
local n
n=$(printf '%s' "$s" | wc -c | tr -d '[:space:]')
while [ $(( n % 4 )) -ne 0 ]; do
s="${s}="
n=$(( n + 1 ))
done
printf '%s' "$s" | tr '_-' '/+' | base64 -d
}
b64url_decode "eyJzdWIiOiJob21lciJ9"
आख़िरी लाइन {"sub":"homer"} प्रिंट करता है, एक बिना-जड़ा-बूटा JSON ऑब्जैक्ट जो एक वेब सर्वर ने ठीक एक पल पहले साइन या सील किया था। जिनकी लंबाई चार से भाग देने पर एक बाकी छोड़ती है, ऐसे सेगमेंट्स शुरू से ही गलत शक्ल के होते हैं, और किसी भी मात्रा में पैडिंग उन्हें बचा नहीं पाती, इसलिए फ़ंक्शन के इस case को रिजेक्ट करना एक फीचर है।
GNU coreutils में एक नेटिव रास्ता भी है: basenc, जो base64 का बड़ा भाई है और इस भाषा को सीधे समझता है:
printf '%s' "Zg==" | basenc --base64url -d
यह f प्रिंट करता है, दो चरों में छिपा वह एक बाइट। इस पर पाइपलाइन्स बनाने से पहले एक चेतावनी: इस दौर का GNU basenc बिना किसी शिकायत के बिना-पैडिंग base64url इनपुट (सादा Zg) तक डिकोड कर लेता है, जबकि uutils का basenc पहले पैडिंग बहाल करने की माँग करता है, जैसा कि ऊपर का उदाहरण दिखाता है। ऊपर वाला छोटा फ़ंक्शन हर जगह काम करता है, यही वजह है कि यह पोर्टेबल विकल्प है।
JWT खोलना
JSON Web टोकन RFC 7515 के अनुसार तीन डॉट्स से जुड़े base64url सेगमेंट होते हैं। पहले दो सादा JSON होते हैं (हेडर और पेलोड क्लेम्स), इसलिए वे सीधे पठनीय टेक्स्ट में डिकोड हो जाते हैं। तीसरा सिग्नेचर है, एक राव बिनरी डायजेस्ट, इसलिए उसे छूते ही छोड़ दें: उसे डिकोड करने से आपको सिग्नेचर के बाइट्स मिलते हैं, कोई संदेश नहीं।
token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiJ9.abc123"
b64url_decode "$(printf '%s' "$token" | cut -d. -f2)"
यह {"sub":"42"} प्रिंट करता है, सब्जेक्ट क्लेम, बिना किसी सर्वर के शामिल होने के। क्लेम्स पढ़ते समय डिकोड के बारे में साफ़ दिमाग़ रखें कि वह क्या है और क्या नहीं: यह सिर्फ़ खुलासा करता है, वेरिफाई नहीं करता। सिग्नेचर तब तक कुछ नहीं कहता जब तक कि जिस सर्वर के पास वह की है, वह इसे फिर से गणना न करे; यह काम openssl dgst का है (एन्कोडिंग लेख पूरी मिंटिंग प्रक्रिया दिखाता है), इस लेख का नहीं। एक आम फील्ड त्रुटि डिकोड हुए पेलोड को टोकन के वैलिड होने का प्रूफ़ मान लेना है; हमलावर हाथ से बिना-सिग्नेचर या कमज़ोर-सिग्नेचर वाले टोकन बना सकता है, और डिकोडर खुशी-खुशी उन सभी को पढ़ लेगा।
फ़ाइलें, बाइट्स और वेरिएबल की दीवार
डिकोड करने से आपको राव बाइट्स हाथ आते हैं। वे किसी अंग्रेज़ी वाक्य की गढ़ रहे हों, या किसी PNG, शेअर्ड लाइब्रेरी, या zip आर्काइव के बीच के हिस्से हों। शेल स्क्रिप्ट में फ़ाइल ही वही जगह है जहाँ बाइट्स बिल्कुल सुरक्षित रहते हैं, इसलिए डिफ़ॉल्ट वर्कफ़्लो फ़ाइल में डिकोड करना और फिर तुलना है:
base64 -d photo.b64 > photo.png
जब मूल आपके पास बठा हो, तो बाइट-दर-बाइट तुलना ही वही प्रूफ़ है जो मायने रखता है:
cmp photo.png photo.png.orig && echo "byte-for-byte identical"
जब मूल दूर हो, तो चेकसम की तुलना करें:
sha256sum expected.bin
base64 -d blob.b64 | sha256sum
दो मेल खाते हैश और आपकी डिकोड सिद्ध तौर पर सटीक है, जो किसी भी इमेज व्यूअर या हेक्स डमप में आँख से देखने से बेहतर है।
शेल वेरिएबल एक अलग कहानी है, और वे दो कारणों से दीवार बन जाते हैं। कमांड सब्स्टीयूशन $(...) आउटपुट की हर आख़िरी नई लाइन हटा देता है, और वह NUL बाइट बिल्कुल नहीं रख पाता: bash एक चेतावनी प्रिंट करता है और उन्हें ख़ामोशी से गिरा देता है। 41 00 42 का तीन-बाइट पेलोड दोनों समस्याओं को एक ही डेमो में दिखाता है:
printf 'QQBC' | base64 -d > out.bin
xxd out.bin
फ़ाइल तीनों बाइट्स (41 00 42) संभालती है। वेरिएबल वाला रास्ता नहीं संभालता:
v=$(printf 'QQBC' | base64 -d)
printf '%s' "$v" | wc -c
आपको 2 मिलता है, साथ में स्टैंडर्ड एरर पर नज़रअंदाज़ किए गए NUL बाइट के बारे में एक चेतावनी। सीख छोटी है: अगर डेटा बिनरी हो सकती है, तो उसे फ़ाइल में डिकोड करें, xxd से जाँचें, और कभी भी उसे वेरिएबल के रास्ते मत जाने दें।
असली काम में Base64 कहाँ-कहाँ छिपा है
जब डिकोड करना आरामदायक हो जाता है, तो आप हर जगह उस फ़ॉर्मेट को नोटिस करने लगते हैं। यहाँ असली शेल काम के कोने हैं जहाँ वह दिखाई देता है, हर एक के साथ ठीक-ठीक चाल।
Kubernetes सीक्रेट्स। सीक्रेट के .data के नीचे हर फील्ड Base64 होता है, और आधिकारिक दस्तावेज़ इस बात पर बल देते हैं कि यह एन्कोडिंग है, एन्क्रिप्शन नहीं। उसे वापस पढ़ना एक क्लासिक वन-लाइनर है:
kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d; echo
Git binary patches। जब diff किसी बिनरी फ़ाइल को छूता है, तो git diff --binary एक GIT binary patch ब्लॉक निकालता है। यहाँ base64 -d की तरफ़ हाथ मत बढ़ाएँ: उस ब्लॉक की लाइन्स git की अपनी base85-शैली की एन्कोडिंग हैं (हर लाइन एक लंबाई चर से शुरू होती है, A-Z या a-z, जिसके बाद base85 डेटा आता है), Base64 नहीं, और सादा डिकोडर उन पर गला घोंट लेगा। सही टूल फ़ॉर्मेट के मालिक ही हैं:
git diff --binary | grep -a -A2 'GIT binary patch'
diff को git apply या git am में डालें, और उन्हें ही अनपैकिंग करने दें।
Data URIs। HTML या CSS में एम्बेडेड इमेज data:image/png;base64,iVBOR... जैसी दिखती है। कॉमा समेत उससे पहले की सब चीज़ हटाएँ, लाइन ब्रेक्स धोएँ, डिकोड करें, और फ़ाइल आपके हाथ में है:
cut -d, -f2- icon.uri | tr -d '\r\n' | base64 -d > icon.png
PEM कवच। सर्टिफिकेट्स और प्राइवेट कीज़ अपना Base64 फ्रेमिंग लाइन्स में रैप करते हैं, जो Base64 ही नहीं होतीं। कवच वाला ब्लॉक चुनें, दोनों फ्रेमिंग लाइन्स हटाएँ, और राव DER बिनरी में डिकोड करें:
awk '/BEGIN CERTIFICATE/{f=1;next} /END CERTIFICATE/{f=0} f' cert.pem | tr -d '\r\n' | base64 -d > cert.der
CERTIFICATE की जगह PRIVATE KEY या वह कोई भी लेबल रखें जो आपकी फ़ाइल रखती है; शक्ल वही रहती है।
MIME ईमेल। ईमेल का वह हर हिस्सा जिसमें Content-Transfer-Encoding: base64 हो, 76 चर पर CRLF लाइन एंडिंग्स के साथ रैप होता है, क्योंकि RFC 2045 वही आदेश देता है। पोर्टेबल कॉम्बो धुलाई और डिकोड का जोड़ है:
tr -d '\r\n' < attachment.b64 | base64 -d > attachment.bin
क्लिपबोर्ड पेस्ट्स। ब्राउज़र, चैट विंडो, या दस्तावेज़ से कॉपी किया गया टेक्स्ट बिखरे हुए स्पेस और आख़िर की नई लाइन के साथ आता है। स्पेस वर्णमाला के चर नहीं होते, इसलिए सख़्त डिकोडर पेस्ट को रिजेक्ट कर देते हैं; स्टैंडर्ड बचाव है कि सबसे पहले उन्हें हटा दिया जाए:
tr -d ' \r\n' < pasted.b64 | base64 -d
पहले बाइट्स: कैरेक्टर सेट्स और Unicode
Base64 काम में सबसे ज़्यादा दोहराई गई गलती यह है कि फ़ॉर्मेट सिर्फ़ बाइट्स जानता है, और आप चरों में सोच रहे हैं। base64 -d आपको राव बाइट्स देता है, और उनका पठनीय टेक्स्ट बनेगा या नहीं, यह फैसला वही करता है जो अगले उन्हें पढ़ता है। वह फैसला कैरेक्टर सेट है, और वह डिकोड के बाद होता है, उसके अंदर कभी नहीं।
UTF-8 डिफ़ॉल्ट मान है और आमतौर पर सही भी। शब्द café UTF-8 में पाँच बाइट्स का होता है, और डिकोड बिल्कुल सुगम होता है:
printf 'Y2Fmw6k=' | base64 -d | xxd -p
यह 636166c3a9 है: caf और é के लिए दो UTF-8 बाइट्स c3 a9। हालाँकि, पुराने सिस्टम आपको Latin-1 (ISO-8859-1) बाइट्स देंगे, जहाँ वही अक्षर एक ही बाइट e9 है। ऐसे ब्लॉब को डिकोड करके सीधे UTF-8 टर्मिनल पर प्रिंट करने से आपको बिगड़ा हुआ चर मिलता है; इलाज है कि बाइट्स को iconv से फिर से इंटरप्रेट किया जाए, इससे पहले कि कुछ और उन्हें देखे:
base64 -d latin1.b64 | iconv -f ISO-8859-1 -t UTF-8 > utf8.txt
कुछ बाइट-लेवल की बातें जो वाक़ई डीबगिंग का वक़्त बचाती हैं:
- UTF-8 BOM तीन बाइट्स
ef bb bfहोते हैं, जो77u/में एन्कोड होते हैं।/को नोटिस करें, जो URL के लिए दुश्मनी भरा चर है, और base64url बस वही चीज़ ठीक करने के लिए मौजूद है। अगर किसी डिकोड हुई फ़ाइल में "आगे कोई अदृश्य कचरा" लगे, तोxxdसे पहले तीन बाइट्स जाँचें। - 😀 जैसा कोई इमोजी चार UTF-8 बाइट्स का होता है और आठ Base64 चर बन जाता है,
8J+YgA==। वहाँ का+स्टैंडर्ड वर्णमाला को अपना डिज़ाइन के मुताबिक काम करते दिखता है; base64url के कपड़ों में वह8J-YgAबन जाता है। - गलत UTF-8 सीक्वेंस बाइट्स के तौर पर बिल्कुल ठीक से डिकोड हो जाते हैं, और फिर कचरे या रिप्लेसमेंट चर के रूप में दिखते हैं। यह Base64 की हार नहीं है; डिकोड ने अपना काम किया।
xxdयाhexdump -Cदिखाते हैं कि बाइट्स वाक़ई क्या हैं। - आपके टर्मिनल का लोकेल तय करता है कि वह उन बाइट्स को कैसे रेंडर करे। "टर्मिनल कचरा दिखा रहा है" यह डिस्प्ले के बारे में बयान है, डेटा के बारे में नहीं। सफ़र के दौरान बाइट्स नहीं बदले।
बड़े ब्लॉब: स्ट्रीम्स, चंक्स और स्पीड
Base64 एक स्ट्रीम फ़ॉर्मेट है, और डिकोडर एक असली स्ट्रीमिंग पाइप के तौर पर काम करता है: 10 GB की फ़ाइल कभी मेमोरी पर नहीं बैठती; वह बस गुज़रती रहती है। इससे स्केल पर डिकोड की तरफ़ लगभग पूरी तरह बोरिंग हो जाती है, जो बिल्कुल वही है जो चाहिए।
जब कोई बड़ा ब्लॉब ट्रांसफ़र के लिए चंक्स में तोड़ा गया हो (ईमेल की सीमा, टिकट अटैचमेंट का साइज़, IM संदेश), तो उसे फिर जोड़ना बस सही क्रम में cat है, और उसके बाद वही पुरानी धुलाई:
cat part_* | tr -d '\r\n' | base64 -d > big.bin
साइज़्स के लिए एक मानसिक मॉडल रखें: एन्कोडेड रूप मूल से हमेशा एक-तिहाई ज़्यादा बड़ा होता है, तीन बाइट्स पर चार चर। इसलिए जब कोई आपको कहता है कि .b64 फ़ाइल उस चीज़ के बराबर साइज़ में होनी चाहिए जिसे वह छुपाती है, तो वह गलत है, और अब आप उसे बता सकते हैं कि कितना: 300 MB का पेलोड लगभग 400 MB टेक्स्ट के रूप में आता है।
असलियत में स्पीड कोई चिंता नहीं है। ये साधारण मेमोरी पर टेबल-ड्रिवन लूप्स हैं, और एक आधुनिक मशीन पर GNU से 200 MB का ब्लॉब लगभग एक-दसवें सेकंड में डिकोड हो जाता है, और आम इम्प्लीमेंटेशन में सबसे धीमा (BusyBox) भी उससे बस कुछ गुना धीमा है, फिर भी दो सेकंड से आराम से नीचे। इनपुट कितनी भी बड़ी हो, मेमोरी का इस्तेमाल सपाट रहता है, क्योंकि कुछ भी बफ़र्ड नहीं होता।
जब मशीन पर base64 ही न हो
ज़्यादातर सिस्टमों में ऊपर से कम से कम एक टूल होता है, और ज़्यादातर में कई। अगर आपके प्लेटफ़ॉर्म से coreutils बिल्कुल ही गायब हों (कोई न्यूनतम कंटेनर, कोई अनोखा अप्लियंस), तो इंस्टॉल रास्ते ऐसे हैं:
| प्लेटफ़ॉर्म | पाने का तरीका | नोट्स |
|---|---|---|
| Debian / Ubuntu | पहले से इंस्टॉल; हटा दी गई हो तो apt install coreutils |
Ubuntu की मौजूदा रिलीज़ uutils परिवार को डिफ़ॉल्ट मानती हैं (25.10 से), GNU जुड़वाँ gnubase64 के रूप में उपलब्ध है; Debian 13 अभी भी डिफ़ॉल्ट में GNU coreutils देती है |
| RHEL / Fedora | dnf install coreutils |
वाक़ई हर इमेज पर पहले से इंस्टॉल |
| Alpine | apk add busybox (आमतौर पर पहले से मौजूद) |
busybox base64 -d; कोई -i फ़्लैग नहीं |
| macOS | बिल्ट-इन; GNU वर्ज़न के लिए brew install coreutils |
BSD फ़्लैग्स: डिकोड के लिए -D, लाइन विड्थ के लिए -b; brew से आपको gbase64 मिलता है |
और अगर पैकेज मैनेजर ही कोई विकल्प न हो, तो नीचे दिए गए सार्वभौमिक फॉलबैक्स सभी स्टैंडर्ड इनपुट से पढ़ते हैं और स्टैंडर्ड आउटपुट पर बाइट्स लिखते हैं, इसलिए वे वही पाइपलाइन्स में बैठ जाते हैं:
openssl base64 -d -A -a < in.b64 > out.bin
perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' < in.b64 > out.bin
python3 -c 'import base64, sys; sys.stdout.buffer.write(base64.b64decode(sys.stdin.buffer.read()))' < in.b64 > out.bin
ऐसे BusyBox सिस्टम पर जहाँ base64 अपलेट कम्पाइल के दौरान बाहर निकाला गया हो, पुराना गार्ड अभी भी काम करता है: uuencode -m MIME Base64 बनाता है, और उसका भाई उसे वापस पढ़ लेता है:
busybox uudecode -o out.bin in.uu
फँदे जो पूरा दोपहर निगल जाते हैं
नीचे की हर बात वह व्यवहार है जो आपको असल काम में मिलेगा, सब एक जगह इकट्ठा:
| फ़र्सा | क्या होता है | इलाज |
|---|---|---|
openssl base64 -d अकेला |
ख़ामोशी से कुछ नहीं करता और exit 0 कर जाता है, क्योंकि वहाँ -d का मतलब डिक्रिप्ट है |
openssl base64 -d -A -a, और ज़ीरो-बाइट नतीजा को त्रुटि मानें |
macOS पर -d के साथ डिकोड करना |
BSD base64 उस फ़्लैग को रिजेक्ट कर देता है (या उसे डीबग मानता है) | -D, या gbase64 के लिए coreutils इंस्टॉल करें |
| इनपुट में CRLF लाइन एंडिंग्स | GNU आधा नतीजा प्रिंट करके फेल होता है; uutils और BusyBox इसे मान लेते हैं | सबसे पहले tr -d '\r\n', बाहरी इनपुट के लिए हमेशा |
| बिना पैडिंग वाले टेल | BusyBox हर बाकी हिस्से को रिजेक्ट करता है; GNU और uutils सिर्फ़ कैनोनिकल टेल मानते हैं | डिकोड करने से पहले गायब = पैडिंग जोड़ें |
पैडिंग के बाद कचरा, जैसे TQ==junk |
uutils और GNU डिकोड करते रहते हैं और exit 0; BusyBox त्रुटि देता है | आउटपुट पर भरोसा करने से पहले इनपुट के शक्ल की जाँच करें |
नॉन-कैनोनिकल स्पेयर बिट्स, जैसे SGV= |
uutils और GNU रिजेक्ट करते हैं (GNU आधा आउटपुट देकर); BusyBox फिर भी डिकोड कर लेता है | इनपुट ख़राब है; स्रोत पर एन्कोडिंग दोबारा बनाएँ |
| डिकोड हुए आउटपुट को वेरिएबल में पढ़ना | $(...) आख़िरी नई लाइन्स हटा देता है और NUL बाइट बिल्कुल नहीं रख पाता |
फ़ाइल में डिकोड करें, xxd से जाँचें |
भरोसेमंद न होने वाले इनपुट पर -i |
ख़राबी ख़ामोश सफलता बन जाती है; नज़रअंदाज़ किए गए चर छिपा डेटा संभाल सकते हैं | सख़्ती से डिकोड करें, त्रुटि पढ़ें, स्रोत ठीक करें |
| दोनों वर्णमालाओं को उलट-पुलट कर लेना | base64url को स्टैंडर्ड के तौर पर डिकोड करना (या उलटा) गलत बाइट्स या त्रुटि देता है | डिकोड करने से पहले फ़ॉर्मेट जान लें; स्वैप tr '_-' '/+' है |
| डिकोड हुए सीक्रेट को सीक्रेट मान लेना | एक कमांड से वह वापस उलट जाता है; RFC में क्रेडेंशियल्स के लीक होने की वाक़ई घटनाएँ दर्ज हैं | असली एन्क्रिप्शन, एन्कोडिंग नहीं |
OpenSSL वाली पंक्ति को अपना अलावा पैराग्राफ मिलने का हक़ है, क्योंकि वह हार इतनी ख़ामोश होती है। OpenSSL 3.x पर स्टैंडअलोन ऐप की -d उसके सामान्य "डिक्रिप्ट" विकल्प है, और Base64 प्रोसेसिंग एक अलग मोड है जो -a से चुना जाता है। तो openssl base64 -d आपका इनपुट पढ़ता है, कुछ नहीं करता, कुछ प्रिंट नहीं करता, और exit 0 कर जाता है। काम करने वाला डिकोड openssl base64 -d -A -a है, जहाँ -A उसे बताता है कि इनपुट एक लगातार लाइन है। अगर आपको डिकोड के लिए OpenSSL ही इस्तेमाल करना है, तो हर एक बार ज़ीरो-बाइट आउटपुट को त्रुटि मानें।
एक सख़्त रूटीन
उन्हीं आदतों के बल पर Base64 कभी आप पर हावी नहीं होता:
- ज़ोर से फेल हों। स्क्रिप्ट्स
set -euo pipefailके साथ चलाएँ और एग्ज़िट कोड्स जाँचें। सभी आम डिकोडर गलत इनपुट पर exit 1 करते हैं; OpenSSL वही शोर मचाने वाला है जो ख़ामोश हो गया, इसलिए उसके लिए आउटपुट का खाली न होना भी जाँचें। - राउंड ट्रिप की प्रूफ़ बनाएँ। अपेक्षित बाइट्स और वापस लाए बाइट्स के बीच
cmpयाsha256sumही वही प्रूफ़ है जो मायने रखता है। बिनरी कभी आँख से न देखें। - बिनरी फ़ाइलों में, कभी वेरिएबल में नहीं। आख़िरी नई लाइन्स और NUL बाइट्स दोनों कमांड सब्स्टीयूशन के शिकार हैं।
- बाहरी इनपुट धो लें। जो भी चीज़ किसी प्लेटफ़ॉर्म बाउंड्री से गुज़री हो, उसे डिकोड करने से पहले
tr -d ' \r\n'लगाएँ। - डिफ़ॉल्ट में सख़्त रहें। यह न देखने तक
-iबंद रखें कि वाक़ई क्या गलत था; सख़्त हार आपको जगह बताती है, मदारी हार कुछ नहीं। - वर्णमाला का नाम लें। RFC 4648 के अनुसार स्टैंडर्ड Base64 और base64url अलग-अलग एन्कोडिंग हैं। JWT को base64url के तौर पर डिकोड करें, ईमेल अटैचमेंट को स्टैंडर्ड के तौर पर, और कभी बिना जाने क्यों, चरों को न उलट-पुलट करें।
- जो डिकोड कर रहे हैं, उसे कभी प्रिंट न करें। सीक्रेट्स की दुनिया में इस फ़ॉर्मेट का पूरा मक़सद अदृश्यता है; लॉग का पूरा मक़सद दिखाना। ये दोनों मक़सद मेल नहीं खाते।
"यह मशीन कौन सा फ़्लैग चाहती है" इस सवाल के लिए एक छोटा पोर्टेबल शिम:
case "$(base64 --version 2>&1 | head -2)" in
*uutils*|*GNU*|*BusyBox*) b64d () { base64 -d; } ;;
*) b64d () { base64 -D; } ;;
esac
b64d < in.b64 > out.bin
case स्टेटमेंट GNU, uutils और BusyBox परिवारों को उनके वर्ज़न बैनर से पकड़ लेता है - तीनों लोअरकेस फ़्लैग लेते हैं - और बाकी सब के लिए BSD फ़्लैग पर फ़िरता है, जो असल दुनिया के चार-तरफ़ा बँटवारे से बिल्कुल मेल खाता है: GNU, uutils, BusyBox, BSD।
शेल को डिकोड करना कब आया
यह फ़ॉर्मेट पुराना है; लेकिन जो कमांड आप टाइप कर रहे हैं, वह नहीं। शेल वाली तरफ़ की कहानी का एक छोटा टाइमलाइन:
- 1980, बर्केली। Mary Ann Horton ने University of California, Berkeley पर बिनरी फ़ाइलों (आमतौर पर कंप्रेस) को ईमेल के रास्ते पहुँचाने के लिए
uuencodeऔरuudecodeलिखे। इस नाम का मतलब है "Unix से Unix तक एन्कोडिंग" - उन Unix सिस्टमों के बीच फ़ाइलें भेजने के लिए एक सुरक्षित एन्कोडिंग, जिनका कैरेक्टर सेट आपस में مشترक न भी हो। दशकों तक शेल यूज़र्स का सहारा यही था, Base64 नहीं। - डायल-अप युग। सबसे पुरानी बेस एन्कोडिंग्स उसी समस्या पर सवार थीं: UNIX पर uuencode, TRS-80 और Apple II पर BinHex, और Macintosh एक क़दम पीछे; प्रत्येक सिर्फ़ उन चरों पर भरोसा करता था जिन्हें उसका अपना टर्मिनल प्रिंट कर सकता था।
- 1993। MIME ने ईमेल के लिए Base64 को स्टैंडर्ड बना दिया (RFC 1521, बाद में RFC 2045), जिसमें 76-चर लाइन रैपिंग शामिल है जो आज भी
base64के डिफ़ॉल्ट की परिभाषा बना है। - 2003 और अक्टूबर 2006। RFC 3548 ने परिवार को संभाला, और RFC 4648 ने उसे वर्णमालाओं और उस सख़्त डिकोड नियम से बदल दिया जिसे यह लेख बार-बार उद्धृत करता है, base64url समेत।
- 15 अगस्त 2006। coreutils 6.0 ने
base64कमांड को ख़ुद जोड़ा, और उसकी NEWS फ़ाइल ने साफ़ शब्दों में लिखा: "base64 एन्कोडिंग और डिकोडिंग (RFC 3548) का फीचर"। उस तारीख़ से पहले Linux के शेल यूज़र्सopenssl base64,uuencode -m, Perl, या Python की तरफ़ हाथ बढ़ाते थे, और इसीलिए इतनी सी पुरानी स्क्रिप्ट्स मानती हैं कि OpenSSL ही शहर में एकमात्र विकल्प है। - OS X 10.7। macOS ने अपना खुद का
base64दिया, BSD फ्लेवर-Dफ़्लैग और बिना डिफ़ॉल्ट लाइन रैपिंग के; यहीं से-dबनाम-Dका बँटवारा शुरू हुआ। - मार्च 2024। coreutils 9.5 ने डिकोडर को ढीला कर दिया: डिकोड के समय पैडिंग अब ज़रूरी नहीं, और नॉन-ज़ीरो स्पेयर बिट्स वाली एन्कोडिंग को अब ख़राबी के तौर पर पहचाना जाता है, ख़ामोशी से स्वीकार नहीं।
- 2025। coreutils का Rust रीराइट (uutils) मौजूदा Ubuntu रिलीज़ का डिफ़ॉल्ट बन गया। वही कमांड नाम, वही फ़्लैग्स, एक नया इंजन जिसके किनारों के बारे में अपने विचार हैं, जैसे
-Dको एलियस के तौर पर मान लेना।
कुछ बातें जो रखने लायक़ हैं
- कमांड फ़ॉर्मेट से जवान है। Base64 ईमेल में 1993 से है, लेकिन
base64कमांड 2006 में आया। तेरह साल की शेल स्क्रिप्ट्स ने यह काम दूसरे टूलों से किया, और उनकी उँगलियों की छाप आज भी हर जगह मिल सकती है। - help टेक्स्ट में एक जीवाश्म। uutils का
base64अपने help में अभी भी अपनी वर्णमाला को "RFC 3548" की बताता है, जो RFC 4648 का रिटायर्ड पूर्ववर्ती है, जबकि उसके GNU भाई ने मौजूदा स्टैंडर्ड को उद्धृत कर दिया है। एक छोटा सा जीवाश्म, जो सिर्फ़ तब दिखता है जब आप help पढ़ें। - टूलबॉक्स का सबसे महँगा ख़ामोश बेकार-कदम।
openssl base64 -dकुछ नहीं करता और exit 0 कर जाता है। पूरा एक घंटा डीबगिंग, बर्बाद, और एग्ज़िट कोड की जाँच पास भी हो गई। - BusyBox स्पेयर बिट्स संभाले रखता है। वह
SGV=को खुशी-खुशी डिकोड कर लेता है, जिनके बचे हुए बिट्स नॉन-ज़ीरो हैं और इसलिए नॉन-कैनोनिकल; जबकि उसके GNU रिश्तेदार वही इनपुट देखकर पनिये उठाते हैं। वही RFC, अलग-अलग नसों का काम। - फ़ॉर्मेट का नाम हर मशीन पर सच है।
printf 'base64' | base64GNU, uutils, BusyBox, और OpenSSL - किसी पर भी -YmFzZTY0देता है। यह 2006 से सच है और हमेशा सच रहेगा। - Base85, Base64 नहीं है। Git के "बिनरी पैच" ब्लॉक बिना-अनुभव वाली आँखों को Base64 जैसे दिखते हैं, लेकिन लंबाई-से-शुरू लाइन्स base85 शैली की एक अलग भाषा हैं। वही इम्पोस्टर जिसकी वजह से लोगों को grep करके डिकोड करने वाला लंबा व्यर्थ detour झेलना पड़ता है।
- ग्यारह चर, छियावन बिट। YouTube वीडियो ID एक 11-चर base64url स्ट्रिंग है, 64-बिट का नंबर जो URL के कपड़े पहने है; इसीलिए वह URL में बिना एक भी पर्सेंट साइन के आ सकता है।
- डिकोडर एक चर पर आपस में अलग हैं।
SGV=जैसी एक स्ट्रिंग पूरे फील्ड को तीन शिविरों में बँट जाती है, जैसे ऊपर का व्यवहार टेबल दिखाता है। अगर कोई डिकोड "साफ़-साफ़ वैलिड" इनपुट पर फेल हो, तो आप शायद स्पेयर-बिट्स की रेखा पर खड़े हैं, और वह एन्कोडिंग शुरू से ही कैनोनिकल नहीं थी।
तो अगली बार जब अक्षरों, अंकों, प्लस और स्लैश वाली स्ट्रिंग आपके टर्मिनल में उतरे, तो आप पूरी कहानी जानते हैं: कौन सा डिकोडर नज़र रख रहा है, कौन सी वर्णमाला बोली रही है, नई लाइन्स कहाँ छिपी हैं, और बाइट्स वापस कैसे लाएँ, अखिले और बाइट-दर-बाइट सिद्ध, बिना एक घंटे कैरियाज रिटर्न की वजह से खोए। और जब काम दूसरी दिशा में मुड़ता है, अपनी बिनरी को सफ़र के लिए टेक्स्ट के लिफ़ाफ़े में बाँधना, तो नीचे लिंक किया गया संबंधित Base64 एन्कोडिंग लेख उसी गहराई तक उस रस्म का ख़याल रखता है।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: Bash में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड