क्या आपको Base64 फ़ॉर्मेट के साथ काम करना होता है? तो यह साइट आपके लिए एकदम सही है! अपने डेटा को एन्कोड या डिकोड करने के लिए, हमारे बेहद आसान ऑनलाइन टूल का इस्तेमाल करें।

Python में Base64 डिकोडिंग: एक सम्पूर्ण गाइड

आपके कोड के कहीं न कहीं अक्षरों की एक स्ट्रिंग उतर आई है जो टेक्स्ट जैसी बिल्कुल नहीं लगती: A से Z की लंबी धार, कुछ अंक, कभी-कभार + या /, शक़-शक़ से - या _, और शक़-शक़ से आख़िर में खड़े एक या दो = चिह्न। उस स्ट्रिंग के पीछे आपकी गेटवे द्वारा रिजेक्ट किए गए JWT का पेलोड हो सकता है, HTML के पेज के अंदर छुपी एक तस्वीर, .b64 अटैचमेंट के तौर पर ईमेल मिली कोई फ़ाइल, या तीन हेल्प डेस्क से गुज़र चुका एक टिकट में सर्टिफिकेट ब्लॉक। आपका काम मूल बाइट्स को ठीक वैसे ही वापस सौंपना है, जैसा वे थे। और इस काम के लिए Python बेहद अच्छे मूड में है, क्योंकि पूरा टूलबॉक्स दशकों से स्टैंडर्ड लाइब्रेरी के साथ शिप हो रहा है: import base64 की एक लाइन और आप हर प्लेटफ़ॉर्म पर तैयार हैं, बिना कुछ इंस्टॉल किए, बिना कुछ कनफ़िगर किए।

बैठने से पहले एक छोटी याद ताज़ा कर लें, क्योंकि हर साल एक बार हर किसी को यह चाहिए: Base64 डेटा के हर तीन बाइट्स को 64-चर वर्णमाला से चुने चार चरों के रूप में फिर से लिख देता है, और जब आख़िरी तीन बाइट्स का समूह अधूरा हो, तो = पैडिंग वह समूह पूरा कर देती है ताकि आउटपुट हमेशा चारों के चारों में निकले। यही सारा तड़का है। यह न कंप्रेशन है और न गुप्तता, बस एक रास्ता है ताकि बाइनरी उन चैनलों में जीवित रहे जहाँ टेक्स्ट के सिवा कुछ स्वीकार नहीं होता। इस साइट का होम पेज इस फ़ॉर्मेट को पूरी गहराई तक समझाता है, वर्णमाला और पैडिंग के गणित समेत, इसलिए हम अपनी शक्ति वहाँ लगाएँगे जहाँ असली तकलीफ़ है: डिकोडिंग के Python पक्ष पर, और नतीजे को सही रखने में।

तीन तथ्य नीचे आने वाली हर चीज़ को गढ़ते हैं, और इन्हें अगली लाइन पढ़ने से पहले रट लेने के लायक हैं। पहला, डिकोडर के दो मूड हैं: एक विनम्र, सहनशील डिफ़ॉल्ट जो जो चीज़ पहचाने नहीं, उसे खामोशी से फेंक देता है, और एक सख़्त मोड जो ऐसी इनपुट को तुरंत मना कर देता है। दूसरा, डिकोड का नतीजा हमेशा bytes ऑब्जेक्ट होता है, कभी स्ट्रिंग नहीं, और असली टेक्स्ट निकालने का फैसला आपको जानबूझकर करना पड़ता है। तीसरा, लगभग एक जैसी दिखने वाली दो वर्णमालाएँ हैं: स्टैंडर्ड और URL-safe, और इन्हें उलट-पुलट कर लेना डेटा खोने का पसंदीदा तरीका है - बिना एक भी एरर के। यह गाइड आपको तीनों के पार ले जाता है, ताकि अगली बार जब आपके टर्मिनल में अर्थहीन अक्षरों की एक दीवार उतरे, तो आप आँखें मूलकर देखने के बजाय मुस्कुरा रहे हों।

पूरा डिकोड मेन्यू

base64 मॉड्यूल खोलो और आप दो पीढ़ियों के इंटरफ़ेस एक-दूसरे के बग़ल में बैठे पाओगे। आधुनिक वाला, जिसका केंद्र b64decode है, बाइट्स जैसी ऑब्जेक्ट्स (और सादे ASCII स्ट्रिंग्स) को वापस बाइट्स में बदलता है, और यह RFC 4648 में परिभाषित दोनों Base64 बोलियों को बोलता है। पुराना वाला और भी बुज़ुर्ग है और फ़ाइल-मुखी: यह फ़ाइल ऑब्जेक्ट्स पर काम करता है, इसे सिर्फ़ स्टैंडर्ड वर्णमाला का पता है, और इसे 76-चर लपटी हुई लाइनों के चारों ओर बनाया गया था, जो लाइनें RFC 2045 - 1996 की MIME मेल स्टैंडर्ड - एन्कोडेड आउटपुट से मांगती थी। पुराने कोड में पुराने नामों के बहुत सारे नमूने मिलेंगे, इसलिए यहाँ पूरा डिकोडिंग वाला मेन्यू है:

फ़ंक्शन यह क्या करता है नोट्स
base64.b64decode(s, altchars=None, validate=False) कामकाजी अश्व: Base64 ब्लाब को कच्चे बाइट्स में वापस बाइट्स या ASCII स्ट्रिंग स्वीकार करता है, हमेशा बाइट्स ही वापस देता है
base64.standard_b64decode(s) वही काम, स्टैंडर्ड वर्णमाला से बंधा हुआ तब उपयोगी जब बोली पक्की हो
base64.urlsafe_b64decode(s) - और _ वाली URL-safe वर्णमाला पढ़ता है यही JWT पढ़ने वाला है
base64.decodebytes(s) Base64 की एक या अधिक लपटी हुई लाइनें डिकोड करता है Python 3.1 में जुड़ा, MIME-अनुकूल रास्ता, सहनशील
base64.decode(input, output) Base64 फ़ाइल को कच्ची फ़ाइल में स्ट्रीम करता है पुरानी पीढ़ी वाला, लाइन-दर-लाइन पढ़ता है, सहनशील
base64.b32decode(s, casefold=False) छोटे Base32 रिश्तेदार को डिकोड करता है casefold लोअरकेस इनपुट स्वीकार करता है
base64.b16decode(s, casefold=False) Base16 डिकोड करता है, जो सादा हैक्सडेसिमल है Python 3.14 में छह गुना तक तेज़
binascii.a2b_base64(s, strict_mode=False) C-स्तर का फ़ंक्शन जो असली काम करता है सख़्ती का सीधा हथौड़ा, strict_mode Python 3.11 से उपलब्ध

नीचे की सब बातें पहले पंक्ति पर खड़ी हैं। गहराई में जाने से पहले एक तथ्य जानने लायक है: आधिकारिक दस्तावेज़ीकरण में यह मॉड्यूल "इंटरनेट डेटा प्रबंधन" के नीचे रहता है, binascii के ठीक बग़ल में, और वह जगह ग़लती से नहीं तय हुई। b64decode एक पतला रैपर है जो वर्णमाला का अनुवाद करता है (जब आप altchars पास करते हैं) और फिर C-स्तर के binascii.a2b_base64 को भारी उठाने देता है। इसलिए ही यह फ़ंक्शन तेज़ है, और इसके एरर मैसेज C की वह क्रिस्प, बे-भाव धार रखते हैं।

