RegEx pour analyser ou valider les données Base64

99

Est-il possible d'utiliser un RegEx pour valider ou nettoyer les données Base64? C'est la question simple, mais ce sont les facteurs qui motivent cette question qui la rendent difficile.

J'ai un décodeur Base64 qui ne peut pas entièrement compter sur les données d'entrée pour suivre les spécifications RFC. Donc, les problèmes auxquels je suis confronté sont des problèmes comme peut-être les données Base64 qui ne peuvent pas être divisées en 78 (je pense que c'est 78, je devrais vérifier la RFC, alors ne me dites pas si le nombre exact est faux) lignes, ou que les lignes peuvent ne pas se terminer par CRLF; en ce qu 'il peut n'avoir qu'un CR, ou LF, ou peut-être aucun.

Donc, j'ai eu un sacré temps à analyser les données Base64 formatées comme telles. Pour cette raison, des exemples tels que les suivants deviennent impossibles à décoder de manière fiable. Je n'afficherai que des en-têtes MIME partiels par souci de concision.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ok, donc l'analyse n'est pas un problème, et c'est exactement le résultat auquel on s'attend. Et dans 99% des cas, l'utilisation de n'importe quel code pour au moins vérifier que chaque caractère du tampon est un caractère base64 valide fonctionne parfaitement. Mais, l'exemple suivant jette une clé dans le mélange.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

C'est une version de l'encodage Base64 que j'ai vu dans certains virus et autres choses qui tentent de profiter du désir de certains lecteurs de messagerie d'analyser le mime à tout prix, par rapport à ceux qui se conforment strictement au livre, ou plutôt à RFC; si vous voulez.

Mon décodeur Base64 décode le deuxième exemple en flux de données suivant. Et gardez à l'esprit ici, le flux d'origine est constitué de toutes les données ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Quelqu'un a-t-il un bon moyen de résoudre les deux problèmes à la fois? Je ne suis pas sûr que ce soit même possible, en dehors de faire deux transformations sur les données avec des règles différentes appliquées, et de comparer les résultats. Cependant, si vous avez adopté cette approche, à quel résultat faites-vous confiance? Il semble que l'heuristique ASCII soit la meilleure solution, mais combien de code supplémentaire, de temps d'exécution et de complexité cela ajouterait-il à quelque chose d'aussi compliqué qu'un antivirus, dans lequel ce code est réellement impliqué? Comment entraîneriez-vous le moteur heuristique pour savoir ce qui est acceptable en Base64 et ce qui ne l'est pas?


METTRE À JOUR:

Compte tenu du nombre de vues que cette question continue d'obtenir, j'ai décidé de publier le simple RegEx que j'utilise dans une application C # depuis 3 ans maintenant, avec des centaines de milliers de transactions. Honnêtement, j'aime le mieux la réponse donnée par Gumbo , c'est pourquoi je l'ai choisie comme réponse choisie. Mais pour quiconque utilise C # et cherche un moyen très rapide de détecter au moins si une chaîne ou un octet [] contient des données Base64 valides ou non, j'ai trouvé que ce qui suit fonctionnait très bien pour moi.

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

Et oui, c'est juste pour une STRING de données Base64, PAS un message RFC1341 correctement formaté . Donc, si vous traitez avec des données de ce type, veuillez en tenir compte avant d'essayer d'utiliser le RegEx ci-dessus. Si vous faites affaire avec Base16, Base32, Radix ou même base64 à d' autres fins (URL, noms de fichiers, XML d' encodage, etc.), il est fortement recommandé de lire RFC4648 qui Gumbo mentionné dans sa réponse que vous devez être bien conscient du jeu de caractères et des terminateurs utilisés par l'implémentation avant d'essayer d'utiliser les suggestions de cet ensemble de questions / réponses.

LarryF
la source
Je suppose que vous devez mieux définir la tâche. On ne sait pas du tout quel est votre objectif: être strict? analyser 100% des échantillons? ...
ADEpt le
Votre premier exemple devrait être 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs
Pourquoi ne pas utiliser une solution standard dans votre langue? Pourquoi avez-vous besoin d'un analyseur manuscrit basé sur des expressions régulières?
jfs
1
Excellente question. Bien que j'ai essayé le regex UPDATE en l'exécutant sur un SHA encodé en base64 retourné par NPM et cela a échoué alors que le regex dans la réponse sélectionnée fonctionne très bien .
Josh Habdas
1
Je ne sais pas comment l' expression régulière UPDATE est toujours publiée sans correction, mais il semble que l'auteur ait voulu mettre ^les parenthèses en dehors des crochets, comme point d'ancrage de départ. Cependant, une bien meilleure regex, sans devenir aussi compliquée que la réponse acceptée, serait^[-A-Za-z0-9+/]*={0,3}$
kael

Réponses:

147

De la RFC 4648 :

Le codage de base des données est utilisé dans de nombreuses situations pour stocker ou transférer des données dans des environnements qui, peut-être pour des raisons héritées, sont limités aux données US-ASCII.

Cela dépend donc du but de l'utilisation des données codées si les données doivent être considérées comme dangereuses.

Mais si vous recherchez simplement une expression régulière correspondant à des mots encodés en Base64, vous pouvez utiliser ce qui suit:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
Gombo
la source
10
La solution la plus simple serait de supprimer tous les espaces (ce qui est ignoré selon la RFC) avant la validation.
Ben Blank
2
Le dernier groupe non capturant pour le remplissage est facultatif.
Gumbo
4
Au début, j'étais sceptique sur la complexité, mais cela se valide assez bien. Si vous voulez juste faire correspondre base64-ish, je proposerais ^ [a-zA-Z0-9 + /] = {0,3} $, c'est mieux!
Lodewijk
3
@BogdanNechyporenko C'est parce qu'il names'agit d'un codage Base64 valide de la séquence d'octets (hexadécimal) 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})$doit échapper au contrecoup
khizar syed
37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Celui-ci est bon, mais correspondra à une chaîne vide

Celui-ci ne correspond pas à une chaîne vide:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$
njzk2
la source
2
Pourquoi une chaîne vide est-elle invalide?
Josh Lee
8
ce n'est pas. mais si vous utilisez une expression régulière pour savoir si une chaîne donnée est ou non base64, il y a de fortes chances que vous ne soyez pas intéressé par les chaînes vides. Au moins je sais que je ne le suis pas.
njzk2
4
@LayZee: si vous le faites, vous forcez la chaîne base64 à contenir au moins un bloc de 4 tailles, ce qui rend les valeurs valides telles que MQ==ne pas correspondre à votre expression
njzk2
5
@ruslan ni ne le devrait. ce n'est pas une chaîne de base 64 valide. (la taille est 23, ce qui n'est pas // 4). AQENVg688MSGlEgdOJpjIUC=est la forme valide.
njzk2
1
@JinKwon base64 se termine par 0, 1 ou 2 =. Le dernier ?autorise 0 =. Le remplacer par {1}nécessite 1 ou 2 fin=
njzk2
4

Ni un " : " ni un " . " N'apparaîtront dans Base64 valide, donc je pense que vous pouvez sans ambiguïté jeter la http://www.stackoverflow.comligne. En Perl, disons, quelque chose comme

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

say decode_base64($sanitized_str);

pourrait être ce que vous voulez. Cela produit

Il s'agit d'un exemple simple ASCII Base64 pour StackOverflow.

oylenshpeegul
la source
Je peux être d'accord, mais toutes les AUTRES lettres de l'URL se trouvent être valides en base64 ... Alors, où tracez-vous la ligne? Juste aux sauts de ligne? (J'ai vu ceux où il n'y a que quelques caractères aléatoires au milieu de la ligne. Je ne peux pas lancer le reste de la ligne juste à cause de cela, à
mon humble avis
@LarryF: à moins qu'il y ait une vérification d'intégrité des données encodées en base 64, vous ne pouvez pas dire quoi faire avec un bloc de données en base 64 contenant des caractères incorrects. Quelle est la meilleure heuristique: ignorer les caractères incorrects (en autorisant tous les bons) ou rejeter les lignes, ou rejeter le lot?
Jonathan Leffler
(suite): la réponse courte est "cela dépend" - d'où proviennent les données et du type de désordre que vous y trouvez.
Jonathan Leffler
(reprise): Je vois dans les commentaires à la question que vous voulez accepter tout ce qui pourrait être en base 64. Donc, mappez simplement chaque caractère qui n'est pas dans votre alphabet de base 64 (notez qu'il existe des encodages de type URL sûr et d'autres variantes), y compris les nouvelles lignes et les deux points, et prenez ce qui reste.
Jonathan Leffler
3

La meilleure expression rationnelle que j'ai pu trouver jusqu'à présent est ici https://www.npmjs.com/package/base64-regex

qui est dans la version actuelle ressemble à:

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
la source
Peut-être mieux sans \\n?.
Jin Kwon
Cela échouera sur les chaînes JSON
idleberg le
3

Pour valider l' image base64, nous pouvons utiliser cette regex

/ ^ données: 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
la source
0

Voici une autre expression régulière:

^(?=(.{4})*$)[A-Za-z0-9+/]*={0,2}$

Il satisfait aux conditions suivantes:

  • La longueur de la chaîne doit être un multiple de quatre - (?=^(.{4})*$)
  • Le contenu doit être composé de caractères alphanumériques ou + ou / - [A-Za-z0-9+/]*
  • Il peut avoir jusqu'à deux caractères de remplissage (=) à la fin - ={0,2}
  • Il accepte les chaînes vides
Paul
la source