RegEx para analizar o validar datos Base64

99

¿Es posible utilizar una expresión regular para validar o desinfectar los datos de Base64? Esa es la pregunta simple, pero los factores que impulsan esta pregunta son los que la dificultan.

Tengo un decodificador Base64 que no puede confiar completamente en los datos de entrada para seguir las especificaciones RFC. Entonces, los problemas que enfrento son problemas como quizás los datos Base64 que pueden no dividirse en 78 (creo que es 78, tendría que verificar el RFC, así que no me diga si el número exacto es incorrecto) líneas, o que las líneas no pueden terminar en CRLF; en el sentido de que puede tener solo un CR, o LF, o tal vez ninguno.

Por lo tanto, me ha costado muchísimo analizar datos Base64 formateados como tales. Debido a esto, los ejemplos como el siguiente se vuelven imposibles de decodificar de manera confiable. Solo mostraré encabezados MIME parciales por brevedad.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Bien, analizar eso no es un problema, y ​​es exactamente el resultado que esperaríamos. Y en el 99% de los casos, usar cualquier código para al menos verificar que cada carácter en el búfer es un carácter base64 válido, funciona perfectamente. Pero, el siguiente ejemplo arroja una llave inglesa a la mezcla.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Esta es una versión de la codificación Base64 que he visto en algunos virus y otras cosas que intentan aprovechar el deseo de algunos lectores de correo de analizar la mímica a toda costa, frente a los que se rigen estrictamente por el libro, o más bien RFC; Si tu quieres.

Mi decodificador Base64 decodifica el segundo ejemplo en el siguiente flujo de datos. Y tenga en cuenta aquí que la transmisión original son todos los datos ASCII.

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

¿Alguien tiene una buena manera de resolver ambos problemas a la vez? No estoy seguro de que sea posible, aparte de hacer dos transformaciones en los datos con diferentes reglas aplicadas y comparar los resultados. Sin embargo, si adoptó ese enfoque, ¿en qué resultado confía? Parece que la heurística ASCII es la mejor solución, pero ¿cuánto más código, tiempo de ejecución y complejidad agregaría eso a algo tan complicado como un escáner de virus, en el que este código está realmente involucrado? ¿Cómo entrenaría el motor heurístico para aprender qué es Base64 aceptable y qué no?


ACTUALIZAR:

Teniendo en cuenta la cantidad de vistas que sigue recibiendo esta pregunta, he decidido publicar la expresión regular simple que he estado usando en una aplicación C # durante 3 años, con cientos de miles de transacciones. Honestamente, me gusta más la respuesta dada por Gumbo , por eso la elegí como la respuesta seleccionada. Pero para cualquiera que use C # y busque una forma muy rápida de al menos detectar si una cadena o byte [] contiene datos Base64 válidos o no, he encontrado que lo siguiente funciona muy bien para mí.

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

Y sí, esto es solo para una CADENA de datos Base64, NO para un mensaje RFC1341 con el formato adecuado . Por lo tanto, si está tratando con datos de este tipo, téngalo en cuenta antes de intentar utilizar la expresión regular anterior. Si se trata de Base16, Base32, Radix base 64 o incluso para otros fines (URL, nombres de archivo XML de codificación, etc.), entonces es altamente recomendable que lea RFC4648 que Gumbo mencionó en su respuesta que tiene que ser así consciente del juego de caracteres y terminadores usados ​​por la implementación antes de intentar usar las sugerencias en este juego de preguntas / respuestas.

LarryF
fuente
Supongo que hay que definir mejor la tarea. No está del todo claro cuál es su objetivo: ¿ser estricto? analizar el 100% de las muestras? ...
ADEpt
El primer ejemplo debería ser 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs
¿Por qué no utiliza una solución estándar en su idioma? ¿Por qué necesita un analizador escrito a mano basado en expresiones regulares?
jfs
1
Gran pregunta. Aunque probé UPDATE regex ejecutándolo contra un SHA codificado en base64 devuelto por NPM y falló, mientras que la expresión regular en la respuesta seleccionada funciona bien .
Josh Habdas
1
No estoy seguro de cómo la expresión regular UPDATE se sigue publicando sin corrección, pero parece que el autor quiso poner el ^exterior de los corchetes, como un ancla de inicio. Sin embargo, una expresión regular mucho mejor, sin ser tan complicada como la respuesta aceptada, sería^[-A-Za-z0-9+/]*={0,3}$
kael