कामकाजी अश्व: b64decode

यहाँ पूरा अनुबंध है, इतना छोटा कि उसे सिर में रखने लायक है। यह फ़ंक्शन एक बाइट्स जैसी ऑब्जेक्ट या ASCII स्ट्रिंग लेता है, एक वैकल्पिक दो-चर वर्णमाला-बदलाव, और एक वेरिफिकेशन फ्लैग। वह वापस एक bytes ऑब्जेक्ट सौंपता है। असफलता पर वह binascii.Error फेंकता है, जो ValueError का उप-क्लास है, ताकि कभी एक पूरे परिवार के एक्ससेप्शन एक साथ पकड़ने का काम आए:

import base64
data = base64.b64decode("Zm9vYmFy")
print(data)
# b'foobar'
print(type(data))
# <class 'bytes'>

वह आख़िरी लाइन इस लेख की सबसे ज़रूरी लाइन है। नतीजा बाइट्स है, स्ट्रिंग नहीं, और Python आपके हाथ उतने ही दूर थामता है जितने उचित है: ऑब्जेक्ट को प्रिंट करने पर आपको b'...' प्रस्तुति दिखती है, और इसे स्ट्रिंग से चिपकाने की कोशिश TypeError फेंकती है। असली टेक्स्ट चाहिए होने का क्षण आपके फैसले पर है, और नीचे चरसेट वाला खंड बताता है कि कभी वह फैसला आसान होता है और कभी जाल।

वैकल्पिक altchars आर्गुमेंट स्टैंडर्ड वर्णमाला के + और / को किसी दूसरी जोड़ी चरों से बदल देता है। यही वह नॉब है जो URL-safe बोली बनाता है, और इसी तरह urlsafe_b64decode को b64decode के ऊपर बनाया गया है। आप खुद altchars तक कम ही पहुँचेंगे, लेकिन जानकर अच्छा लगता है कि यंत्र तैयार है। बाकी सब में, फ़ंक्शन सिर्फ़ काम करता है, तेज़ी से, C में।

डिफ़ॉल्ट में सहनशील, माँग पर सख़्त

डिफ़ॉल्ट में b64decode एक विनम्र भूला-बसवा है। जो भी चर 64-चर वर्णमाला में नहीं होता (और आपके altchars में भी नहीं), डिकोडिंग शुरू होने से पहले उसे खामोशी से फेंक दिया जाता है, और जो बचता है वह डिकोड हो जाता है। न चेतावनी, न सूचना, न देखने के लिए कोई रिटर्न वैल्यू, बस एक नतीजा। उस सहनशीलता का एक गौरवशाली पूर्वज है: RFC 2045 का खंड 6.8 डिकोडरों को बताता है कि "टेबल 1 में न मिलने वाली सभी लाइन ब्रेक्स और अन्य चरों को अनदेखा किया जाना चाहिए", क्योंकि SMTP ऐतिहासिक रूप से लंबी लाइनों को लपेटता था और रास्ते में बेग़ार चर बिखेर देता था। मेल क्लाइंट, चैट ऐप, या PDF कॉपी से गुज़रा पेलोड अक्सर बिना किसी तैयारी के डिकोड हो जाता है, और यह एक असली सुपरपावर है।

वही दया है जिसके कारण डिफ़ॉल्ट डिकोडर वेरिफ़ाईयर के तौर पर बिल्कुल बेकार है। RFC 4648 का खंड 12 जोखिम साफ़ लिखता है: न-वर्णमाला चरों को ठहराने के बजाय पूरी एन्कोडिंग को रिजेक्ट न करना एक गुप्त चैनल खोलता है जिससे जानकारी रिसाई जा सकती है, और यह स्ट्रिंग समानता की जाँचों को भी तोड़ सकता है, क्योंकि दो अलग-अलग इनपुट एक ही बाइट्स में डिकोड हो सकते हैं। आपके द्वारा एन्कोड न की गई किसी भी चीज़ के लिए, validate=True पास करें और एक्ससेप्शन को ही जवाब मानें। यहाँ नुक़सान की रिपोर्ट है, हर पंक्ति किसी भी आधुनिक Python में दोहराई जा सकती है:

क्या अंदर जाता है सहनशील (डिफ़ॉल्ट) validate=True
Zm9vYmFy (साफ़ पेलोड) b'foobar' b'foobar'
Zm9v\r\nYmFy (बीच में लाइन ब्रेक) b'foobar' binascii.Error
Zm9v YmFy (अतिरिक्त ख़ाली जगहें) b'foobar' binascii.Error
Zm9v!YmFy (एक बेग़ार एक्सक्लेमेशन चिह्न) b'foobar' binascii.Error
junkZm9vYmFy (पेलोड के सामने एक शब्द) b'\x8e\xe9\xe4foobar' b'\x8e\xe9\xe4foobar'
Zm9v=YmFy (बीच में एक पैड) b'foobar' binascii.Error
=Zm9v (सामने से पैडिंग) b'foo' binascii.Error
==== (चार पैड, डेटा नहीं) b'' binascii.Error
(ख़ाली इनपुट) b'' b''

देखिए सहनशील कॉलम की ख़ामोश मेहनत। सबसे पहले हैरान करने वाली पंक्ति वह है जिसमें सामने शब्द है: junk के चारों अक्षर Base64 वर्णमाला में ठहरे हुए हैं, इसलिए वह "कचरा" तीन असली बाइट्स के रूप में डिकोड होता है और सीधे चेहरे के साथ आपके पेलोड से चिपक जाता है। सख़्त मोड इस पंक्ति का कोई बचाव करने वाला नहीं है, क्योंकि इनपुट वाकई वैलिड Base64 है; अन्य पंक्तियाँ ही एकदम मना दी जाती हैं, और इन मनाओं का बिल्कुल एक ही आकार है: binascii.Error, जिसके साथ कुछ यादगार मैसेजों में से एक होता है:

  • Incorrect padding - फेंकने के बाद लंबाई चार का गुणज नहीं है, या आख़िरी समूह बहुत छोटा है। Zm9vYmE जैसी स्ट्रिंग, जिस पर पैड बिल्कुल नहीं, यहीं उतरती है।
  • Invalid base64-encoded string: number of data characters (N) cannot be 1 more than a multiple of 4 - इनपुट अगले समूह से बिल्कुल एक चर कम है। यह काट गए या कॉपी-पेस्ट किए गए पेलोड की क्लासिक पहचान है।
  • Only base64 data is allowed - वर्णमाला-बाहरी चर सख़्त मोड तक पहुँच गया, और एक न्यूलाइन भी गिनती में आता है।
  • Excess padding not allowed - स्ट्रिंग के बीच में पैड, या आख़िरी समूह की अनुमति से ज़्यादा पैड।
  • Leading padding not allowed - स्ट्रिंग = से शुरू होती है।
  • और एक अलग परिवार की: ValueError: string argument should contain only ASCII characters, जो तब मिलता है जब आप स्ट्रिंग में न-ASCII अक्षर लेकर आएँ। स्ट्रिंग स्वीकार होती हैं, पर सिर्फ़ ASCII वाली।

