RegEx per analizzare o convalidare i dati Base64


99

È possibile utilizzare una RegEx per convalidare o disinfettare i dati Base64? Questa è la semplice domanda, ma i fattori che guidano questa domanda sono ciò che la rendono difficile.

Ho un decoder Base64 che non può fare completamente affidamento sui dati di input per seguire le specifiche RFC. Quindi, i problemi che devo affrontare sono problemi come forse i dati Base64 che potrebbero non essere suddivisi in 78 (penso sia 78, dovrei ricontrollare l'RFC, quindi non chiamarmi se il numero esatto è sbagliato) righe, o che le righe non possono finire in CRLF; in quanto può avere solo una CR, o LF, o forse nessuno dei due.

Quindi, mi sono divertito un mondo ad analizzare i dati Base64 formattati come tali. A causa di ciò, esempi come i seguenti diventano impossibili da decodificare in modo affidabile. Visualizzerò solo intestazioni MIME parziali per brevità.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ok, quindi l'analisi non è un problema ed è esattamente il risultato che ci aspetteremmo. E nel 99% dei casi, l'utilizzo di qualsiasi codice per verificare almeno che ogni carattere nel buffer sia un carattere base64 valido, funziona perfettamente. Ma il prossimo esempio getta una chiave nel mix.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Questa è una versione della codifica Base64 che ho visto in alcuni virus e altre cose che tentano di trarre vantaggio dal desiderio di alcuni lettori di posta di analizzare mime a tutti i costi, contro quelli che seguono rigorosamente il libro, o meglio RFC; se vorrai.

Il mio decodificatore Base64 decodifica il secondo esempio nel seguente flusso di dati. E tieni presente che lo stream originale è tutto di dati ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Qualcuno ha un buon modo per risolvere entrambi i problemi contemporaneamente? Non sono nemmeno sicuro che sia possibile, a parte fare due trasformazioni sui dati con regole diverse applicate e confrontare i risultati. Tuttavia, se hai adottato questo approccio, di quale output ti fidi? Sembra che l'euristica ASCII sia la soluzione migliore , ma quanto più codice, tempo di esecuzione e complessità aggiungerebbero a qualcosa di complicato come uno scanner antivirus, in cui è effettivamente coinvolto questo codice? Come addestreresti il ​​motore euristico per apprendere cosa è Base64 accettabile e cosa no?


AGGIORNARE:

Considerando il numero di visualizzazioni che questa domanda continua a ottenere, ho deciso di pubblicare la semplice RegEx che utilizzo in un'applicazione C # da 3 anni, con centinaia di migliaia di transazioni. Onestamente, mi piace di più la risposta data da Gumbo , motivo per cui l'ho scelta come risposta selezionata. Ma per chiunque utilizzi C # e cerchi un modo molto rapido per rilevare almeno se una stringa o un byte [] contiene dati Base64 validi o meno, ho trovato che quanto segue funziona molto bene per me.

[^-A-Za-z0-9+/=]|=[^=]|={3,}$

E sì, questo è solo per una STRINGA di dati Base64, NON un messaggio RFC1341 formattato correttamente . Quindi, se hai a che fare con dati di questo tipo, ti preghiamo di tenerne conto prima di tentare di utilizzare la RegEx sopra. Se hai a che fare con Base16, Base32, Radix o anche Base64 per altri scopi (URL, nomi di file, codifica XML, ecc.), Ti consigliamo vivamente di leggere RFC4648 che Gumbo ha menzionato nella sua risposta perché devi stare bene consapevole del set di caratteri e dei terminatori utilizzati dall'implementazione prima di tentare di utilizzare i suggerimenti in questo set di domande / risposte.


Immagino che tu debba definire meglio il compito. Non è del tutto chiaro qual è il tuo obiettivo: essere severo? analizzare il 100% dei campioni? ...
ADEpt

Il primo esempio dovrebbe essere "VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ="
jfs

Perché non utilizzare una soluzione standard nella tua lingua? Perché hai bisogno di un parser scritto a mano basato su regex?
jfs

1
Ottima domanda. Anche se ho provato la regex UPDATE eseguendola su uno SHA con codifica base64 restituito da NPM e non è riuscito mentre la regex nella risposta selezionata funziona bene .
Josh Habdas

1
Non sono sicuro di come la regex UPDATE sia ancora pubblicata senza correzione, ma sembra che l'autore intendesse mettere l' ^esterno delle parentesi, come punto di ancoraggio iniziale. Tuttavia, una regex molto migliore, senza complicarsi come la risposta accettata, sarebbe^[-A-Za-z0-9+/]*={0,3}$
kael

Risposte:


145

Dalla RFC 4648 :

La codifica di base dei dati viene utilizzata in molte situazioni per archiviare o trasferire dati in ambienti che, forse per motivi legacy, sono limitati ai dati US-ASCII.

Quindi dipende dallo scopo dell'utilizzo dei dati codificati se i dati devono essere considerati pericolosi.

Ma se stai solo cercando un'espressione regolare per abbinare parole con codifica Base64, puoi usare quanto segue:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

10
La soluzione più semplice sarebbe quella di eliminare tutti gli spazi bianchi (che vengono ignorati secondo l'RFC) prima della convalida.
Ben Blank,

2
L'ultimo gruppo non di acquisizione per il riempimento è facoltativo.
Gumbo

4
All'inizio ero scettico sulla complessità, ma si convalida abbastanza bene. Se desideri semplicemente abbinare base64-ish, mi viene in mente di fare ^ [a-zA-Z0-9 + /] = {0,3} $, è meglio!
Lodewijk

3
@BogdanNechyporenko Questo perché nameè una codifica Base64 valida della sequenza di byte (esadecimale) 9d a9 9e.
Marten

3
^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$deve sfuggire al contraccolpo
Khizar ha dichiarato il

37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Questo è buono, ma corrisponderà a una stringa vuota

Questo non corrisponde a una stringa vuota:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$

2
Perché una stringa vuota non è valida?
Josh Lee

8
non è. ma se stai usando un'espressione regolare per scoprire se una data stringa è o non è base64, è probabile che non ti interessino le stringhe vuote. Almeno so di non esserlo.
njzk2

4
@LayZee: se lo fai, costringi la stringa base64 a contenere almeno un blocco di 4 dimensioni, MQ==
restituendo

5
@ruslan né dovrebbe. questa non è una stringa di base 64 valida. (la dimensione è 23, che non è // 4). AQENVg688MSGlEgdOJpjIUC=è la forma valida.
njzk2

1
@JinKwon base64 termina con 0, 1 o 2 =. L'ultimo ?consente 0 =. Sostituirlo con {1}richiede 1 o 2 finali=
njzk2

4

Né un " : " né un " . " Appariranno in Base64 valido, quindi penso che tu possa buttare via senza ambiguità la http://www.stackoverflow.comlinea. In Perl, diciamo, qualcosa di simile

my $sanitized_str = join q{}, grep {!/[^A-Za-z0-9+\/=]/} split /\n/, $str;

say decode_base64($sanitized_str);

potrebbe essere quello che vuoi. Produce

Questo è un semplice ASCII Base64 per l'esempio StackOverflow.


Posso essere d'accordo su questo punto, ma tutte le ALTRE lettere nell'URL sono valide in base64 ... Allora, dove traccia la linea? Solo durante le interruzioni di riga? (Ne ho visti di quelli in cui ci sono solo un paio di caratteri casuali nel mezzo della riga. Non posso lanciare il resto della riga solo per questo, IMHO) ...
LarryF

@ LarryF: a meno che non ci sia un controllo dell'integrità sui dati codificati in base 64, non puoi dire cosa fare con qualsiasi blocco di dati in base 64 contenente caratteri errati. Qual è la migliore euristica: ignorare i caratteri errati (consentendo tutti quelli corretti) o rifiutare le righe o rifiutare il lotto?
Jonathan Leffler

(continua): la risposta breve è "dipende", dalla provenienza dei dati e dal tipo di confusione che ci si trova.
Jonathan Leffler

(ripreso): Vedo dai commenti alla domanda che vuoi accettare tutto ciò che potrebbe essere base 64. Quindi mappare semplicemente ogni carattere che non è nel tuo alfabeto base 64 (nota che ci sono codifiche sicure per URL e altre codifiche simili), inclusi i caratteri a capo e due punti, e prendi ciò che rimane.
Jonathan Leffler

3

La migliore regexp che ho trovato fino ad ora è qui https://www.npmjs.com/package/base64-regex

che è nella versione corrente ha il seguente aspetto:

module.exports = function (opts) {
  opts = opts || {};
  var regex = '(?:[A-Za-z0-9+\/]{4}\\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)';

  return opts.exact ? new RegExp('(?:^' + regex + '$)') :
                    new RegExp('(?:^|\\s)' + regex, 'g');
};

Forse meglio senza \\n?.
Jin Kwon

Questo fallirà sulle stringhe JSON
idleberg il

3

Per convalidare l' immagine base64 possiamo usare questa regex

/ ^ data: image / (?: gif | png | jpeg | bmp | webp) (?:; charset = utf-8) ?; base64, (?: [A-Za-z0-9] | [+ /] ) + = {0,2}

  private validBase64Image(base64Image: string): boolean {
    const regex = /^data:image\/(?:gif|png|jpeg|bmp|webp)(?:;charset=utf-8)?;base64,(?:[A-Za-z0-9]|[+/])+={0,2}/;
    return base64Image && regex.test(base64Image);
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.