Respuestas:

145

Desde el RFC 4648 :

La codificación base de datos se usa en muchas situaciones para almacenar o transferir datos en entornos que, quizás por razones heredadas, están restringidos a datos US-ASCII.

Por lo tanto, depende del propósito de uso de los datos codificados si los datos deben considerarse peligrosos.

Pero si solo busca una expresión regular que coincida con las palabras codificadas en Base64, puede usar lo siguiente:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
Gumbo
fuente
10
La solución más simple sería eliminar todos los espacios en blanco (que se ignoran según el RFC) antes de la validación.
Ben Blank
2
El último grupo que no captura el relleno es opcional.
Gumbo
4
Al principio, era escéptico sobre la complejidad, pero se valida bastante bien. Si solo desea hacer coincidir base64-ish, se me ocurrirá hacer ^ [a-zA-Z0-9 + /] = {0,3} $, ¡esto es mejor!
Lodewijk
3
@BogdanNechyporenko Eso es porque namees una codificación Base64 válida de la secuencia de bytes (hexadecimal) 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})$debe escapar de la reacción violenta
khizar syed
37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Este es bueno, pero coincidirá con una cadena vacía

Este no coincide con la cadena vacía:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$
njzk2
fuente
2
¿Por qué una cadena vacía no es válida?
Josh Lee
8
No lo es. pero si está utilizando una expresión regular para averiguar si una cadena dada es o no es base64, es probable que no esté interesado en cadenas vacías. Al menos sé que no lo soy.
njzk2
4
@LayZee: si lo hace, fuerza a la cadena base64 para que contenga al menos un bloque de 4 tamaños, lo que genera valores válidos, como MQ==no coincidir con su expresión
njzk2
5
@ruslan ni debería. esta no es una cadena base 64 válida. (el tamaño es 23, que no es // 4). AQENVg688MSGlEgdOJpjIUC=es la forma válida.
njzk2
1
@JinKwon base64 termina con 0, 1 o 2 =. El último ?permite 0 =. Reemplazarlo con {1}requiere 1 o 2 terminaciones=
njzk2
4

Ni un " : " ni un " . " Se mostrarán en Base64 válida, así que creo que puede descartar la http://www.stackoverflow.comlínea sin ambigüedades . En Perl, digamos, algo como

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

say decode_base64($sanitized_str);

puede ser lo que quieras. Produce

Este es un ejemplo simple de ASCII Base64 para StackOverflow.

Oylenshpeegul
fuente
Puedo estar de acuerdo allí, pero todas las OTRAS letras en la URL resultan ser base64 válidas ... Entonces, ¿dónde trazas la línea? ¿Solo en los saltos de línea? (He visto algunos en los que hay solo un par de caracteres aleatorios en el medio de la línea. No puedo tirar el resto de la línea solo por eso, en mi humilde opinión) ...
LarryF
@LarryF: a menos que haya una verificación de integridad en los datos codificados en base 64, no puede saber qué hacer con cualquier bloque de datos en base 64 que contenga caracteres incorrectos. ¿Cuál es la mejor heurística: ignorar los caracteres incorrectos (permitiendo todos y cada uno de los correctos) o rechazar las líneas o rechazar el lote?
Jonathan Leffler
(continuación): la respuesta corta es "depende", de dónde provienen los datos y el tipo de desorden que encuentres en ellos.
Jonathan Leffler
(resumido): Veo por los comentarios a la pregunta que desea aceptar cualquier cosa que pueda ser base-64. Así que simplemente mapee todos y cada uno de los caracteres que no estén en su alfabeto base 64 (tenga en cuenta que hay codificaciones de URL seguras y otras codificaciones similares), incluidas las nuevas líneas y los dos puntos, y tome lo que queda.
Jonathan Leffler
3

La mejor expresión regular que pude encontrar hasta ahora está aquí https://www.npmjs.com/package/base64-regex

que está en la versión actual se ve así:

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
fuente
Quizás mejor sin \\n?.
Jin Kwon
Esto fallará en las cadenas JSON
idleberg
3

Para validar la imagen base64 podemos usar esta expresión regular

/ ^ datos: imagen / (?: gif | png | jpeg | bmp | webp) (?:; juego de caracteres = 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
fuente