पर्दे के पीछे, validate=True बिल्कुल अलग कोड पथ नहीं है। मॉड्यूल वह फ्लैग binascii.a2b_base64 को उसके strict_mode पैरामीटर के तौर पर अग्रेषित करता है, वह सख़्त जाँच जो Python 3.11 में binascii में जोड़ी गई थी। इससे आपको सीधा हथौड़ा मिलता है जब सख़्ती चाहिए हो बिना base64 स्तर से गुज़रे:

import binascii
line = b"Zm9vYmFy"
print(binascii.a2b_base64(line, strict_mode=True))
# b'foobar'

सख़्त मोड पर अंधा भरोसा करने से पहले एक अजीब बात पकड़ लें: वह एक ही आख़िरी न्यूलाइन को भी मना कर देता है, इसलिए MIME-लपटा ब्लॉक सहनशील पथ या decodebytes का काम है, validate=True का नहीं। सख़्त पथ को वह डेटा के लिए रखें जो बिल्कुल साफ़ होने की उम्मीद है, जैसे अपने कोड से बस निकला ताज़ा-ताज़ा बनाया टोकन।

base64url: URL में बैठ जाने वाली वर्णमाला

स्टैंडर्ड वर्णमाला में दो ऐसे चर हैं जिनसे URL की नफ़रत है। + चिह्न को कोई भी फ़ॉर्म डिकोडर ख़ाली जगह के तौर पर पढ़ लेता है, और / चिह्न पथ-विलेपक के लिए आरक्षित है। RFC 4648 का खंड 5 रिश्तेदार बोली परिभाषित करता है, जिसमें + हो जाता है - और / हो जाता है _, और जहाँ भी डेटा की लंबाई संदर्भ से पता हो, पैडिंग छोड़ दी जाती है। RFC ने इस वैरिएंट को एक सही-सही नाम तक दे दिया है, base64url, और ज़ोर देता है कि इसे सिर्फ़ "base64" कहना उचित नहीं। आपको यह सबसे ज़्यादा JSON Web Tokens के अंदर मिलेगा, जहाँ टोकन का हर हिस्सा पैडिंग के बिना base64url होता है, और यह OAuth टोकन और API कर्सर पैरामीटर में भी दिखता है।

Python इसके लिए एक समर्पित फ़ंक्शन देता है, urlsafe_b64decode। यह डैश और अंडरस्कॉर को वापस प्लस और स्लैश में बदलता है और फिर डिकोड करता है, पर वह आपके लिए फिर से पैड नहीं करेगा। बिना पैड इनपुट JWTs के लिए सामान्य मामला है, इसलिए अंकगणित वाली लाइन पहले आती है, और यही वह लाइन है जो PyJWT जैसी लाइब्रेरियाँ अंदर से इस्तेमाल करती हैं:

import base64
segment = "Zm9vYmE"
padded = segment + "=" * (-len(segment) % 4)
print(base64.urlsafe_b64decode(padded))
# b'fooba'

यह अभिव्यक्ति "=" * (-len(segment) % 4) एक चाल जैसी दिखती है, पर यह पूरा काम है: यह शून्य, एक, या दो पैड बनाता है और कभी तीन नहीं, इसलिए पहले से पैड की हुई स्ट्रिंग बिना छुई गुज़र जाती है। नेगेटिव मोड्यूलो ही है जो इसे हर लंबाई की स्ट्रिंग के लिए कामदार बनाता है, और यही वह एक लाइन Base64 अंकगणित है जो हर Python डेवलपर कम-से-कम एक बार ज़रूर टाइप करता है।

अब ख़तरनाक उलझन, क्योंकि दोनों वर्णमालाएँ इतनी नज़दीक हैं कि भ्रम का माहौल बन जाता है। base64url स्ट्रिंग को स्टैंडर्ड डिकोडर से गुज़ारो और डैश व अंडरस्कॉर स्टैंडर्ड वर्णमाला में बस मौजूद नहीं हैं, इसलिए सहनशील डिकोडर उन्हें निगल लेता है और जो बचता है, उसे डिकोड करता है। कुछ पेलोड के लिए यह एक कुचला हुआ बाइट स्ट्रीम है; दूसरों के लिए यह बिल्कुल कुछ नहीं:

import base64
tricky = base64.urlsafe_b64encode(b"\xfb\xff\xfe")
print(tricky)
# b'-__-'
print(base64.standard_b64decode(tricky))
# b'' - हर चर चुपचाप डिस्कार्ड हो गया
print(base64.urlsafe_b64decode(tricky))
# b'\xfb\xff\xfe'

उल्टी दिशा में ढीलापन है, और यही वजह है कि उलझन बिना नोट किए रह जाती है: urlsafe_b64decode पहले अपनी वर्णमाला का अनुवाद करता है और फिर सहनशीलता से डिकोड करता है, इसलिए वह + और / वाली स्टैंडर्ड-वर्णमाला स्ट्रिंग को भी खुशी से स्वीकार कर लेगा। सबक है कि खुदबुनियादी न करें। हर बोली के लिए एक फ़ंक्शन चुनना है और उसी पर टिके रहना है, वैसे ही जैसे विदेशी मुद्रा के साथ: येन वह जगह खर्च करें जहाँ येन वैध है, ग़लत एक्सचेंज कार्यालय पर नहीं।

आउटपुट बाइट्स होता है: चरसेट की बातचीत

यह वह वाक्य है जो लोगों के Base64 के पास लाने वाले चरसेट के सवालों का आधा हिस्सा तय कर देता है: b64decode बाइट्स डिकोड करता है; वह टेक्स्ट नहीं डिकोड करता। कोई चरसेट आर्गुमेंट नहीं है, कोई रूपांतरण नहीं है, और इनपुट के बारे में कुछ भी Python को यह नहीं बताता कि बाइट्स का क्या मतलब होना चाहिए। मतलब वह चीज़ है जिसे आपको संदर्भ से देना पड़ता है, और वह संदर्भ लगभग हमेशा तीन चीज़ों में से एक होता है: एक ऐसा हेडर जो कहता है, एक ऐसा API अनुबंध जो कहता है, या बाइट्स के अपने अंदर छिपा एक मैजिक नंबर।

import base64
raw = base64.b64decode("w6l0w6k=")
print(raw)
# b'\xc3\xa9t\xc3\xa9'
print(raw.decode("utf-8"))
# été

ग़लत लेबल के साथ वही विचार एक शोरगुल भरी असफलता है, और यह रहम है। वैध UTF-8 न होने वाले बाइट्स स्ट्रिंग बनने से इनकार करते हैं, और एक्ससेप्शन बिल्कुल साफ़ बताता है कि कौन सा बाइट आहत हुआ:

import base64
raw = base64.b64decode("/w==")
try:
  raw.decode("utf-8")
