PowerShell में Base64 डिकोडिंग: एक सम्पूर्ण गाइड
किसी लॉग लाइन, किसी कॉन्फ़िग फ़ाइल या किसी एरर मैसेज में, आप इससे टकरा जाते हैं: अक्षरों और अंकों की एक लंबी धार, बीच-बीच में कभी एक प्लस या स्लैश, और आख़िर में शक़-शक़ से पड़े एक-दो बराबर चिह्न। यह शोर जैसा दिखता है। ऐसा नहीं है। यह Base64 है, और आपको पहले से पता है कि आप क्या चाहते हैं: वह चीज़ जो वह छिपा रहा है।
Base64 एक अनुवाद है, न कि कंप्रेशन, और न ही कोई लॉक। यह किसी भी बाइट्स की पंक्ति को प्रिंट होने लायक टेक्स्ट में फिर से लिख देता है: हर तीन इनपुट बाइट्स के बदले चार चर (इसलिए एन्कोड डेटा मूल से करीब 33% बड़ा निकलता है), और इसका सहारा है 64 चरों की वर्णमाला, जिसमें आख़िरी पैडिंग के लिए बराबर का चिह्न भी जुड़ा है। इस साइट का होम पेज वर्णमाला, बिट गणित और सभी वैरिएंट पूरे रूप में समझाता है, इसलिए यह लेख अपना वक़्त वहीँ लगाता है जहाँ PowerShell असल फ़र्क़ पैदा करता है: वह एक ही .NET मेटोड जिसे आप कॉल करेंगे, वह नियम जो वह मज़बूती से लागू करता है, और असल काम के दस-बारह ऐसे कोने जहाँ PowerShell में डिकोडिंग दिलचस्प होने लगती है।
मेटोड और उसका अनुबंध
PowerShell के पास अपना कोई Base64 कमांडलेट नहीं है। यह काम एक .NET क्लास के मेटोड से होता है, जो 2003 में .NET Framework 1.1 से फ्रेमवर्क का हिस्सा है, यानी PowerShell के अपने रिलीज़ होने से तीन साल पहले:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
यह पूरा API है: एक स्ट्रिंग अंदर, एक बाइट्स ऐरे बाहर। यह हर ऑपरेटिंग सिस्टम पर हर PowerShell में काम करता है, Windows PowerShell 5.1 में और Windows, Linux, macOS पर PowerShell 7 में, क्योंकि यह बस .NET है। यह अनुबंध इतना छोटा है कि रट लिया जा सके, इसलिए यहाँ वह टेबल के रूप में है:
| इनपुट | वापस आपको क्या मिलता है |
|---|---|
$null |
खाली ऐरे, कोई एरर नहीं। कॉल से पहले PowerShell $null को खामोशी से खाली स्ट्रिंग में बदल देता है |
| खाली स्ट्रिंग | खाली ऐरे, कोई एरर नहीं |
| सही पेलोड | एक byte[], कभी कोई स्ट्रिंग नहीं, भले ही डेटा टेक्स्ट हो |
| गलत पेलोड | एक FormatException, जो आपको MethodInvocationException के अंदर सँवाँकर मिलती है |
कोई भी एरर हैंडलिंग लिखने से पहले एक चेतावनी: उस FormatException में एक ही मैसेज है जो तीन अलग-अलग गुनाहों को एक साथ ढकता है। वर्णमाला से बाहर कोई चर, दो से ज़्यादा पैडिंग चर, या पैडिंग के बीच छिपा कोई नॉन-व्हिटस्पेस चर, तीनों बिल्कुल वही एक वाक्य पैदा करते हैं। जब वह सामने आए, तो मैसेज आपको नहीं बताएगा कि आपने कौन-सा गुनाह किया, इसलिए वापस जाकर अपने इनपुट को पढ़ें:
try {
[System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
$real = $_.Exception.InnerException
$real.GetType().Name
# FormatException
$real.Message
}
और चार के गुणज के नियम में एक ऐसा किनारा है जो लोग पहली बार टकराने पर हैरान हो जाते हैं। बिना किसी पैडिंग के चार चर बिल्कुल वैध हैं; इसका मतलब बस इतना है कि आख़िरी चर के बचे-खुचे बिट फेंक दिए जाते हैं। तीन चर चार के गुणज नहीं होते, इसलिए अस्वीकार हो जाते हैं:
[System.Convert]::FromBase64String("SGVs").Count
# 3: बिना पैडिंग के चार चर ठीक हैं
[System.Convert]::FromBase64String("SGV")
# FormatException: तीन चर चार के गुणज नहीं हैं
डिकोडर क्या लेगा और क्या नहीं
डिकोडर वर्णमाला की बात में सख़्त है, और बस एक खास चीज़ में नरम। वैध चर हैं 64 Base64 अंक (A से Z तक, a से z तक, 0 से 9 तक, साथ में प्लस और स्लैश), और आख़िरी पैडिंग के लिए बराबर का चिह्न। बिल्कुल चार व्हिटस्पेस चर को अनदेखा किया जाता है, चाहे वे कहाँ पर कितनी बार आएं: टैब, लाइन फीड, कैरिज रेटर्न और स्पेस। आधिकारिक .NET डॉक्यूमेंटेशन इन्हें उनके Unicode नामों से सूचीबद्ध करता है, और इससे पता चलता है कि यह दस्तावेज़ीकृत गारंटी है, किसी भाग्यशाली ग़लती की वजह से नहीं।
अमल में यह एक सुपरपावर है। MIME, वह मेल एन्कोडिंग जिसने Base64 को मशहूर किया, एन्कोड लाइनों को 76 चर पर रैप करती है, इसलिए कोई पेलोड जो मेल, टिकट या लॉग फ़ाइल से गुज़रा हो, आमतौर पर कई लाइनों में टूटकर आता है। डिकोडर को फ़र्क़ नहीं पड़ता। इसे जैसा है वैसे ही पेस्ट करें:
$wrapped = "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4gQmFzZTY0IHRleHQg`r`n" +
"YXJyaXZlcyB3cmFwcGVkIGF0IHNldmVudHktc2l4IGNvbHVtbnMgaW4gbWFpbCwgc28gdGhlIGRl`r`n" +
"Y29kZXIgbXVzdCBub3QgY2FyZS4="
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($wrapped))
# The quick brown fox jumps over the lazy dog. Base64 टेक्स्ट आता है
# मेल में 76 चर पर रैप, इसलिए डिकोडर को इसकी चिंता नहीं।
बाक़ी हर वह चीज़ जो वर्णमाला का चर नहीं है, वह पक्की रुकावट है। बाहर की दुनिया में सबसे आम दोषी हैं नॉन-ब्रेकिंग स्पेस (वेब पेजों से पेस्ट किए गए टेक्स्ट का फ़ेवरेट) और बाइट-ऑर्डर मार्क (वह अदृश्य चिह्न जो पीछे-पीछे चलता है जब फ़ाइल गलत एन्कोडिंग से पढ़ी गई हो)। इस मेटोड की नज़र में न तो एक व्हिटस्पेस है और न दूसरा, इसलिए दोनों एक्सेप्शन फेंकते हैं:
try {
[System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
$_.Exception.InnerException.GetType().Name
# FormatException
}
यह सख़्ती जानबूझकर है, न कि फ़िजूल के पुरजोशपन से। RFC 4648, वह स्टैंडर्ड जिसने 2006 में Base64 को लिखित नियमों में पिरोया, कहता है कि इम्प्लीमेंटेशन को नॉन-वर्णमाला चर अस्वीकार करने हों, जब तक कि प्रोटोकॉल खुद अनुग्रह की अनुमति न दे, क्योंकि एक ऐसा डिकोडर जो बिना शोर मचाए पराये चर निगल ले, उसे गुप्त चैनल के रूप में इस्तेमाल किया जा सकता है, जिससे डेटा को बस वर्णमाला ही जाँचने वाले हर फ़िल्टर से चुपके से निकाला जा सके। .NET डिकोडर सख़्त नियम का पालन करता है, और आमतौर पर आप यही चाहेंगे।
बाइट्स ऐरे स्ट्रिंग नहीं है
यह मेटोड जानबूझकर बाइट्स ऐरे पर रुक जाता है। उन बाइट्स का मतलब क्या है, यह दूसरा फैसला है जिसे सिर्फ़ आप ले सकते हैं, और ग़लत अंदाज़ा लगाना PowerShell Base64 काम की सबसे मशहूर ग़लती है। डिफ़ॉल्ट मान, UTF-8, इंटरनेट की लगभग हर चीज़ के लिए सही है, और राउंड ट्रिप बस दो कॉल है:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
एन्कोडिंग्स जो आप असल में इस्तेमाल करेंगे, और हर एक ग़लत अंदाज़े पर क्या करती है:
| एन्कोडिंग | कब इस्तेमाल करें | ग़लत अंदाज़ा लगने पर |
|---|---|---|
UTF8 |
वेब API, JSON, JWTs, आधुनिक सब कुछ। सुरक्षित डिफ़ॉल्ट | Latin-1 या UTF-16 टेक्स्ट मोज़िबैक के रूप में लौटता है |
Unicode (UTF-16LE) |
पेलोड Windows टूलिंग, किसी रेजिस्ट्री वैल्यू, या किसी .NET स्ट्रिंग से आया है जिसे भेजने से पहले एन्कोड कर दिया गया था | हर चर के चारों तरफ़ एक खाई दिखने लगती है, क्योंकि जहाँ दो बाइट्स होने थे वहाँ आपने एक पढ़ा |
ASCII |
क्लासिक HTTP Basic क्रेडेंशियल्स और अन्य गारंटीड 7-बिट प्रोटोकॉल | मान 127 से ऊपर की हर चीज़ सवाल चिह्न बन जाती है |
Latin1 |
लीगेसी यूरोपीय टेक्स्ट, जो UTF-8 से पहले का है | मल्टी-बाइट UTF-8 सीक्वेंस कई ग़लत अक्षरों में बँट जाते हैं |
Default |
लगभग कभी नहीं। यह मशीन का सिस्टम कोड पेज है | आपकी स्क्रिप्ट हर Windows रीजन सेटिंग पर अलग-अलग व्यवहार करेगी |
क्लासिक विफलता है UTF-8 टेक्स्ट का UTF-16 के रूप में डिकोड होना। बाइट्स असली हैं, मेटोड खुश है, और फिर भी रिज़ल्ट गार्बेज है:
# "SGk=" "Hi" के UTF-8 बाइट्स हैं
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# एक अगुज़ार चर: 2 UTF-8 बाइट्स को एक 2-बाइट UTF-16 इकाई के रूप में पढ़ा गया
प्राक्टिकल नियम: अगर डिकोड टेक्स्ट ऐसा दिखे कि हर चर के चारों तरफ़ एक अदृश्य खाई है, या कोई और वर्णमाला जैसा लगे, तो आप एक एन्कोडिंग ग़लत हैं। पूछें कि डेटा कहाँ पैदा हुआ, और संदेह हो तो UTF-8 पर भरोसा करें, लेकिन पहले कुछ चरों को अपनी आँखों से जाँचें। और एन्कोडिंग का फैसला डिकोड करने से पहले करें, तब नहीं जब आपकी लॉग में मोज़िबैक आकर नज़र आए।
base64url: वह वर्णमाला जो URL में नम्र रहती है
हर API टोकन, JWT और URL में बेठे हुए ID में आप Base64 के एक रिश्तेदार से मिलेंगे। स्टैंडर्ड Base64 का प्लस और स्लैश URL में बस परसेंट-एन्कोडिंग के बाद ही क़ानूनी हैं, और बराबर चिह्न की पैडिंग किसी फ़ील्ड सेपरेटर जैसी दिखती है। इसलिए RFC 4648 ने एक URL- और फ़ाइल-नाम-सुरक्षित वर्णमाला दी: वही 64 चर, सिर्फ़ इतना फ़र्क़ कि प्लस की जगह हाइफ़न आया और स्लैश की जगह अंडरस्कोर। पैडिंग आमतौर पर पूरी तरह छोड़ दी जाती है, क्योंकि डेटा की लंबाई उसे ज़रूरी नहीं छोड़ती। RFC ख़ास तौर पर कहता है कि इस वैरिएंट को base64url कहें, बस "base64" नहीं, और इस सेक्शन का बाक़ी हिस्सा यही राह रखता है।
.NET में इसका एक खास क्लास है, System.Buffers.Text.Base64Url, जो .NET 9 में जुड़ी, तेज़ एन्कोड और डिकोड मेटोड्स के साथ जो बिल्कुल ReadOnlySpan<T> पैरामीटर के चारों तरफ़ बने हैं। वर्तमान PowerShell (7.4 और उससे आगे, जब वह किसी .NET वर्ज़न पर चल रहा हो जिसमें यह क्लास मिलती है) आज इन स्पैन लेने वाले ओवरलॉड्स को सिधे हाथ से कॉल कर सकता है, इसलिये कि मेटोड बाइंडर अब एक अप्रत्यक्ष ऐरे/स्ट्रिंग-से-स्पैन कन्वर्ज़न करता है, इसलिए [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") बिना किसी औपचारिकता के काम करता है। हमेशा ऐसा नहीं रहा: Windows PowerShell 5.1 और पुरानी PowerShell 7.x रिलीज़ स्पैन पैरामीटर से बंध ही नहीं पाती थीं, और .NET 9 से पहले यह क्लास मौजूद ही नहीं थी, इसलिए हर उस स्क्रिप्ट को पोर्टेबल वर्ज़न चाहिए जो 5.1, किसी पुरानी 7.x, या .NET 9 से पहले के किसी होस्ट पर चलनी है: दो चरों का बदले-बदल, और टेक्स्ट को स्टैंडर्ड डिकोडर को सौंपने से पहले पैडिंग वापस जोड़ना। जो पैडिंग जोड़नी है, वह उतनी ही, जितनी से लंबाई चार का गुणज हो जाए:
$token = "--__AQI" # base64url, बिना पैडिंग
$standard = $token.Replace("-", "+").Replace("_", "/")
$pad = 4 - ($standard.Length % 4)
if ($pad -eq 4) { $pad = 0 }
$standard = $standard.PadRight($standard.Length + $pad, "=")
$bytes = [System.Convert]::FromBase64String($standard)
$bytes -join ","
# 251,239,255,1,2
उस छोटे से ब्लॉक में दो फँदे हैं। पहला: पैडिंग की गणित। अगर पेलोड की लंबाई पहले से चार का गुणज है तो कोई पैडिंग ज़रूरी नहीं, और -eq 4 की जाँच ही इस अभिव्यक्ति को सही राह पर खड़ी रखती है। दूसरा: दिशा। जब आप सिर्फ़ डिकोड कर रहे हैं, तो आप पैडिंग जोड़ते हैं और चर बदलते हैं; स्टैंडर्ड Base64 इनपुट से पैडिंग कभी नहीं हटाते, क्योंकि स्टैंडर्ड डिकोडर उसे वहाँ मौजूद होने की उम्मीद करते हैं। अगर स्रोत JWT या API टोकन है, तो वह बिना पैडिंग वाला base64url होगा, और ऊपर की रीसिपी बिल्कुल वही शक्ल है जो आपको चाहिए।
बिना की के JWT खोलना
JSON Web Token तीन base64url सेगमेंट हैं जो डॉट से जुड़े होते हैं: हेडर, पेलोड, सिग्नेचर। पहले दो सादा JSON होते हैं, और Base64 एन्क्रिप्शन नहीं है, इसलिए जिसके पास टोकन है वह दोनों पढ़ सकता है। यह एक ख़ूबी है, न कि दोष: टोकन इसीलिए बनाया गया है कि उसमें झाँका जाए, और सिग्नेचर ही उसे नक़ल से बचाता है। PowerShell इस झलक को एक तीन-लाइनर बना देता है:
$jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
$parts = $jwt.Split(".")
function Decode-UrlSegment([string]$segment) {
$standard = $segment.Replace("-", "+").Replace("_", "/")
$pad = 4 - ($standard.Length % 4)
if ($pad -eq 4) { $pad = 0 }
$standard = $standard.PadRight($standard.Length + $pad, "=")
return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($standard))
}
Decode-UrlSegment $parts[0] | ConvertFrom-Json | ConvertTo-Json -Compress
Decode-UrlSegment $parts[1] | ConvertFrom-Json
# name प्रॉपर्टी:
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe
तीन बातें याद रखें। तीसरा सेगमेंट, सिग्नेचर, वह भी base64url है, लेकिन यह बिनरी सिग्नेचर बाइट्स में डिकोड होता है, टेक्स्ट में नहीं, इसलिए वहाँ सुंदर JSON की उम्मीद न करें। हेडर आमतौर पर बस यह बताता है कि कौन-सा एल्गोरिथम ने टोकन पर हस्ताक्षर किए (HS256, RS256, ...), और ऐसा हेडर जिसमें none लिखा हो, वह लाल फ़्लैग है, सुविधा नहीं। और पेलोड पढ़ना उस पर भरोसा करना नहीं है: base64 आपको क्लेम्स देखने देता है, उन्हें असली सिर्फ़ सिग्नेचर बनाता है। अगर आपका काम टोकन स्वीकारना है, तो जारीकर्ता की की से सिग्नेचर की जाँच करें; अगर आपका काम किसी टोकन की डीबगिंग है, तो ऊपर का कोड आपकी ख़ुशअमानी है।
फ़ाइलें, PEM, और बाइट्स तक का लंबा रास्ता
सबसे आम फ़ाइल-शक्ल यह है: एक टेक्स्ट फ़ाइल जिसमें किसी बड़ी चीज़ का Base64 है - बैकअप ब्लॉब, डाउनलोड हुई बिनरी, या सीरीयलाइज़ड ऑब्जेक्ट। राउंड ट्रिप चार लाइन का है, और आउटपुट पढ़ने का आधुनिक तरीका है असली बाइट्स ऐरे, टेक्स्ट का अनुमान नहीं:
$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# टेक्स्ट कितने बाइट्स ढो रहा था
मूल बिनरी को वापस पढ़ना वही जगह है जहाँ PowerShell 6 और नए वर्ज़न अपना काम साबित करते हैं। -AsByteStream पैरामीटर राव बाइट्स पढ़ता है, और -Raw के साथ वह आपको एक ही झटके में एक असली byte[] सौंपता है:
$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# मूल साइज़, 33 फ़ीसदी के टेक्स्ट-टैक्स से पहले
-Raw छोड़ दें तो आपको अलग-अलग बाइट ऑब्जेक्ट्स की एक स्ट्रीम मिलती है (कैप्चर करने पर Object[]), जो जाँच-पड़ताल के लिए ठीक है, लेकिन उन .NET मेटोड्स के लिए ग़लत है जो ऐरे की उम्मीद करते हैं। और Windows PowerShell 5.1 में -AsByteStream का नाम-निशान ही नहीं है, इसलिए 5.1 पर भरोसेमंद पढ़ने का तरीका है [System.IO.File]::ReadAllBytes(), जो हर जगह मौजूद है।
PEM वह कवच-धारी रिश्तेदार है जिसे आप हर सर्टिफिकेट और प्राइवेट की से जानते हैं: एक स्टैंडर्ड Base64 बॉडी, आमतौर पर 64 चर पर रैप की हुई, -----BEGIN ... और -----END ... लाइनों के बीच। कवच टेक्स्ट है; बॉडी पेलोड है। कवच उतारें, लाइनें जोड़ें, डिकोड करें:
$pem = Get-Content -Path ./certificate.pem -Raw
$body = ($pem -split "`n") | Where-Object { $_ -notmatch "^-----" } | ForEach-Object { $_.Trim() }
$der = [System.Convert]::FromBase64String(($body -join ""))
$der.Length
# सर्टिफिकेट का बिनरी DER साइज़
क्योंकि स्टैंडर्ड डिकोडर व्हिटस्पेस को बस अनदेखा ही करता है, -join "" कोई ज़रूरत नहीं बल्कि दोहरी सावधानी है, लेकिन स्क्रिप्ट को स्पष्ट रखना कि वह क्या निकाल रही है, उसे हर मशीन पर और हर लाइन-एंडिंग रिवाज़ में एक जैसा व्यवहार करने लाता है। दूसरी दिशा - DER बाइट्स को PEM में रैप करना - बस Base64 एन्कोडर और दो टेक्स्ट लाइनें है, और बहन साइट का एन्कोडिंग लेख 64-कॉलम व्रैप पूरा दिखाता है।
सर्टिफिकेट्स और Windows टूलबॉक्स
रोज़मर्रा के काम में सर्टिफिकेट्स सबसे भारी Base64 नागरिक हैं, और PowerShell पूरा परिवार ढो सकता है। PFX फ़ाइल सर्टिफिकेट और प्राइवेट की की बिनरी बंडल है, और यही वह फॉर्मेट है जो आप सबसे अक्सर कॉन्फ़िग फ़ाइलों और डिप्लॉयमेंट स्क्रिप्ट्स में Base64 टेक्स्ट की शक्ल में मिलते देखेंगे। इसे वापस एक चालू सर्टिफिकेट में डिकोड करना .NET टाइप के साथ एक वन-लाइनर है, और यह PowerShell 7 में क्रॉस-प्लेटफ़ॉर्म काम करता है:
$bytes = [System.Convert]::FromBase64String($pfxText)
$password = ConvertTo-SecureString "secret" -AsPlainText -Force
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($bytes, $password)
$cert.Subject
# CN=example.org
$cert.NotAfter
# जब यह ठीक होना बंद कर दे
PowerShell 7 Get-PfxCertificate भी देता है, जो PFX फ़ाइल को सीधे डिस्क से -Password पैरामीटर के साथ पढ़ता है, इसलिए डिस्क पर रखी फ़ाइलों के लिए आप मैन्युअल डिकोड पूरी तरह छोड़ सकते हैं। एक बंदा सर्टिफिकेट (बिना की के) और भी आसान है: DER बाइट्स सीधे उसी X509Certificate2 टाइप में चले जाते हैं, बिना किसी पासवर्ड के।
भाषा के बाहर, दो नेटिव टूल जानने लायक हैं। Windows पर certutil -decode infile.b64 outfile Base64 फ़ाइल को फ़ाइल-इन/फ़ाइल-आउट सेमेंटिक्स के साथ डिकोड करता है (ओवरराइट करने के लिए -f जोड़ें), जिससे यह सादे कमांड प्रॉम्प्ट में तेज़ ठीक-थकाइयों का पक्का सहारा बनता है। उसका भाई certutil -encode में एक फ़्लैग याद रखने लायक है: -unicodetext इनपुट टेक्स्ट को Base64-एन्कोड करने से पहले UTF-16 में बदल देता है, और पूरा एन्कोडिंग फैसला एक स्विच के अंदर छुपा देता है। Linux और macOS पर क्लासिक यूटिलिटी है base64 -d, जो फ़ाइल या स्टैंडर्ड इनपुट डिकोड करता है, डिफ़ॉल्ट रूप से लाइन ब्रेक्स को छोड़कर; GNU coreutils में अगर पेलोड में Windows मेल से आए स्पेस, टैब या CRLF भी हों, तो -i जोड़ें।
Base64 एनवेलॉप में कमांड
वर्ज़न 1.0 से PowerShell को Base64 बोलने का एक बिल्ट-इन कारण रहा है: होस्ट का अपना -EncodedCommand पैरामीटर। आप pwsh को एक Base64 स्ट्रिंग सौंपते हैं, वह बाइट्स को UTF-16LE के रूप में डिकोड करता है, और परिणाम कमांड के रूप में चला दिया जाता है। आधिकारिक उद्देश्य, सीधे डॉक्यूमेंटेशन से, यह है कि ऐसी कमांड भेजी जा सकें जो जटिल क्वोटेशन मार्क्स या कर्ली ब्रेसेस माँगती हों, बिना बाहरी शेल की क्वोटिंग नियमों से लड़ें:
$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello
दूसरी लाइन को ध्यान से पढ़ें, क्योंकि यहीं सब ठोकर खाते हैं: पेलोड UTF-16LE होना चाहिए, यानी [System.Text.Encoding]::Unicode। अगर आप कमांड को UTF-8 में एन्कोड कर दें, तो PowerShell खुशी-खुशी उसे UTF-16LE के रूप में डिकोड कर लेगा और एक मोज़िबैक से बनी कमांड चला देगा, और जो एरर मैसेज वह पैदा करेगा वह उस ग़लती की पक्की तस्वीर होगी:
$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# एरर: गढ़े-मुढ़े चरों की दीवार, "The term ... is not recognized..."
यही मेकानिज़म है जो सुरक्षा टीमों को PowerShell में Base64 की परवाह करने लाता है। -EncodedCommand को दी गई एक लंबी अस्पष्ट टोकन ऑटोमेटेड टूलिंग की आम शक्ल है, और बस इसलिए एंडपॉइंट प्रोटेक्शन प्रोडक्ट्स ये पेलोड चलेने से पहले डिकोड कर लेते हैं: Base64 में कुछ भी ऐसा नहीं है जो कमांड को किसी डिकोडर से छुपाए, यह बस उसे उस इंसान से छुपाता है जो प्रोसेस लिस्ट पढ़ रहा हो। अगर आप अपने ऑटोमेशन के लिए एन्कोडेड कमांड बनाते हैं, तो टोकन के साथ ही स्रोत कमांड रखें, क्योंकि टोकन खुद रात तीन बजे खुद को बयान नहीं करेगा।
जब इनपुट बहुत बड़ा हो तो डिकोडिंग
रोज़मर्रा के साइज़ के लिए एक-मेटोड वाला रास्ता ही तेज़ है। पाँच मेगाबाइट की बिनरी करीब 69 लाख चरों की स्ट्रिंग बन जाती है, और उस स्ट्रिंग की डिकोडिंग एक आधुनिक मशीन पर एक अंकों के मिलीसेकंड में हो जाती है। .NET डॉक्यूमेंटेशन की अपनी नोट यह है कि FromBase64String का डिज़ाइन पूरा डेटा संभालने वाली एक ही स्ट्रिंग पर काम करने के लिए है, जो सच है, और बहुत बड़ी हद तक ठीक भी है, क्योंकि मेटोड स्ट्रिंग पर ही काम करता है, कोई मतलब वाली अतिरिक्त कॉपी नहीं करता।
जब पेलोड एक स्ट्रिंग में संभालने से बड़ा हो, या स्ट्रीम के रूप में आता हो (डाउनलोड, सॉकेट, बहुत बड़ा लॉग), तो डॉक्यूमेंटेड टूल है System.Security.Cryptography.FromBase64Transform, जो CryptoStream में सँवाँ होता है: आप इसे Base64 टेक्स्ट खिलते हैं और डिकोड बाइट्स बाहर पढ़ते हैं, और हर पल ज़िंदा सिर्फ़ एक छोटा बफ़र होता है। ध्यान रखें कि TransformStream, इसका C# हेल्पर, एक एक्सटेंशन मेटोड है, और PowerShell को एक्सटेंशन मेटोड्स दिखते ही नहीं, इसलिए CryptoStream को सीधे बनाना पड़ता है:
$inputStream = [System.IO.File]::OpenRead("./payload.b64")
$transform = [System.Security.Cryptography.FromBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new(
$inputStream, $transform, [System.Security.Cryptography.CryptoStreamMode]::Read)
$destination = [System.IO.File]::Create("./payload.bin")
$buffer = New-Object byte[] 65536
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$destination.Write($buffer, 0, $read)
}
$destination.Dispose()
$stream.Dispose()
$inputStream.Dispose()
नब्बे फ़ीसदी काम के लिए सादा रास्ता ही सही रास्ता है: पूरी टेक्स्ट फ़ाइल Get-Content -Raw से पढ़ें, ट्रिम करें, डिकोड करें, बाइट्स लिखें। जब फ़ाइल इतनी बड़ी हो कि मेमोरी में आराम से संभाली ही न जाए, या डेटा टुकड़ों में आ रहा हो, तब स्ट्रीम वर्ज़न की ओर हाथ बढ़ाएं। और लाइनों पर लूप चलाकर हर लाइन को अलग-अलग डिकोड करने की कोशिश न करें: Base64 के चार चरों के ग्रुप आपकी लाइन ब्रेक्स को मानते नहीं, इसलिए वह लाइन जो किसी ग्रुप को बीच से काट दे, अकेली डिकोड ही नहीं होगी। पूरा टेक्स्ट पढ़ें, फिर एक बार डिकोड करें।
ऐसे फँदे जो पूरी दोपहर खा जाते हैं
- चरसेट का अंदाज़ा। UTF-8 को UTF-16 के रूप में पढ़ना, या Latin-1 को UTF-8 के रूप में, आत्मविश्वास भरा मोज़िबैक पैदा करता है। एन्कोडिंग का फैसला डेटा के स्रोत से करें, डिफ़ॉल्ट UTF-8 रखें, और बाक़ी पर भरोसे से पहले पहले कुछ डिकोड चरों को देख लें।
- वेब से आए अदृश्य चर। पेज या रिच-टेक्स्ट ईमेल से पेस्ट किया गया नॉन-ब्रेकिंग स्पेस या बाइट-ऑर्डर मार्क डिकोडर की नज़र में पराया चर है, और वह सामान्य
FormatExceptionफेंकता है। डिकोड करने से पहले इनपुट को.Trim()और नॉन-प्रिंटिंग चर की जाँच से गुज़ारें। - पैडिंग की भ्रामकता। स्टैंडर्ड Base64 आख़िर में
=या==के साथ आता है; टोकन से आने वाला base64url बिना पैडिंग के आता है। एक को दूसरे के लिए बनी रीसिपी में डालना API काम में सबसे आम ख़ामोश टूट है, और base64url सेक्शन की लंबाई-जाँच ही इसका गार्ड है। - एक मैसेज, तीन गुनाह। क्योंकि
FormatExceptionका मैसेज बुरे चर, ज़्यादा पैडिंग और गंदा पैडिंग - तीनों को एक साथ ढकता है, वह catch ब्लॉक जो बस मैसेज लॉग करता है, आपको घुमाता-फिराता रहता है। इनपुट की लंबाई और पहली ग़लत जगह को भी लॉग करें। - वापस स्ट्रिंग की उम्मीद। रिज़ल्ट हमेशा बाइट्स ऐरे होता है। जिस पल आप उसे सिधे स्ट्रिंग-फ़ॉर्मेट करने लगें, आपको टेक्स्ट की जगह अंकों की सूची मिलती है। एक बार, आख़िर में, और एक खुलकर बताई एन्कोडिंग के साथ कन्वर्ट करें।
- 5.1 की फ़ाइल-डिफ़ॉल्ट। Windows PowerShell 5.1 BOM-रहित फ़ाइलों को सिस्टम की ANSI कोड पेज से पढ़ता है, जबकि PowerShell 7 UTF-8 मानता है। अगर आपकी स्क्रिप्ट 5.1 पर Base64 टेक्स्ट फ़ाइल पढ़ती है और फ़ाइल UTF-8 है जिसमें पेलोड के चारों तरफ नॉन-ASCII चीज़ें हैं, तो नुकसान डिकोडर के देखने से पहले ही हो चुका होता है।
- Base64 को लॉक समझना। यह एक अनुवाद है। Base64 में रखा पासवर्ड, टोकन या सीक्रेट बस सादा टेक्स्ट है जिसने कॉस्ट्यूम पहना है, और पृथ्वी का हर डिकोडर, यह लेख समेत, उसे एक लाइन में खोल देता है।
आदतें जो स्क्रिप्ट्स को ईमानदार रखती हैं
- बाहरी इनपुट को डिकोड से पहले ट्रिम करें। एक
.Trim()किसी भी एरर हैंडलर से ज़्यादा प्रोडक्शन इनसिडेंट हटा देता है। - जब स्रोत भरोसेमंद न हो, तो डिकोड से पहले वैलिडेट करें: चार अनुमत व्हिटस्पेस चर हटाने के बाद, स्ट्रिंग में सिर्फ़ वर्णमाला के चर होने चाहिए, और आख़िर में ज़्यादा से ज़्यादा दो बराबर चिह्न। एक तेज़ रेगुलर एक्सप्रेशन जाँच रहस्यमयी एक्सेप्शन को एक साफ़ इनपुट-अस्वीकृत मैसेज में बदल देती है।
- बाइट्स को बाइट्स ही रखें, बिल्कुल आख़िरी क़दम तक। एक बार डिकोड करें,
byte[]को उस फ़ाइल API या एन्कोडर को सौंपें जो इसे माँगता है, और तब ही एक सोची-समझी एन्कोडिंग के साथ टेक्स्ट में बदलें। - लंबाई लॉग करें, पेलोड नहीं। इनपुट का साइज़ और डिकोड आउटपुट का साइज़ डिकोड विफलता के बारे में लगभग सब कुछ बता देता है, बिना संवेदनशील डेटा को लॉग में पेस्ट किए।
- जो भी वायर पार करता है, उसके लिए लिख दें कि वह कौन-सी वर्णमाला में है, स्टैंडर्ड या base64url, और कौन-सा पैडिंग रिवाज़ है - उसी कोड की लाइन में जहाँ डिकोड होता है। भविष्य का आप ही उस नोट का पढ़ने वाला है।
PowerShell को अपना डिकोडर कैसे मिला
PowerShell में Base64 का सबसे छोटा सच्चा इतिहास यह है कि PowerShell ने कभी खुद का नहीं लिखा। जिस मेटोड का आप इस्तेमाल करते हैं, Convert.FromBase64String, वह 2003 में .NET Framework 1.1 के साथ निकला, और नवंबर 2006 के वर्ज़न 1.0 से हर PowerShell ने बस वह .NET एक्सपोज़ किया जिस पर वह चलता है। इस प्रोजेक्ट को बनते वक़्त Monad कहा जाता था, पहली बार जनता के सामने अक्टूबर 2003 में Professional Developers Conference में दिखाया गया, और रिलीज़ होने तक वह .NET एन्कोडर-डिकोडर जोड़ी, जिसका वह कवर करता है, तीन साल पुरानी और रोज़मर्रा के इस्तेमाल में थी।
फॉर्मेट खुद उसी साल स्टैंडर्ड बना जब शेल लॉन्च हुआ। RFC 4648, जो अक्टूबर 2006 में प्रकाशित हुआ, वही दस्तावेज़ है जिसने वर्णमाला, पैडिंग के नियम, सख़्त-डिकोड की उम्मीद और base64url वैरिएंट तय किए, और यह आज भी बिल्कुल वही व्यवहार बयान करता है जो FromBase64String लागू करता है। जब अगस्त 2016 में PowerShell Core के रूप में PowerShell ओपन-सोर्स और क्रॉस-प्लेटफ़ॉर्म बना, तो डिकोडर बिना किसी बदलाव के Linux और macOS पर सफ़र में साथ चला, क्योंकि बदलने को कुछ ही था।
एक असली जोड़ है PowerShell Gallery का Microsoft.PowerShell.TextUtility मॉड्यूल, जो कम्युनिटी की देखभाल में है, और जिसका ConvertFrom-Base64 कमांडलेट उसी .NET मेटोड का कवर करता है, साथ में -AsByteArray स्विच जोड़ता है, और एक टेक्स्ट डिफ़ॉल्ट जो UTF-8 के रूप में डिकोड करता है। अगर आपको कमांडलेट शक्ल ज़्यादा पसंद है तो Install-Module -Name Microsoft.PowerShell.TextUtility से इंस्टॉल करें, लेकिन एक चेतावनी: मॉड्यूल अब अर्शिव है और इसकी देखभाल अब ज़ोर-शोर से नहीं होती, और यही वजह भी है कि नई स्क्रिप्ट्स के लिए बिल्ट-इन मेटोड ही सिफ़ारिश बनी रहता है।
ये बातें याद रखने लायक हैं
- डिकोडर इनपुट में कहीं भी टैब, लाइन फीड, कैरिज रेटर्न और स्पेस को अनदेखा करता है। सौ रैप की लाइनें बिल्कुल वैसे ही डिकोड होती हैं जैसे एक लंबी लाइन।
$nullऔर खाली स्ट्रिंग, दोनों बिना शिकायत के खाली ऐरे में डिकोड होते हैं, जिससे किनारे परFromBase64Stringआमतौर से ज़्यादा अनुग्रहशील निकलता है।- एक ही
FormatExceptionमैसेज तीन अलग-अलग विफलता मोड को ढकता है। जब वह गिरे, तो जवाब मैसेज में नहीं, इनपुट में है। "SABpAA=="स्ट्रिंगHiहै PowerShell के अपने इंटरनल एन्कोडिंग, UTF-16LE में। वह उसी दो अक्षरों के UTF-8 एन्कोडिंग से दोगुनी लंबी है, और यही अनुपात है Windows-नेटिव टेक्स्ट का फिंगरप्रिंट, किसी भी Base64 में जो आप पढ़ेंगे।-EncodedCommandपहली PowerShell रिलीज़ से मौजूद है, और उसके पेलोड को UTF-16LE होना ही ज़रूरी है, UTF-8 नहीं। ग़लत एन्कोडिंग से एन्कोड करें तो शेल खुशी-खुशी आपका मोज़िबैक चला देता है।- .NET के नए स्पैन-आधारित Base64 हेल्पर, जिनमें
Base64Urlक्लास भी है, पुरानी PowerShell रिलीज़ से पहुँच से बाहर थे, क्योंकि स्पैन byref-समान टाइप्स हैं जिनसे मेटोड बाइंडर बंध ही नहीं पाता था। यह बदल गया है: वर्तमान PowerShell (7.4+, एक ऐसे .NET वर्ज़न पर जिसमें वह क्लास हो) अब ऐरे या स्ट्रिंग आर्गुमेंट कोReadOnlySpan<T>पैरामीटर के साथ बिना शिकायत से जोड़ देता है, इसलिए सीधा कॉल आज काम करता है। दो चरों का बदला-बदला वह वर्ज़न है जो Windows PowerShell 5.1 और पुराने होस्ट पर भी चलता है, न कि शेष बचा एकमात्र रास्ता। Get-Content -AsByteStream,-Rawके बिना, बाइट ऑब्जेक्ट्स की स्ट्रीम देता है, बाइट्स ऐरे नहीं।-Rawजोड़ें और टाइप बिल्कुल वही है जो .NET मेटोड्स की उम्मीद है।
लंबी परिक्रमा
इस लेख की हर चीज़ Base64 स्ट्रिंग लेकर आपके डेटा को वापस पाने के बारे में है। उल्टा काम, डेटा को Base64 बनाना, एक वन-लाइनर जैसा लगता है, जब तक कि ये बातें सामने न आएँ: PowerShell स्ट्रिंग्स बाइट्स नहीं हैं, UTF-16 साइज़ दोगुना कर देता है, लाइन व्रैपिंग की दो रिवाज़ी चौड़ाई हैं, और base64url आउटपुट को अपना खुद का दो-चर ऑपरेशन चाहिए। उस दिशा को उसी गहराई की चर्चा मिलती है, अपने फँदों और अपने इतिहास के साथ, बहन साइट के संबंधित लेख में, PowerShell में Base64 एन्कोडिंग, जिससे यह पेज नीचे लिंक करता है।
अंतिम अपडेट: 2026-09-07
संबंधित लेख: PowerShell में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड