RegEx zum Parsen oder Validieren von Base64-Daten

99

Ist es möglich, mit einem RegEx Base64-Daten zu validieren oder zu bereinigen? Das ist die einfache Frage, aber die Faktoren, die diese Frage antreiben, machen es schwierig.

Ich habe einen Base64-Decoder, der sich nicht vollständig auf die Eingabedaten verlassen kann, um den RFC-Spezifikationen zu folgen. Die Probleme, mit denen ich konfrontiert bin, sind Probleme wie vielleicht Base64-Daten, die möglicherweise nicht in 78 unterteilt sind (ich denke, es ist 78, ich müsste den RFC noch einmal überprüfen, also sag mir nichts, wenn die genaue Zahl falsch ist) Zeilen oder dass die Zeilen möglicherweise nicht in CRLF enden; , dass es nur eine CR oder LF haben kann, oder vielleicht auch keine.

Ich hatte verdammt viel Zeit damit, als solche formatierte Base64-Daten zu analysieren. Aus diesem Grund können Beispiele wie die folgenden nicht mehr zuverlässig dekodiert werden. Der Kürze halber werde ich nur teilweise MIME-Header anzeigen.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ok, das Parsen ist also kein Problem und genau das Ergebnis, das wir erwarten würden. In 99% der Fälle funktioniert die Verwendung eines beliebigen Codes, um zumindest zu überprüfen, ob jedes Zeichen im Puffer ein gültiges base64-Zeichen ist, einwandfrei. Aber das nächste Beispiel wirft einen Schraubenschlüssel in die Mischung.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Dies ist eine Version der Base64-Codierung, die ich bei einigen Viren und anderen Dingen gesehen habe, die versuchen, die Vorteile einiger E-Mail-Leser zu nutzen. Sie möchten Mime um jeden Preis analysieren, im Gegensatz zu solchen, die sich strikt an das Buch halten, oder besser gesagt an RFC. wenn man so will.

Mein Base64-Decoder decodiert das zweite Beispiel in den folgenden Datenstrom. Und denken Sie daran, der ursprüngliche Stream besteht aus allen ASCII-Daten!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Hat jemand eine gute Möglichkeit, beide Probleme gleichzeitig zu lösen? Ich bin mir nicht sicher, ob es überhaupt möglich ist, zwei Transformationen an den Daten mit unterschiedlichen Regeln durchzuführen und die Ergebnisse zu vergleichen. Wenn Sie diesen Ansatz gewählt haben, welchem ​​Output vertrauen Sie? Es scheint, dass ASCII-Heuristiken die beste Lösung sind, aber wie viel mehr Code, Ausführungszeit und Komplexität würde dies zu etwas so Kompliziertem wie einem Virenscanner beitragen, an dem dieser Code tatsächlich beteiligt ist? Wie würden Sie die Heuristik-Engine trainieren, um zu erfahren, was Base64 akzeptabel ist und was nicht?


AKTUALISIEREN:

Angesichts der Anzahl der Aufrufe, die diese Frage weiterhin erhält, habe ich beschlossen, das einfache RegEx, das ich seit 3 ​​Jahren in einer C # -Anwendung verwende, mit Hunderttausenden von Transaktionen zu veröffentlichen. Ehrlich gesagt gefällt mir die Antwort von Gumbo am besten, weshalb ich sie als ausgewählte Antwort ausgewählt habe. Aber für alle, die C # verwenden und nach einer sehr schnellen Möglichkeit suchen, um zumindest festzustellen, ob eine Zeichenfolge oder ein Byte [] gültige Base64-Daten enthält oder nicht, hat sich Folgendes für mich als sehr gut erwiesen.

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

Und ja, dies ist nur für einen STRING von Base64-Daten, NICHT für eine richtig formatierte RFC1341- Nachricht. Wenn Sie also mit Daten dieses Typs arbeiten, berücksichtigen Sie dies bitte, bevor Sie versuchen, das oben genannte RegEx zu verwenden. Wenn Sie sich für andere Zwecke (URLs, Dateinamen, XML-Codierung usw.) mit Base16, Base32, Radix oder sogar Base64 beschäftigen, wird dringend empfohlen, RFC4648 zu lesen , das Gumbo in seiner Antwort erwähnt hat, da Sie gesund sein müssen Beachten Sie den Zeichensatz und die Terminatoren, die von der Implementierung verwendet werden, bevor Sie versuchen, die Vorschläge in diesem Frage- / Antwortsatz zu verwenden.

LarryF
quelle
Ich denke, dass Sie die Aufgabe besser definieren müssen. Es ist völlig unklar, was Ihr Ziel ist: streng sein? 100% der Proben analysieren? ...
ADEpt
Ihr erstes Beispiel sollte 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 =' sein
jfs
Warum verwenden Sie keine Standardlösung in Ihrer Sprache? Warum benötigen Sie einen handgeschriebenen Parser, der auf regulären Ausdrücken basiert?
JFS
1
Gute Frage. Obwohl ich den UPDATE- Regex ausprobiert habe, indem ich ihn gegen einen von NPM zurückgegebenen base64-codierten SHA ausgeführt habe, ist er fehlgeschlagen, während der Regex in der ausgewählten Antwort einwandfrei funktioniert .
Josh Habdas
1
Ich bin mir nicht sicher, wie der UPDATE- Regex noch ohne Korrektur veröffentlicht wird, aber es sieht so aus, als ob der Autor beabsichtigt hat , das ^Äußere der Klammern als Startanker zu setzen . Ein viel besserer Regex, ohne so kompliziert zu werden wie die akzeptierte Antwort, wäre jedoch^[-A-Za-z0-9+/]*={0,3}$
kael

Antworten:

145

Aus dem RFC 4648 :

Die Basiscodierung von Daten wird in vielen Situationen verwendet, um Daten in Umgebungen zu speichern oder zu übertragen, die möglicherweise aus früheren Gründen auf US-ASCII-Daten beschränkt sind.

Es hängt also vom Verwendungszweck der verschlüsselten Daten ab, ob die Daten als gefährlich eingestuft werden sollen.

Wenn Sie jedoch nur nach einem regulären Ausdruck suchen, der mit Base64-codierten Wörtern übereinstimmt, können Sie Folgendes verwenden:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
Gumbo
quelle
10
Die einfachste Lösung wäre, vor der Validierung alle Leerzeichen (die gemäß RFC ignoriert werden) zu entfernen.
Ben Blank
2
Die letzte nicht erfassende Gruppe für das Auffüllen ist optional.
Gumbo
4
Anfangs war ich skeptisch gegenüber der Komplexität, aber sie bestätigt sich recht gut. Wenn Sie nur mit base64-ish übereinstimmen möchten, würde ich mir ^ [a-zA-Z0-9 + /] = {0,3} $ einfallen lassen, das ist besser!
Lodewijk
3
@BogdanNechyporenko Das liegt daran, dass namees sich um eine gültige Base64-Codierung der (hex) Byte-Sequenz handelt 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})$muss der
Gegenreaktion
37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Dieser ist gut, passt aber zu einem leeren String

Dieser stimmt nicht mit einer leeren Zeichenfolge überein:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$
njzk2
quelle
2
Warum ist eine leere Zeichenfolge ungültig?
Josh Lee
8
es ist nicht. Wenn Sie jedoch einen regulären Ausdruck verwenden, um herauszufinden, ob eine bestimmte Zeichenfolge base64 ist oder nicht, sind Sie wahrscheinlich nicht an leeren Zeichenfolgen interessiert. Zumindest weiß ich, dass ich es nicht bin.
NJZK2
4
@ LayZee: Wenn Sie dies tun, erzwingen Sie, dass die Base64-Zeichenfolge mindestens einen Block mit 4 Größen enthält, wodurch gültige Werte wie z. B. MQ==keine Übereinstimmung mit Ihrem Ausdruck
gerendert werden
5
@ Ruslan noch sollte es. Dies ist keine gültige Basis-64-Zeichenfolge. (Größe ist 23, was nicht // 4 ist). AQENVg688MSGlEgdOJpjIUC=ist das gültige Formular.
NJZK2
1
@ JinKwon base64 endet mit 0, 1 oder 2 =. Der letzte ?erlaubt 0 =. Das Ersetzen durch {1}erfordert 1 oder 2 Endung=
njzk2
4

Weder ein " : " noch ein " . " Werden in der gültigen Base64 angezeigt, daher können Sie die http://www.stackoverflow.comZeile eindeutig wegwerfen . Sagen wir in Perl so etwas wie

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

say decode_base64($sanitized_str);

könnte sein, was Sie wollen. Es produziert

Dies ist ein einfaches Beispiel für ASCII Base64 für StackOverflow.

oylenshpeegul
quelle
Ich kann da zustimmen, aber alle ANDEREN Buchstaben in der URL sind zufällig gültig base64 ... Also, wo ziehen Sie die Grenze? Nur bei Zeilenumbrüchen? (Ich habe solche gesehen, bei denen es nur ein paar zufällige Zeichen in der Mitte der Zeile gibt. Ich kann den Rest der Zeile nur deswegen nicht werfen, IMHO) ...
LarryF
@LarryF: Wenn die Base-64-codierten Daten nicht auf Integrität überprüft werden, können Sie nicht sagen, was mit einem Base-64-Datenblock zu tun ist, der falsche Zeichen enthält. Welches ist die beste Heuristik: Ignorieren Sie die falschen Zeichen (lassen Sie alle richtigen zu) oder lehnen Sie die Zeilen ab oder lehnen Sie das Los ab?
Jonathan Leffler
(Fortsetzung): Die kurze Antwort lautet "es kommt darauf an" - davon, woher die Daten stammen und welche Art von Chaos Sie darin finden.
Jonathan Leffler
(wieder aufgenommen): Ich sehe aus den Kommentaren zu der Frage, dass Sie alles akzeptieren möchten, was Base-64 sein könnte. Ordnen Sie also einfach jedes einzelne Zeichen zu, das nicht in Ihrem Basis-64-Alphabet enthalten ist (beachten Sie, dass es URL-sichere und andere solche Codierungsvarianten gibt), einschließlich der Zeilenumbrüche und Doppelpunkte, und nehmen Sie die verbleibenden Zeichen.
Jonathan Leffler
3

Der beste reguläre Ausdruck, den ich bisher finden konnte, ist hier https://www.npmjs.com/package/base64-regex

Das ist in der aktuellen Version wie folgt:

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');
};
Bogdan Nechyporenko
quelle
Vielleicht besser ohne \\n?.
Jin Kwon
Dies wird bei JSON-Strings
fehlschlagen
3

Um das base64-Image zu validieren , können wir diesen regulären Ausdruck verwenden

/ ^ 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);
  }
Jayani Sumudini
quelle