except UnicodeDecodeError as caught:
  print(caught)
# 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

तीन अनुभव-आधारित नियम इस खंड को हॉरर शो से बचाते हैं। एक: जब डिकोड किया हुआ डेटा JSON हो, तो आपको मैन्युअली डिकोड करने की ज़रूरत ही नहीं, क्योंकि Python 3.6 से json.loads बाइट्स को सीधे स्वीकार करता है और खुद UTF-8, UTF-16, और UTF-32 पहचान लेता है। दो: बाइनरी टेक्स्ट नहीं है, इसलिए PNG के लिए "पहचानी गई" चरसेट तथ्य नहीं किस्मत वाला अंदाज़ा है; लेबल के बजाय बाइट्स की जाँच करें। तीन: अगर भेजने वाले ने चरसेट बताया, तो भेजने वाले पर भरोसा करें, क्योंकि Content-Type हेडर या API दस्तावेज़ हर एक बार किसी भी डिटेक्टर से ऊपर है।

जिन जगहों पर Python कोड में डिकोड किया हुआ Base64 दिखाई देता है

थोड़ी देर बाद आपको आकार पहचानने लगते हैं। यहाँ वह फ़ील्ड गाइड है जिसमें बताई गई हैं उन जगहों की जहाँ Python ऐप्लिकेशन में डिकोड किया हुआ Base64 मिलता है, और हर जगह की एक-लाइन रेसिपी। जो खंड आगे आए हैं, वे सबसे आम मामलों को पूरा इलाज देते हैं:

यह कहाँ मिलता है यह क्या है इसे कैसे पढ़ें
JWT हेडर, पेलोड और साइनचर के हिस्से (RFC 7519) डॉट पर बाँटें, पैडिंग फ़िक्स के साथ urlsafe_b64decode
Authorization हेडर HTTP Basic क्रेडेंशियल्स, user:pass (RFC 7617) Basic प्रीफ़िक्स हटाएँ, डिकोड करें, पहले कोलन पर बाँटें
data: URI HTML या CSS में इनलाइन मीडिया (RFC 2397) पहले कॉमा पर काटें, बाकी को डिकोड करें
मेल अटैचमेंट Content-Transfer-Encoding: base64 बॉडी (RFC 2045) मैसेज पार्ट पर get_payload(decode=True)
मेल हेडर वैल्यू =?charset?b?...?= एन्कोडेड वर्ड (RFC 2047) email पैकेज को आपके लिए डिकोड करने दें
PEM फ़ाइल कवच वाला की या सर्टिफिकेट (RFC 7468) कवच की लाइनें हटाएँ, बॉडी को DER में डिकोड करें
JSON API फ़ील्ड स्ट्रिंग के तौर पर तस्करी हुई बाइनरी डिकोड करें, फिर नतीजे को बाइट्स की तरह ही समझें, टेक्स्ट की तरह नहीं
TEXT कॉलम या एनवाइरनमेंट वेरिएबल सिर्फ़ टेक्स्ट की जगह में संचित बाइनरी या JSON डिकोड करें, फिर पार्स करें या लिखें, उस चरसेट से जो आपने तय किया था

JSON Web Token पढ़ना

JWT तीन base64url टुकड़ों से बना होता है जो डॉट से जुड़े होते हैं: हेडर, पेलोड, और साइनचर। पहले दो सादा JSON होते हैं, इसलिए उनमें झाँकना एक-एक लाइन का काम है, ऊपर वाले खंड की पैडिंग फ़िक्स का इस्तेमाल करके:

import base64
import json
def read_part(segment):
  padded = segment + "=" * (-len(segment) % 4)
  return base64.urlsafe_b64decode(padded)
token = ("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
         "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0."
         "8Rmup2hf8jZvoBgoCRqRWlBFNtvUYmA0eR7YKellPMs")
head, body, _signature = token.split(".")
print(json.loads(read_part(head)))
# {'alg': 'HS256', 'typ': 'JWT'}
print(json.loads(read_part(body)))
# {'sub': '1234567890', 'name': 'John Doe'}

स्कोप का एक नोट, क्योंकि इसका असर है: टोकन को इस तरह जाँचना डीबगिंग टूल है, ऑथेंटिकेशन मेकनिज्म नहीं। पेलोड पढ़ने लायक इसका मतलब यह नहीं कि वह सही है; हमलावर आपके सीक्रेट कभी न जानते हुए भी पहले दो सेगमेंट नक़ली बना सकता है। असली वेरिफिकेशन के लिए, टोकन को PyJWT (pip install pyjwt) को सौंपें, जो सिग्नेचर की जाँच करता है और बिना स्पष्ट एल्गोरिद्म सूची के डिकोड करने से इनकार करता है:

import jwt
# 32 बाइट्स से छोटी की PyJWT की InsecureKeyLengthWarning कमाती है (PyJWT 2.11+), डिमो की के लिए एक उचित टिप्पणी।
decoded = jwt.decode(token, "super-secret-key", algorithms=["HS256"])
print(decoded)
# {'sub': '1234567890', 'name': 'John Doe'}

ग़लत की के साथ आपको डिकशనरी के बजाय एक्ससेप्शन मिलता है, और यही बिल्कुल वह व्यवहार है जो आप प्रोडक्शन कोड में चाहते हैं। और अगर टोकन एक समय-सीमा पार किए टाइमस्टैम्प के साथ पहुँचा, तो PyJWT उसके लिए भी एक्ससेप्शन फेंकता है, इसलिए क्लेम नामों को खुद याद रखने की ज़रूरत नहीं पड़ती।

Data URI खोलना

Data URI मीडिया को सीधे HTML या CSS के अंदर बैठा देता है ताकि ब्राउज़र दूसरा रिक्वेस्ट न छोड़े: data:, मीडिया टाइप, base64 शब्द, एक कॉमा, और एन्कोडेड बाइट्स। विभाजन पहले कॉमा पर होता है, ख़त्म बात, और उसके बाद की हर चीज़ सादी स्टैंडर्ड-वर्णमाला पेलोड है:

import base64
uri = ("data:image/png;base64,"
       "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ"
       "AAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==")
mime, payload = uri.split(",", 1)
data = base64.b64decode(payload)
print(mime)
# data:image/png;base64
print(data[:8])
# b'\x89PNG\r\n\x1a\n'

नतीजे के सामने आठ बाइट्स की PNG साइनचर यह सस्ता और ख़ुशमिज़ाज इम्तिहान है कि आपने सही चीज़ डिकोड की है। दो फँसवों का जिक्र ज़रूरी है। अगर URI स्क्रेप किए गए पेज या चैट मैसेज से आया है, तो पहले HTML इन्टिटीज़ और बेग़ार ख़ाली जगहें हटाएँ, क्योंकि सहनशील डिकोडर बहुत सारे कचरे को माफ़ कर देगा और एरर के बजाय टूटी तस्वीर सौंप देगा। और अगर आप बेभरोसे इनपुट को बड़ी मात्रा में डिकोड कर रहे हैं, तो validate=True पास करें: सख़्त वेरिफिकेशन में फेल होने वाला data URI वह data URI है जो कभी सही ढंग से बनाया नहीं गया था, और आप इसे अनुमान पर डिस्क पर लिखना नहीं चाहते।

