Decodifica Base64 in PowerShell: una guida completa
In qualche punto, in una riga di log, in un file di configurazione o in un messaggio di errore, ti imbatti in questo: una lunga fila di lettere e cifre con l'occasionale più o barra, e uno o due segni di uguale parcheggiati in modo sospetto alla fine. Sembra rumore. Non lo è. È Base64, e tu sai già cosa vuoi: la cosa che nasconde.
Base64 è una traduzione, non una compressione e non un lucchetto. Riscrive qualsiasi sequenza di byte in testo stampabile, quattro caratteri per ogni tre byte in ingresso (così i dati codificati finiscono circa il 33% più grandi dell'originale), usando un alfabeto di 64 caratteri più il segno di uguale come riempimento finale. La home page di questo sito percorre l'alfabeto, il calcolo dei bit e le varianti nel dettaglio, quindi questo articolo si prende il suo tempo dove PowerShell fa la differenza: l'unico metodo .NET che userai, le regole che impone, e le una decina di angoli del lavoro reale in cui la decodifica in PowerShell diventa interessante.
Il metodo e il suo contratto
PowerShell non include un cmdlet Base64 tutto suo. Il lavoro lo fa un metodo di una classe .NET che fa parte del framework dal .NET Framework 1.1 del 2003, tre anni prima che PowerShell stesso uscisse:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
È tutta l'API: una stringa in ingresso, un array di byte in uscita. Funziona in ogni PowerShell su ogni sistema operativo, in Windows PowerShell 5.1 e in PowerShell 7 su Windows, Linux e macOS, perché è semplicemente .NET. Il contratto è abbastanza corto da poterlo memorizzare, quindi eccolo sotto forma di tabella:
| Ingresso | Quello che ricevi indietro |
|---|---|
$null |
Un array vuoto, nessun errore. PowerShell trasforma in silenzio $null in una stringa vuota prima della chiamata |
| Una stringa vuota | Un array vuoto, nessun errore |
| Un carico utile valido | Un byte[], mai una stringa, anche quando i dati sono testo |
| Un carico utile non valido | Una FormatException, impacchettata per te in una MethodInvocationException |
Un avvertimento prima di scrivere qualsiasi gestione degli errori: quella FormatException ha un solo messaggio che copre tre peccati diversi. Un carattere fuori dall'alfabeto, più di due caratteri di riempimento, o un carattere non di spaziatura bianca nascosto tra il riempimento producono tutti esattamente la stessa frase. Quando la vedi, il messaggio non ti dirà quale dei tre hai commesso, quindi torni indietro e rileggi il tuo input:
try {
[System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
$real = $_.Exception.InnerException
$real.GetType().Name
# FormatException
$real.Message
}
E la regola del multiplo di quattro ha un bordo che sorprende la prima volta che ci si imbatte. Quattro caratteri senza alcun riempimento sono perfettamente validi; significa solo che i bit avanzati dell'ultimo carattere vengono scartati. Tre caratteri non è un multiplo di quattro, e viene rifiutato:
[System.Convert]::FromBase64String("SGVs").Count
# 3: quattro caratteri senza riempimento va bene
[System.Convert]::FromBase64String("SGV")
# FormatException: tre caratteri non è un multiplo di quattro
Quello che il decodificatore accetta, e quello che no
Il decodificatore è rigoroso sull'alfabeto e generoso su una cosa specifica. I caratteri validi sono le 64 cifre Base64 (da A a Z, da a a z, da 0 a 9, più e barra) e il segno di uguale come riempimento finale. Esattamente quattro caratteri di spaziatura bianca vengono ignorati, ovunque e comunque spesso compaiano: la tabulazione, il line feed, il carriage return e lo spazio. La documentazione ufficiale .NET li elenca con i loro nomi Unicode, il che ti dice che si tratta di una garanzia documentata e non di un colpo di fortuna.
In pratica è un superpotere. MIME, la codifica della posta che ha portato il Base64 sulle mappe, avvolge le righe codificate a 76 caratteri, quindi un carico utile che ha viaggiato in una email, in un ticket o in un file di log arriverà di solito spezzato su molte righe. Al decodificatore non importa. Incollalo com'è:
$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. Il testo Base64 arriva
# in posta è avvolto a 76 colonne, quindi il decoder può non farci caso.
Tutto il resto che non è un carattere dell'alfabeto è uno stop definitivo. I trasgressori più comuni in giro sono lo spazio non interrottabile (il favorito del testo incollato dalle pagine web) e il byte order mark (il marchio invisibile che ti segue quando un file è stato letto con la codifica sbagliata). Per questo metodo nessuno dei due è spaziatura bianca, quindi entrambi generano un'eccezione:
try {
[System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
$_.Exception.InnerException.GetType().Name
# FormatException
}
La severità è intenzionale, non smania. La RFC 4648, lo standard che ha codificato il Base64 nel 2006, dice che le implementazioni devono rifiutare i caratteri fuori dall'alfabeto, a meno che il protocollo non permetta esplicitamente la tolleranza, perché un decodificatore che inghiotte in silenzio i caratteri estranei può essere trasformato in un canale coperto per contrabbandare dati oltre qualsiasi cosa che ispezioni soltanto l'alfabeto. Il decodificatore .NET segue la regola rigorosa, e di solito è esattamente quello che vuoi.
Un array di byte non è una stringa
Il metodo si ferma volutamente all'array di byte. Cosa significhino quei byte è una seconda decisione che solo tu puoi prendere, e indovinarla male è l'errore più famoso del lavoro con Base64 in PowerShell. L'ipotesi predefinita, UTF-8, è quella giusta per quasi tutto quello che c'è su internet, e l'andata e ritorno sono due chiamate:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
Le codifiche che userai davvero, e cosa fa ciascuna quando indovini male:
| Codifica | Usala quando | Se indovini male |
|---|---|---|
UTF8 |
API web, JSON, JWT, tutto il mondo moderno. La scelta sicura di default | Il testo Latin-1 o UTF-16 torna indietro come testo illeggibile |
Unicode (UTF-16LE) |
Il carico utile viene da strumenti Windows, da un valore di registro, o da una stringa .NET codificata prima della spedizione | Ogni carattere si ritrova con uno spazio vuoto intorno, perché hai letto un byte dove ne erano previsti due |
ASCII |
Credenziali HTTP Basic classiche e altri protocolli garantiti a 7 bit | Tutto ciò che supera il valore 127 diventa un punto interrogativo |
Latin1 |
Testo europeo legacy che precede UTF-8 | Le sequenze UTF-8 multibyte si spezzano in più lettere sbagliate |
Default |
Quasi mai. È la pagina di codice di sistema della macchina | Il tuo script si comporta in modo diverso a ogni impostazione regionale di Windows |
Il fallimento classico è testo UTF-8 decodificato come UTF-16. I byte sono veri, il metodo è contento, e il risultato è comunque spazzatura:
# "SGk=" sono i byte UTF-8 di "Hi"
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Un singolo carattere illeggibile: 2 byte UTF-8 letti come un'unità UTF-16 da 2 byte
La regola pratica: se il testo decodificato sembra che ogni carattere abbia uno spazio invisibile intorno, o che sia scritto in un altro alfabeto, ti sbagli di una codifica. Chiedi dove i dati sono stati prodotti, e in caso di dubbio fidati di UTF-8, ma verifica con i tuoi occhi i primi pochi caratteri. E decidi la codifica prima di decodificare, non dopo che il testo illeggibile è apparso nel tuo log.
base64url: l'alfabeto che si comporta bene negli URL
Incontrerai un cugino del Base64 in ogni token API, JWT e identificatore incorporato in un URL che ti capita tra le mani. Il più e la barra del Base64 standard sono legali in un URL solo dopo la codifica percent, e il riempimento con gli equals sembra un separatore di campi. Così la RFC 4648 ha definito un alfabeto sicuro per URL e nomi di file: gli stessi 64 caratteri, tranne che il più diventa trattino e la barra diventa sottolineatura. Il riempimento viene di solito tolto del tutto, perché la lunghezza dei dati lo rende inutile. La RFC tiene a precisare che questa variante deve essere chiamata base64url e non semplicemente "base64", e il resto di questa sezione segue quella convenzione.
.NET include davvero una classe dedicata, System.Buffers.Text.Base64Url, aggiunta nel .NET 9 con metodi veloci di codifica e decodifica costruiti interamente intorno a parametri ReadOnlySpan<T>. Il PowerShell attuale (7.4 e versioni successive, una volta che gira su una versione di .NET che include la classe) può in realtà chiamare direttamente, già oggi, questi sovraccarichi che accettano span, grazie alla conversione implicita da array/stringa a span che il risolutore di metodi esegue adesso, così [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") funziona senza cerimonie. Non è sempre stato così: Windows PowerShell 5.1 e le versioni più vecchie di PowerShell 7.x non potevano legarsi a parametri span, e la classe non esisteva affatto prima del .NET 9, quindi qualsiasi script che debba girare su 5.1, un 7.x più vecchio, o un host pre-.NET-9 ha ancora bisogno della versione portatile: scambiare i due caratteri, e ripristinare il riempimento prima di passare il testo al decodificatore standard. Il riempimento da aggiungere è quello che rende la lunghezza un multiplo di quattro:
$token = "--__AQI" # base64url, senza riempimento
$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
In quel piccolo blocco vivono due trabocchetti. Primo, il calcolo del riempimento: un carico utile la cui lunghezza è già un multiplo di quattro non ha bisogno di riempimento, ed è la guardia -eq 4 a tenere onesta l'espressione. Secondo, la direzione: quando decodifichi soltanto, aggiungi il riempimento e fai lo scambio; non togli mai il riempimento da un input Base64 standard, perché i decodificatori standard se l'aspettano lì. Se la fonte è un JWT o un token API, sarà base64url senza riempimento, e la ricetta qui sopra è esattamente la forma che vuoi.
Aprire un JWT senza le chiavi
Un JSON Web Token è tre segmenti base64url uniti da punti: intestazione, carico utile, firma. I primi due sono JSON puro, e il Base64 non è crittografia, quindi chiunque abbia il token può leggerli entrambi. È una caratteristica, non un difetto: il token è progettato per essere ispezionato, ed è la firma a renderlo infalsificabile. PowerShell riduce la sbirciata a tre righe:
$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
# proprietà name:
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe
Tre cose da tenere a mente. Il terzo segmento, la firma, è anche lui base64url, ma si decodifica in byte binari di firma, non in testo, quindi non aspettarti JSON ben fatto lì. L'intestazione di solito dice solo quale algoritmo ha firmato il token (HS256, RS256, ...), e un'intestazione che dice none è un segnale di allarme, non una comodità. E leggere il carico utile non è fidarsene: il base64 ti lascia vedere le claims, solo la firma le rende autentiche. Se il tuo lavoro è accettare token, verifica la firma con la chiave dell'emittente; se il tuo lavoro è debuggarne uno, il codice qui sopra è tutto ciò che ti serve.
File, PEM e la strada lunga verso i byte
La forma di file più comune è un file di testo che contiene il Base64 di qualcosa di più grande: un blob di backup, un binario scaricato, un oggetto serializzato. L'andata e ritorno sono quattro righe, e il modo moderno di leggere l'output è un vero array di byte, non un'ipotesi in testo:
$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# quanti byte portava il testo
Rileggere il binario originale è lì che PowerShell 6 e versioni successive si fanno valere. Il parametro -AsByteStream legge i byte grezzi, e con -Raw ti passa un autentico byte[] in un colpo solo:
$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# la dimensione originale, prima della tassa del 33 per cento sul testo
Ometti -Raw e ottieni un flusso di oggetti byte individuali (un Object[] se lo catturi), che va bene per l'ispezione ma è sbagliato da passare a metodi .NET che si aspettano un array. E Windows PowerShell 5.1 non ha proprio -AsByteStream, quindi su 5.1 la lettura affidabile è [System.IO.File]::ReadAllBytes(), che esiste dappertutto.
PEM è il cugino con l'armatura che conosci da ogni certificato e chiave privata: un corpo Base64 standard, di solito avvolto a 64 caratteri, tra righe -----BEGIN ... e -----END .... L'armatura è testo; il corpo è il carico utile. Togli l'armatura, unisci le righe, decodifica:
$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
# la dimensione binaria DER del certificato
Dato che il decodificatore standard ignora gli spazi bianchi comunque, il -join "" è la cintura e i pantaloni, non un requisito, ma tenere lo script esplicito su cosa rimuove lo fa comportare allo stesso modo su ogni macchina e con ogni convenzione di fine riga. L'altra direzione, avvolgere i byte DER in PEM, è semplicemente il codificatore Base64 più due righe di testo, e l'articolo sulla codifica del sito sorella mostra l'avvolgimento a 64 colonne per intero.
Certificati e il baule degli attrezzi di Windows
I certificati sono i cittadini Base64 più pesanti del lavoro quotidiano, e PowerShell può portare l'intera famiglia. Un file PFX è un bundle binario di certificato più chiave privata, ed è il formato che più spesso trovi girovagare come testo Base64 nei file di configurazione e negli script di distribuzione. Decodificarlo di nuovo in un certificato vivo è una riga sola con il tipo .NET, e funziona su più piattaforme in 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
# quando smette di essere vero
PowerShell 7 include anche Get-PfxCertificate, che legge un file PFX direttamente dal disco con un parametro -Password, quindi per i file su disco puoi saltare del tutto la decodifica manuale. Un certificato nudo (senza chiave) è ancora più semplice: i byte DER vanno dritti nello stesso tipo X509Certificate2 senza nessuna password.
Fuori dal linguaggio, due strumenti nativi meritano di essere conosciuti. Su Windows, certutil -decode infile.b64 outfile decodifica un file Base64 con semantica di file in ingresso e file in uscita (aggiungi -f per sovrascrivere), il che lo rende la scelta di riferimento per le correzioni rapide in una semplice riga di comando. Il suo fratello certutil -encode ha un flag da ricordare: -unicodetext converte il testo di input in UTF-16 prima di codificarlo in Base64, nascondendo un'intera decisione di codifica dentro un solo switch. Su Linux e macOS l'utilità classica è base64 -d, che decodifica un file o lo standard input, saltando di default gli a capo; su GNU coreutils aggiungi -i se il carico utile porta con sé anche spazi, tabulazioni o CRLF della posta Windows.
Comandi in una busta Base64
PowerShell ha una ragione integrata per parlare Base64 dalla versione 1.0: il parametro -EncodedCommand dell'host stesso. Dai a pwsh una stringa Base64, lui decodifica i byte come UTF-16LE, e il risultato viene eseguito come comando. Lo scopo ufficiale, preso di peso dalla documentazione, è inviare comandi che richiedono virgolette o parentesi graffe complesse senza lottare con le regole di virgolettatura della shell esterna:
$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello
Leggi bene quella seconda riga, perché è lì che tutti ci cascano: il carico utile deve essere UTF-16LE, ed è [System.Text.Encoding]::Unicode. Se invece codifichi il comando in UTF-8, PowerShell lo decodifica volentieri come UTF-16LE ed esegue un comando fatto di testo illeggibile, e il messaggio di errore che produce è il ritratto perfetto dell'errore:
$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Errore: un muro di caratteri illeggibili, "The term ... is not recognized..."
Lo stesso meccanismo è la ragione per cui i team di sicurezza si preoccupano del Base64 in PowerShell. Un lungo token opaco passato a -EncodedCommand è una forma comune per strumenti di automazione, ed è esattamente per questo che i prodotti di protezione endpoint decodificano questi carichi utili prima che vengano eseguiti: nulla nel Base64 nasconde il comando a un decodificatore, lo nasconde solo a un essere umano che legge una lista di processi. Se generi comandi codificati per le tue automazioni, tieni il comando sorgente accanto al token, perché il token da solo non si spiegherà alle 3 di notte.
Decodificare quando l'input è enorme
Per le dimensioni di ogni giorno, l'approccio a metodo singolo è quello più veloce. Un binario da cinque megabyte diventa una stringa di circa sei milioni e novcentomila caratteri, e la decodifica di quella stringa richiede solo millisecondi a una sola cifra su una macchina moderna. La nota stessa della documentazione .NET è che FromBase64String è progettato per elaborare una singola stringa contenente tutti i dati, il che è vero, e va anche bene fino a limiti molto grandi, perché il metodo lavora sulla stringa in loco senza copie aggiuntive significative.
Quando il carico utile è più grande di quanto ti sentiresti comodo a tenere in una sola stringa, o arriva come flusso (un download, un socket, un log enorme), lo strumento documentato è System.Security.Cryptography.FromBase64Transform avvolto in un CryptoStream: gli dai del testo Base64 e ne leggi fuori i byte decodificati, e a ogni istante è vivo solo un piccolo buffer. Nota che TransformStream, l'aiutante C# per questo scopo, è un metodo di estensione, e PowerShell non vede i metodi di estensione, quindi istanzi il CryptoStream direttamente:
$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()
Per il novanta per cento dei lavori, il percorso semplice è ancora quello giusto: leggi il file di testo intero con Get-Content -Raw, ripuliscilo, decodificalo, scrivi i byte. Ricorri alla versione a flusso quando il file è troppo grande per stare comodamente in memoria, o quando i dati arrivano pezzo per pezzo. E non provare a iterare sulle righe e decodificare ogni riga separatamente: i gruppi di quattro caratteri del Base64 non rispettano i tuoi a capo, quindi una riga che taglia un gruppo a metà non si decodificherà da sola. Leggi il testo intero, poi decodifica una volta.
Trappole che sono costate pomeriggi
- L'ipotesi sul set di caratteri. UTF-8 letto come UTF-16, o Latin-1 letto come UTF-8, produce testo illeggibile con grande convinzione. Decidi la codifica dalla fonte dei dati, parti di default da UTF-8, e guarda i primi caratteri decodificati prima di fidarti del resto.
- Caratteri invisibili dal web. Uno spazio non interrottabile o un byte order mark incollati da una pagina o da una email in formato ricco sono caratteri estranei per il decodificatore e fanno generare la generica
FormatException. Passa l'input attraverso.Trim()e un controllo sui caratteri non stampabili prima di decodificare. - Confusione sul riempimento. Il Base64 standard arriva con
=o==alla fine; il base64url dai token arriva senza. Dare l'uno alla ricetta costruita per l'altro è il guasto silenzioso più comune nel lavoro con le API, e il controllo di lunghezza nella sezione base64url è la guardia. - Un messaggio, tre crimini. Poiché il messaggio di
FormatExceptioncopre caratteri sbagliati, riempimento in eccesso e riempimento sporco tutto in una volta, i blocchi catch che registrano solo il messaggio ti fanno girare a vuoto. Registra anche la lunghezza dell'input e la prima regione colpevole. - Aspettarsi una stringa indietro. Il risultato è sempre un array di byte. Nel momento in cui inizi a convertirlo direttamente in stringa ottieni una lista di numeri, non testo. Converti con una codifica esplicita, una volta sola, alla fine.
- Il default dei file su 5.1. Windows PowerShell 5.1 legge i file senza BOM con la pagina di codice ANSI di sistema, mentre PowerShell 7 dà per scontato UTF-8. Se il tuo script legge il file di testo Base64 su 5.1 e il file è UTF-8 con non-ASCII intorno al carico utile, il testo si corrompe prima che il decodificatore lo veda.
- Trattare il Base64 come un lucchetto. È una traduzione. Una password, un token o un segreto in Base64 è testo semplice vestito da altro, e ogni decodificatore del pianeta, incluso questo articolo, lo apre in una riga.
Abitudini che tengono gli script onesti
- Ripulisci l'input esterno prima di decodificare. Un
.Trim()toglie più incidenti di produzione di qualsiasi gestore degli errori. - Valida prima di decodificare quando la fonte non è fidata: dopo aver tolto i quattro caratteri di spaziatura bianca ammessi, la stringa dovrebbe contenere solo caratteri dell'alfabeto con al massimo due segni di uguale finali. Un rapido controllo con espressioni regolari trasforma un'eccezione misteriosa in un pulito messaggio di input rifiutato.
- Tieni i byte come byte fino all'ultimo passo. Decodifica una volta, passa il
byte[]all'API dei file o al codificatore che ne ha bisogno, e solo allora converti in testo con una codifica deliberata. - Registra le lunghezze, non i carichi utili. La dimensione dell'input e la dimensione dell'output decodificato ti dicono quasi tutto su un fallimento di decodifica, senza incollare dati potenzialmente sensibili nel log.
- Per tutto ciò che attraversa un cavo, annota in quale alfabeto si trova, standard o base64url, e con quale convenzione di riempimento, nella stessa riga di codice che lo decodifica. Il te del futuro è il consumatore di quella nota.
Come PowerShell ha ereditato il suo decodificatore
La storia vera più corta del Base64 in PowerShell è che PowerShell non ne ha mai scritto uno. Il metodo che usi, Convert.FromBase64String, è uscito con .NET Framework 1.1 nel 2003, e ogni PowerShell dalla versione 1.0 di novembre 2006 ha semplicemente esposto il .NET su cui gira. Il progetto si chiamava Monad mentre veniva costruito, mostrato per la prima volta in pubblico alla Professional Developers Conference di ottobre 2003, e al momento del rilascio la coppia codificatore-decodificatore .NET che avvolge era già vecchia di tre anni e in uso quotidiano.
Il formato stesso è stato standardizzato lo stesso anno in cui la shell è uscita. La RFC 4648, pubblicata in ottobre 2006, è il documento che ha fissato l'alfabeto, le regole di riempimento, l'aspettativa di decodifica rigorosa e la variante base64url, e descrive ancora esattamente il comportamento che FromBase64String implementa oggi. Quando PowerShell è diventato open-source e multipiattaforma in agosto 2016 come PowerShell Core, il decodificatore è arrivato con lui su Linux e macOS senza modifiche, perché non c'era niente da cambiare.
L'unica vera aggiunta è il modulo mantenuto dalla comunità Microsoft.PowerShell.TextUtility della PowerShell Gallery, il cui cmdlet ConvertFrom-Base64 avvolge lo stesso metodo .NET e aggiunge uno switch -AsByteArray più un default in testo che decodifica come UTF-8. Installalo con Install-Module -Name Microsoft.PowerShell.TextUtility se preferisci la forma cmdlet, ma una riserva: il modulo è ora archiviato e non viene più mantenuto attivamente, un'altra ragione per cui il metodo integrato resta la raccomandazione per gli script nuovi.
Fatti da tenere a mente
- Il decodificatore ignora tabulazioni, line feed, carriage return e spazi in qualsiasi punto dell'input. Cento righe avvolte si decodificano esattamente come una riga lunga.
$nulle la stringa vuota si decodificano entrambe in un array vuoto senza lamentarsi, il che rendeFromBase64Stringinsolitamente accomodante ai bordi.- Il singolo messaggio di
FormatExceptioncopre tre modalità di fallimento diverse. Quando scatta, è nell'input, non nel messaggio, che sta la risposta. "SABpAA=="è la stringaHinella codifica interna di PowerShell stessa, UTF-16LE. È il doppio della lunghezza della codifica UTF-8 delle stesse due lettere, e quel rapporto è l'impronta digitale del testo nativo di Windows in qualsiasi Base64 che leggerai.-EncodedCommandesiste dal primo rilascio di PowerShell, e il suo carico utile deve essere UTF-16LE, non UTF-8. Codifica con la codifica sbagliata e la shell esegue volentieri il tuo testo illeggibile.- I nuovi aiutanti Base64 basati su span di .NET, inclusa la classe
Base64Url, erano irraggiungibili dalle versioni più vecchie di PowerShell, perché gli span sono tipi simili a byref a cui il risolutore di metodi non sapeva legarsi. Questo è cambiato: il PowerShell attuale (7.4+, su una versione di .NET abbastanza nuova da includere la classe) risolve un argomento array o stringa contro un parametroReadOnlySpan<T>senza lamentarsi, quindi la chiamata diretta funziona oggi. Lo scambio di due caratteri si guadagna il suo posto come la versione che gira anche su Windows PowerShell 5.1 e host più vecchi, non come l'unico percorso rimasto. Get-Content -AsByteStreamsenza-Rawti dà un flusso di oggetti byte, non un array di byte. Aggiungi-Rawe il tipo è esattamente quello che i metodi .NET si aspettano.
La strada lunga in tondo
Tutto in questo articolo riguarda il prendere una stringa Base64 e riavere i tuoi dati. L'operazione speculare, trasformare dati in Base64, sembra una riga sola fino a che non incontri il fatto che le stringhe di PowerShell non sono byte, che UTF-16 raddoppia le dimensioni, che l'avvolgimento di riga ha due larghezze convenzionali, e che l'output base64url ha bisogno della sua chirurgia di due caratteri. Quella direzione riceve la sua trattazione completa, con le sue trappole e la sua storia, nell'articolo correlato sul sito sorella, la codifica Base64 in PowerShell, a cui questa pagina collega qui sotto.
Ultimo aggiornamento: 2026-09-07
Articolo correlato: Codifica Base64 in PowerShell: una guida completa