Authorization हेडर तोड़ना

Basic ऑथेंटिकेशन (RFC 7617) HTTP का सबसे पुराना स्कीम है, और आज भी यह हैरान करने वाली संख्या में API इंटीग्रेशन, वेबहुक, और CI पाइपलाइन का आधार बना हुआ है। क्लाइंट अपने क्रेडेंशियल्स user:pass के रूप में भेजता है, base64-एन्कोडेड, Basic शब्द के पीछे:

import base64
header = "Basic amFuZTpwYTpzcw=="
decoded = base64.b64decode(header[len("Basic "):]).decode("utf-8")
user, _, password = decoded.partition(":")
print(user, password)
# jane pa:ss

partition पर ध्यान दें, क्योंकि यह वही विवरण है जो बाद में आपको बचाएगा: पासवर्ड में कोलन हो सकते हैं, यूज़र ID में नहीं, और सिर्फ़ पहला कोलन ही विभाजक है। एक ईमानदार नोट, क्योंकि RFC खुद इसमें ख़ुलकर बात करता है: base64 एन्क्रिप्शन नहीं है। RFC 4648 कहता है कि base एन्कोडिंग "देखने में ऐसी जानकारी को छिपाता है जो वरना आसानी से पहचानी जा सकती थी, जैसे पासवर्ड, पर यह कोई कंप्यूटेशनल गुप्तता नहीं प्रदान करता"। Basic हेडर को ट्रैफ़िक देखने वाला कोई भी डिकोड कर सकता है, इसलिए इसे TLS-सुरक्षित कनेक्शन के लिए सुविधा की तरह समझें, सुरक्षा सीमा की तरह नहीं। जब आप ही हेडर भेजने वाले हैं, तो requests auth=("jane", "pa:ss") के साथ इसे आपके लिए बना देता है, और जब तक वह लाइब्रेरी आपकी स्टैक में है, तो उसका इस्तेमाल ज़रूर करें।

ईमेल: मूल ग्राहक

Base64 को 1993 में बिल्कुल एक ही काम के लिए मानक बनाया गया था: बाइनरी को ईमेल में जीवित रखना। RFC 2045, MIME मानक, Content-Transfer-Encoding: base64 बॉडी एन्कोडिंग की परिभाषा करता है, और आज भी यह वह डिफ़ॉल्ट रास्ता है जिससे अटैचमेंट इंटरनेट पर यात्रा करते हैं। Python का email पैकेज पूरा काम आपके लिए कर देता है: यह हेडर पार्स करता है, =?utf-8?b?...?= एन्कोडेड वर्ड्स डिकोड करता है जो RFC 2047 हेडर फ़ील्ड्स में छिपाता है, और आपसे मांगने पर बॉडी को base64-डिकोड करता है:

import email
from email import policy
raw = (b"Subject: =?utf-8?b?w6l0w6k=?=\r\n"
       b"From: sender@example.com\r\n"
       b"To: reader@example.com\r\n"
       b"Content-Transfer-Encoding: base64\r\n"
       b"\r\n"
       b"w6l0w6kgbWFpbA==\r\n")
msg = email.message_from_bytes(raw, policy=policy.default)
print(msg["Subject"])
# été
print(msg.get_payload(decode=True))
# b'\xc3\xa9t\xc3\xa9 mail'

get_payload(decode=True) कॉल Content-Transfer-Encoding हेडर पढ़ता है और बॉडी को आपके लिए base64-डिकोड करता है, रास्ते में 76-चर लाइनों को खोलकर। policy=policy.default आर्गुमेंट Python 3.6 के बाद का आधुनिक इंटरफ़ेस चुनता है, जब नया पॉलीसी-आधारित email API अस्थायी रहना बंद कर दिया, जिससे आपको डिकोड किए हुए हेडर वैल्यूज़ तैयार मिलती हैं; पुराना पार्सर अभी भी काम करता है, पर आपको एन्कोडेड वर्ड्स हाथ से डिकोड करने पड़ते हैं। आप सिर्फ़ तब decodebytes तक गिरते हैं जब आप एक नंगे स्निपेट को पार्स कर रहे हों जो पूरा मैसेज नहीं है, जैसे कोई ब्लॉक जिसे किसी ने टिकट में पेस्ट किया हो। मल्टीपार्ट मैसेज के लिए iter_attachments() से पुनरावृत्ति करें और हर पार्ट को वही एक-लाइन इलाज दें।

PEM की कवच और cryptography पैकेज

PEM फ़ाइल एक हेडर लाइन, कुछ लपटा हुआ Base64, और एक फ़ुटर लाइन है, और इससे आगे कुछ नहीं। कवच सजावटी है; Base64 ही पूरा क़िस्सा है, क्योंकि वह नीचे कच्ची DER संरचना में डिकोड होता है। cryptography पैकेज (pip install cryptography) नतीजे को सीधे लोड कर सकता है, और यही वजह है कि यह सर्टिफिकेट्स और की से जुड़े हर काम के लिए मानक औजार है:

import base64
from cryptography import x509
pem = b"""-----BEGIN CERTIFICATE-----
MIIBGzCBwaADAgECAgEBMAoGCCqGSM49BAMCMBcxFTATBgNVBAMMDGV4YW1wbGUu
dGVzdDAeFw0yNjA4MjkxNzIxMzZaFw0yNjA4MzAxNzIxMzZaMBcxFTATBgNVBAMM
DGV4YW1wbGUudGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPvNHjdF4b1n
SkBDT6UWtG2k8ICe45eL3kSkVfuhriev1uO9PBLMP50HWnrLbCXtl3lhWaVibctl
QbWRG4xqGLcwCgYIKoZIzj0EAwIDSQAwRgIhAKdFm5GLecg2fF7qUhSmKGtgNFaL
qVyKtDXK07N6GZd/AiEAtRXemnYqDMz77o9+VpM/NsNEwDi0yaVB+tKGLbdKJb0=
-----END CERTIFICATE-----
"""
body = b"".join(pem.splitlines()[1:-1])
der = base64.b64decode(body)
cert = x509.load_der_x509_certificate(der)
print(cert.subject.rfc4514_string())
# CN=example.test

ज़्यादातर प्रोडक्शन कोड में आप कवच-और-डिकोड हाथ से कभी नहीं करते: load_pem_x509_certificate कवच वाले बाइट्स स्वीकार करता है और Base64 स्टेप आपके लिए अंदर से निपटा लेता है। मैन्युअल पथ तब काम आता है जब DER बाइट्स पहले से आपके हाथ में हों (एक डेटाबेस कॉलम, एक कॉन्फ़िग फ़ाइल, किसी प्रोटोकॉल से बाइट बफ़र), या जब ब्लॉक एक स्ट्रिंग में लपटा हुआ पहुँचा हो और आप भरोसे से पहले देखना चाहें कि अंदर क्या है। की इसी तरह काम करती हैं, load_der_private_key उसी डिकोड के दूसरी तरफ़ इंतज़ार करती है।

फ़ाइलें, मैजिक नंबर और .b64 की आदत

डिकोडिंग सिर्फ़ आधा काम है; बाइट्स को आमतौर पर फ़ाइल चाहिए होती है। पैटर्न है: पढ़ो, डिकोड करो, जाँचो, लिखो - और जाँच ज़रूरी है, क्योंकि टूटा हुआ पेलोड वरना ख़ामोशी से ग़लत फ़ाइल बना देता, जो आपको हफ़्तों बाद पता चलती।

import base64
import binascii
with open("payload.b64", "rb") as handle:
  encoded = handle.read()
try:
  data = base64.b64decode(encoded, validate=True)
except binascii.Error:
  data = base64.b64decode(encoded)
with open("payload.bin", "wb") as out:
  out.write(data)

तेज़ एक-बारगी रूपांतरण के लिए, पुरानी फ़ाइल-से-फ़ाइल फ़ंक्शन पूरा सफ़र एक ही कॉल में कर देता है, लपटी हुई लाइनें समेत:

import base64
with open("photo.b64", "rb") as src, open("photo.png", "wb") as dst:
  base64.decode(src, dst)

आख़िर आपने अभी क्या डिकोड किया? लगभग हर आम फ़ॉर्मेट के पहले बाइट्स एक तय साइनचर होते हैं, और चूँकि Base64 निर्धारक है, एन्कोडेड साइनचर भी तय है। इन प्रीफ़िक्स में से एक को देखना दूर से नंबर प्लेट पहचानने जैसा है:

Base64 किससे शुरू होता है शायद यह है
iVBORw0KGgo एक PNG तस्वीर
/9j/ एक JPEG तस्वीर
R0lGODlh एक GIF तस्वीर
JVBERi0 एक PDF दस्तावेज़
UEsDBA== एक ZIP आर्काइव
UklGRg== एक RIFF कंटेनर (WAV, WEBP, AVI)
LS0tLS1CRUdJTg== ASCII-कवच वाला ब्लॉक ("-----BEGIN ...")

और फ़ाइल लिखते वक़्त साइज़ का हिसाब भी कर लें, क्योंकि यही वह नंबर है जो डिस्क भरने पर लोगों को हैरान करता है: एन्कोडिंग डेटा को करीब एक-तिहाई फूल देती है, इसलिए 300 KB की फ़ाइल करीब 400 KB Base64 टेक्स्ट के रूप में यात्रा करती है, और जो फ़ाइल आप वापस डिकोड करते हैं, वह छोटी होती है - मूल साइज़ में। आपकी डिस्क, और आपकी मेमोरी अगर आप पूरी फ़ाइल एक साथ पढ़ते हों, उस अंतर का बजट रखनी चाहिए।

डेटाबेस, कॉन्फ़िग फ़ाइलें और एनवाइरनमेंट वेरिएबल

Base64 बाइनरी (या JSON) को सिर्फ़ टेक्स्ट स्वीकार करने वाली स्टोरेज से तस्करी करने का पसंदीदा रास्ता है: TEXT कॉलम, .ini फ़ाइल में वैल्यू, या डिप्लॉई पाइपलाइन में एनवाइरनमेंट वेरिएबल। डिकोडिंग की रेसिपी फ़ाइलों जैसी ही है, सिर्फ़ बिना डिस्क के:

import base64
import json
stored = "eyJyb2xlIjogImFkbWluIiwicHJvamVjdCI6Im15c2l0ZSJ9"
payload = json.loads(base64.b64decode(stored))
print(payload)
# {'role': 'admin', 'project': 'mysite'}

इस कोने के लिए दो नोट्स। जब संचित वैल्यू JSON हो, तो बीच वाला .decode("utf-8") स्टेप छोड़ दें और json.loads को बाइट्स सीधे लेने दें, क्योंकि Python 3.6 से वह ऐसा ही करता है। और एक ईमानदार चेतावनी, क्योंकि पूरे लेख का सबसे महँगा भ्रम यहीं रहता है: एनवाइरनमेंट वेरिएबल या कॉन्फ़िग फ़ाइल में Base64 उस इंसान के ख़िलाफ़ ढाल है जो फ़ाइल पर नज़र फेंकता है, उस इंसान के ख़िलाफ़ नहीं जो उसे पढ़ता है। अगर वैल्यू सच में संवेदनशील है, तो पहले उसे एन्क्रिप्ट करें (cryptography पैकेज बिल्कुल इसके लिए Fernet देता है) और तभी, अगर आपकी स्टोरेज टेक्स्ट मांगती है, सिफ़रपाठ को Base64 करें।

जब पेलोड टुकड़ों में पहुँचे

स्टैंडर्ड लाइब्रेरी में कोई क्रमिक Base64 डिकोडर नहीं है: update-and-finish की जोड़ी नहीं है, इसलिए स्ट्रीमिंग डेटा को थोड़ी अपनी-ही बही-खाती की ज़रूरत होती है। अंकगणित सादा और साथ ही सख़्त है। चार एन्कोडेड चर तीन बाइट्स बनाते हैं, इसलिए आप सिर्फ़ पूरे चार-चर समूह डिकोड कर सकते हैं, और शेष को अगले चंक में ले जाना पड़ता है:

import base64
def chunked_decode(chunks):
  out = []
  leftover = b""
  for chunk in chunks:
    buffer = leftover + chunk
    whole = len(buffer) // 4 * 4
    if whole:
      out.append(base64.b64decode(buffer[:whole]))
    leftover = buffer[whole:]
  if leftover:
    out.append(base64.b64decode(leftover + b"=" * (-len(leftover) % 4)))
  return b"".join(out)

इसे सॉकेट बफ़र, 64 KB टुकड़ों में पढ़ी गई फ़ाइल, या लाइन ब्रेक्स हटाकर लाइनों का जनरेटर खिलाएँ, और आउटपुट पूरे को एक साथ डिकोड करने जैसा ही मिलेगा। अगर आपका इनपुट साफ़ और बिना लपटने के गारंटी दे, तो सख़्ती बनाए रखें - हर पूरे समूह को validate=True से डिकोड करें - और याद रखें कि आख़िरी शेष को पैडिंग फ़िक्स की ज़रूरत हो सकती है, इसलिए हेल्पर आख़िरी डिकोड से पहले उसे जोड़ता है। यह वही सीम लॉजिक है जो एन्कोडर दूसरी तरफ़ इस्तेमाल करते हैं, सिर्फ़ तीन बाइट्स के बजाय चार चरों के साथ।

कमांड लाइन से

base64 मॉड्यूल एक छोटे से कमांड-लाइन टूल का काम भी करता है, जो तब उपयोगी है जब पेलोड आपके कोड के बजाय आपके टर्मिनल में बैठा हो। एन्कोडिंग डिफ़ॉल्ट है; -d (या उसका जुड़वा, -u) डिकोड करता है:

echo -n "hello world" | python3 -m base64
aGVsbG8gd29ybGQ=
echo -n "aGVsbG8gd29ybGQ=" | python3 -m base64 -d
hello world

अगर आप कोई फ़ाइल न दें तो वह stdin से पढ़ता है, वरना उस फ़ाइल से जिसे आप नाम दें, और अंदर से यह पुरानी फ़ाइल-से-फ़ाइल इंटरफ़ेस है, इसलिए आउटपुट 76 चरों पर लपटा आता है, हर लाइन पर आख़िरी न्यूलाइन के साथ। सख़्ती बढ़ाकर पेलोड को सेशन में पेस्ट करने के लिए, डिकोडर की एक-लाइन वर्ज़न एक अच्छी आदत है:

import base64
import sys
print(base64.b64decode(sys.stdin.read(), validate=True))

नौ तरीके जिनसे झुलसते हैं

Python में हर Base64 डिकोडिंग बग इनमें से एक है। इस सूची को ऐसे कहीं रखें कि घबराहट में वह आपको मिल जाए, क्योंकि इसने इस साल आपके पढ़ने वाले किसी भी एक दस्तावेज़ से ज़्यादा दोपहरें पकड़ी हैं। पहले तीन कोड के साथ आते हैं, क्योंकि बर्बादी देखने के बाद याद रखना आसान होता है:

ग़ायब पैडिंग। सबसे आम क्रैश, आमतौर पर इसलिए कि JWT का हिस्सा या API की वैल्यू अपने पैड के बिना पहुँची:

import base64
import binascii
segment = "Zm9vYmE"
try:
  base64.urlsafe_b64decode(segment)
except binascii.Error as caught:
  print(caught)
# Incorrect padding
padded = segment + "=" * (-len(segment) % 4)
print(base64.urlsafe_b64decode(padded))
# b'fooba'

काटा गया स्ट्रिंग। जब एरर कहती है कि डेटा चरों की गिनती "चार के गुणज से 1 ज़्यादा नहीं हो सकती", तो पेलोड रास्ते में कट गया, या कॉपी-पेस्ट ने आख़िर का एक चर गिरा दिया। कितनी भी पैडिंग जोड़ें, जिस स्ट्रिंग की लंबाई चार से बाँटने पर 1 शेष छोड़े, वह ठीक नहीं हो सकती; डेटा बस वहाँ नहीं है, और ईमानदार जवाब है कि पेलोड दोबारा माँगे।

ख़ामोश कचरा। सहनशील मोड जो बचता है, उसे डिकोड कर लेता है, और साधारण अंग्रेज़ी शब्दों में Base64-वर्णमाला के अक्षर भरए हैं, इसलिए पेलोड के सामने एक बेग़ार शब्द असली बाइट्स बनकर आपके डेटा से चिपक जाता है:

import base64
print(base64.b64decode("junkZm9vYmFy"))
# b'\x8e\xe9\xe4foobar' - तीन बाइट्स की बिल्कुल बेबुनियाद कहानी, फिर सच

बाकी छह को कोड की ज़रूरत ही नहीं:

  • आपने base64url स्ट्रिंग को स्टैंडर्ड डिकोडर से डिकोड किया। डैश और अंडरस्कॉर स्टैंडर्ड वर्णमाला में नहीं हैं, इसलिए वे ख़ामोशी से गायब हो गए और पेलोड कुचला हुआ, या ख़ाली, निकला। पैडिंग फ़िक्स के साथ urlsafe_b64decode इस्तेमाल करें।
  • आपने भूल गए कि नतीजा बाइट्स है। इसे स्ट्रिंग से चिपकाने पर TypeError उठती है, और इसे JSON रिस्पॉन्स में धकेलने पर b'...' प्रस्तुति सिरीलाइज़ हो जाती है। सीमा पर, जानबूझकर, .decode(encoding) कॉल करें, वही चरसेट जो आप असल में चाहते हैं।
  • आपने न-ASCII स्ट्रिंग पास की। डिकोडर स्ट्रिंग स्वीकार करता है, पर सिर्फ़ ASCII वाली; बाकी सब ValueError है। अगर आपका पेलोड ग़लत चरसेट से पढ़ी गई टेक्स्ट फ़ाइल से निकला है, तो पढ़ना ठीक करें, डिकोड नहीं।
  • आपने दो बार डिकोड किया। डेटा पहले से ऊपर की तरफ़ डिकोड हो चुका था, या वह Base64 के Base64 था, और दूसरी पारी में आपका पासवर्ड छह बाइट्स में बदल गया जो कोई इंसान कभी नहीं पढ़ेगा।
  • आपने लपटे डेटा पर सख़्त मोड इस्तेमाल किया। एक ही न्यूलाइन validate=True को उछालने के लिए काफ़ी है, इसलिए MIME ब्लॉक और PEM बॉडी सहनशील औजारों की दुनिया में हैं, सख़्त वाले की नहीं।
  • आपने बीच में पैड पर भरोसा किया। सहनशील मोड में स्ट्रिंग के कहीं भी = ख़ामोशी से फेंका जाता है, इसलिए ग़लत जगह पैड वाला टूटा पेलोड "सही" जवाब में डिकोड हो सकता है। सिर्फ़ सख़्त मोड नोटिस करता है, और वह मना करके नोटिस करता है।

अगर आपका काम गेटकीपर बनना है, तो यहाँ एक छोटा हेल्पर है जो दोनों मूडों को साथ काम पर लगाता है: पहले सख़्त, फिर पैडिंग फ़िक्स, और जब दोनों काम न आएं, तो शोरगुल भरी असफलता:

import base64
import binascii
def safe_decode(text):
  candidate = text.strip()
  try:
    return base64.b64decode(candidate, validate=True)
  except binascii.Error:
    padded = candidate + "=" * (-len(candidate) % 4)
    return base64.b64decode(padded, validate=True)
print(safe_decode("Zm9vYmE"))
# b'fooba'
print(safe_decode("Zm9vYmFy"))
# b'foobar'

ध्यान दें कि हेल्पर उसी वर्णमाला पर भरोसा करता है जिस पर भरोसा करने को कहा गया हो। अगर आपका इनपुट base64url हो सकता है, तो उसकी बजाय urlsafe_b64decode को खिलाएँ। वेरिफिकेशन एक अनुबंध है, और अनुबंध कहता है कि डेटा किस बोली में है।

एक ख़ामोश मॉड्यूल के तीन दशक

यह मॉड्यूल चौथाई सदी से स्टैंडर्ड लाइब्रेरी में है, और ज़्यादातर वक़्त वह स्थिर बैठा रहा। जब हिला, तो हलचल छोटी पर असली थी, और वे कुछ "मेरे मशीन पर काम करता है" कहानियों की व्याख्या करती हैं जो पुराने फ़ोरम में घूमती रहती हैं:

  • 1995 - Jack Jansen ने base64.py को फिर से लिखा ताकि असली काम C-स्तर के binascii मॉड्यूल को सौंपा जाए। वह टिप्पणी आज भी फ़ाइल में है, और वह सौपना आज भी सच है।
  • 2003, Python 2.4 में शिप किया - Barry Warsaw ने पूरा RFC 3548 सपोर्ट जोड़ा: b16, b32 और b64 परिवार, साथ ही standard_* और urlsafe_* वैरिएंट जो आप आज इस्तेमाल करते हैं।
  • Python 3.1 - encodestring और decodestring को encodebytes और decodebytes के पक्ष में अप्रचलित कर दिया गया, जो नाम टिके रहे।
  • Python 3.3 - डिकोड फ़ंक्शन ने ASCII स्ट्रिंग स्वीकार करना शुरू किया, उस दौर को ख़त्म करते हुए जब हर डिकोड बाइट्स लिटरल से शुरू होता था।
  • Python 3.4 - कोई भी बाइट्स जैसा ऑब्जेक्ट (memoryviews समेत) हर जगह स्वीकार हो गया, और Base85 के रिश्तेदार, a85 और b85, मॉड्यूल में शामिल हो गए।
  • Python 3.9 - लंबे वक़्त से अप्रचलित encodestring और decodestring को आख़िरकार हटा दिया गया। पुराने ट्यूटोरियल्स जो उन्हें कॉल करते हैं, उन्हें एक शब्द का नाम-बदलाव चाहिए।
  • Python 3.10 - b32hexencode और b32hexdecode विस्तृत हैक्सडेसिमल वर्णमाला के साथ आए, जो एन्कोडेड डेटा को शब्दकोशीय क्रम में क्रमबद्ध रखने लायक बनाती है।
  • Python 3.11 - binascii.a2b_base64 को strict_mode मिला, और यही वह है जिस पर validate=True अंदर से सवार होता है।
  • Python 3.13 - z85encode और z85decode ने ZeroMQ की Z85 बोली को स्टैंडर्ड लाइब्रेरी में लाया, और बहुत पुराना uu मॉड्यूल PEP 594 के तहत हटा दिया गया, उसके साथ base64 इस्तेमाल करने की सलाह देता एक तीखा नोट भी।
  • Python 3.14 - b16decode छह गुना तक तेज़ हो गया: उसकी वेरिफिकेशन अब bytes.translate पर चलती है, रेगुलर एक्सप्रेशन के बजाय, और मॉड्यूल अब re को बिल्कुल इम्पोर्ट नहीं करता। उसका इम्पोर्ट समय भी बेहतर मॉड्यूलों की सूची में आ गया।

इनमें से कुछ भी फ़ंक्शनों के काम को नहीं बदलता, और यही इतने पुराने मॉड्यूल की ख़ामोश लक्ज़री है: जो कोड 2005 में Base64 डिकोड कर रहा था, वह 2026 में भी डिकोड करता है, वही लाइन, वही नतीजा।

किनारों की ख़ुशीयाँ

गंभीर काम पूरा हो गया, इसलिए यहाँ वह छोटा-मोटा मनोरंजन है जो मॉड्यूल अपने किनारों में छुपाता है:

  • मॉड्यूल का अपना दस्तावेज़ीकरण दशक से ज़्यादा वक़्त से वही प्रदर्शन कर रहा है: b'data to be encoded' अंदर जाता है, b'ZGF0YSB0byBiZSBlbmNvZGVk' बाहर आता है। अगर आपने पिछले बीस सालों में किसी भी Python रिलीज़ की base64 पेज पढ़ी है, तो आप पहले से ही इस जोड़ी से मिल चुके हैं।
  • junk शब्द एक बिल्कुल वैध Base64 स्ट्रिंग है। चारों अक्षर वर्णमाला में हैं, इसलिए पेलोड की शुरुआत में कोई बेग़ार शब्द एरर के बजाय काल्पनिक तीन बाइट्स बन जाता है, और इसलिए ही सहनशील मोड अपना उपनाम कमाता है।
  • urlsafe_b64decode बेख़ूबी से दो-भाषी है। यह पहले अपनी वर्णमाला का अनुवाद करता है और फिर सहनशीलता से डिकोड करता है, इसलिए वह + और / वाली स्टैंडर्ड-वर्णमाला स्ट्रिंग भी पढ़ लेगा। एक फ़ंक्शन, दो बोलियाँ, शून्य शिकायतें।
  • एरर मैसेज एक स्थिर छोटा सा शब्दकोश है जो C क्रियान्वयन से अब तक नहीं बदला है: Incorrect padding, Only base64 data is allowed, Excess padding not allowed, Leading padding not allowed। इन्हें सीख लें और आप एक भी लाइन कोड चलाए बिना टूटे पेलोड की पहचान कर सकते हैं।
  • ख़ाली स्ट्रिंग एकमात्र इनपुट है जिस पर बिल्कुल कोई प्रतिक्रिया नहीं: b'' अंदर, b'' बाहर, दोनों मूडों में। कुछ भी अंदर नहीं, कुछ भी बाहर नहीं, कोई अलार्म नहीं।
  • मॉड्यूल का डॉक्स्ट्रिंग अभी भी RFC 3548 का नाम लेता है, स्पेसिफिकेशन की 2003 संस्करण। RFC 4648 2006 से मौजूदा मानक है, और मॉड्यूल उसका निष्ठापूर्वक पालन करता है, बिना उस वाक्य को अपडेट करने की परवाह किए।
  • Python 2 में डिकोडिंग की तरफ़ कोई टाइप दीवार नहीं थी: सादा str अंदर, सादा str बाहर। Python 3 के विकास की 2007 की bytes-सफ़ाई ने उसे बदल दिया, और ज़्यादातर "मेरा डिकोड क्यों टूटा है" थ्रेड अभी भी पुराने Python 2 ट्यूटोरियल्स की तरफ़ इशारा करते हैं।

तो यहाँ पूरा दर्शन चार नियमों में है। जो कुछ आपने खुद एन्कोड नहीं किया, उसके लिए validate=True पास करें, और एक्ससेप्शन को असली जवाब की तरह समझें, सुझाव की तरह नहीं। जानें कि आप कौन सी बोली पकड़े हैं: स्टैंडर्ड, base64url, या MIME-लपटी, क्योंकि डिकोडर आपको नहीं बताएगा; वह सिर्फ़ अनुमान लगाएगा - जो जगह नहीं पाता, उसे फेंककर। नतीजे को बाइट्स की तरह ही समझें जब तक आपने साबित नहीं किया कि वह टेक्स्ट है, और फिर पूछें कि चरसेट किसका था। और याद रखें कि इस फ़ंक्शन की सबसे दोस्ताना खासियत - बिल्कुल Base64 न होने वाली चीज़ों को डिकोड करने की तैयारी - वही खासियत है जो इसे ख़तरनाक बनाती है, इसलिए हर कॉल पर तय करें कि इनपुट ने कितना भरोसा कमाया है।

अगर कभी आपको उल्टी तरफ़ जाना हो - ताज़े बाइट्स को टोकन, अटैचमेंट, या इनलाइन तस्वीर के लिए उसी दोस्ताना अक्षरों की रीबन में वापस लपेटना - तो b64encode का पूरा क़िस्सा इस पेज के नीचे जुड़े Base64 एन्कोडिंग लेख में विस्तार से दिया गया है। दो दिशाएँ एक-दूसरे का आइना हैं, पर हर एक के अपने सरप्राइज़ हैं, और आप अब यह एक रट चुके हैं। खुशी से डिकोड करें।

अंतिम अपडेट: 2026-09-08

संबंधित लेख: Python में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड