RegEx para analisar ou validar dados Base64

99

É possível usar um RegEx para validar ou higienizar dados Base64? Essa é uma questão simples, mas os fatores que a motivam são os que a tornam difícil.

Eu tenho um decodificador Base64 que não pode confiar totalmente nos dados de entrada para seguir as especificações RFC. Então, os problemas que eu enfrento são problemas como talvez dados de Base64 que não podem ser divididos em 78 (eu acho que é 78, eu teria que verificar o RFC, então não me diga se o número exato estiver errado) caractere linhas, ou que as linhas não podem terminar em CRLF; no sentido de que pode ter apenas um CR, ou LF, ou talvez nenhum.

Então, eu tive um inferno de tempo ao analisar dados Base64 formatados como tal. Devido a isso, exemplos como o seguinte tornam-se impossíveis de decodificar de forma confiável. Exibirei apenas cabeçalhos MIME parciais por questões de brevidade.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ok, então analisar isso não é problema e é exatamente o resultado que esperaríamos. E em 99% dos casos, usar qualquer código para pelo menos verificar se cada caractere no buffer é um caractere base64 válido funciona perfeitamente. Mas, o próximo exemplo joga uma chave na mistura.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Esta é uma versão da codificação Base64 que vi em alguns vírus e outras coisas que tentam tirar vantagem do desejo de alguns leitores de e-mail de analisar a mímica a todo custo, em comparação com aquelas que seguem estritamente o livro, ou melhor, RFC; Se você for.

Meu decodificador Base64 decodifica o segundo exemplo para o seguinte fluxo de dados. E tenha em mente aqui, o stream original é todo de dados ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Alguém tem uma boa maneira de resolver os dois problemas ao mesmo tempo? Não tenho certeza se isso é possível, além de fazer duas transformações nos dados com regras diferentes aplicadas e comparar os resultados. No entanto, se você adotou essa abordagem, em qual resultado você confia? Parece que a heurística ASCII é a melhor solução, mas quanto mais código, tempo de execução e complexidade isso acrescentaria a algo tão complicado quanto um scanner de vírus, no qual esse código está realmente envolvido? Como você treinaria o mecanismo de heurística para aprender o que é Base64 aceitável e o que não é?


ATUALIZAR:

Quanto ao número de visualizações que esta pergunta continua obtendo, decidi postar o RegEx simples que venho usando em um aplicativo C # há 3 anos, com centenas de milhares de transações. Honestamente, eu gosto mais da resposta dada por Gumbo , e é por isso que a escolhi como a resposta selecionada. Mas para qualquer pessoa que use C # e esteja procurando uma maneira muito rápida de pelo menos detectar se uma string ou byte [] contém dados Base64 válidos ou não, descobri que o seguinte funciona muito bem para mim.

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

E sim, isso é apenas para uma STRING de dados Base64, NÃO uma mensagem RFC1341 formatada corretamente . Portanto, se você estiver lidando com dados desse tipo, leve isso em consideração antes de tentar usar o RegEx acima. Se você está lidando com Base16, Base32, Radix ou mesmo Base64 para outros fins (URLs, nomes de arquivos, codificação XML, etc.), então é altamente recomendável que você leia a RFC4648 que Gumbo mencionou em sua resposta, pois você precisa estar bem ciente do conjunto de caracteres e terminadores usados ​​pela implementação antes de tentar usar as sugestões neste conjunto de perguntas / respostas.

LarryF
fonte
Acho que você tem que definir melhor a tarefa. Não está totalmente claro qual é o seu objetivo: ser rigoroso? analisar 100% das amostras? ...
ADEpt
Seu primeiro exemplo deve ser 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs
Por que não usa uma solução padrão em seu idioma? Por que você precisa de um analisador escrito à mão baseado em regexs?
jfs
1
Ótima pergunta. Embora eu tenha tentado a regex UPDATE executando-a em um SHA codificado em base64 retornado pelo NPM e tenha falhado, a regex na resposta selecionada funciona bem .
Josh Habdas
1
Não tenho certeza de como o regex UPDATE ainda é postado sem correção, mas parece que o autor pretendia colocar o ^fora dos colchetes, como uma âncora de início. No entanto, uma regex muito melhor, sem ficar tão complicada quanto a resposta aceita, seria^[-A-Za-z0-9+/]*={0,3}$
kael

Respostas:

145

Do RFC 4648 :

A codificação básica de dados é usada em muitas situações para armazenar ou transferir dados em ambientes que, talvez por motivos legados, são restritos a dados US-ASCII.

Portanto, depende do propósito de uso dos dados codificados se os dados devem ser considerados perigosos.

Mas se você está apenas procurando uma expressão regular que corresponda às palavras codificadas em Base64, você pode usar o seguinte:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
quiabo
fonte
10
A solução mais simples seria remover todos os espaços em branco (que são ignorados de acordo com o RFC) antes da validação.
Ben Blank
2
O último grupo sem captura para o preenchimento é opcional.
Gumbo
4
No começo eu estava cético quanto à complexidade, mas valida muito bem. Se você apenas gostaria de combinar base64-ish, eu faria ^ [a-zA-Z0-9 + /] = {0,3} $, isso é melhor!
Lodewijk de
3
@BogdanNechyporenko Isso porque nameé uma codificação Base64 válida da sequência de bytes (hex) 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 escapar da reação
khizar syed
37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Este é bom, mas corresponderá a uma string vazia

Este não corresponde a uma string vazia:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$
njzk2
fonte
2
Por que uma string vazia é inválida?
Josh Lee
8
não é. mas se você estiver usando uma regex para descobrir se uma determinada string é ou não base64, é provável que você não esteja interessado em strings vazias. Pelo menos eu sei que não sou.
njzk2
4
@LayZee: se você fizer isso, você força a string base64 a conter pelo menos um bloco de 4 tamanhos, renderizando valores válidos, como MQ==não corresponder à sua expressão
njzk2
5
@ruslan nem deveria. esta não é uma string de base 64 válida. (o tamanho é 23, o que não é // 4). AQENVg688MSGlEgdOJpjIUC=é a forma válida.
njzk2
1
@JinKwon base64 termina com 0, 1 ou 2 =. O último ?permite 0 =. Substituí-lo por {1}requer 1 ou 2 terminando=
njzk2
4

Nem um " : " nem um " . " Serão exibidos em uma Base64 válida, então acho que você pode descartar a http://www.stackoverflow.comlinha sem ambigüidades . Em Perl, digamos, algo como

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

say decode_base64($sanitized_str);

pode ser o que você deseja. Produz

Este é um exemplo ASCII Base64 simples para StackOverflow.

Oylenshpeegul
fonte
Posso concordar, mas todas as OUTRAS letras na URL são base64 válida ... Então, onde você traça o limite? Apenas nas quebras de linha? (Eu vi alguns em que há apenas alguns caracteres aleatórios no meio da linha. Não posso jogar o resto da linha só por causa disso, IMHO) ...
LarryF
@LarryF: a menos que haja verificação de integridade nos dados codificados em base 64, você não pode dizer o que fazer com qualquer bloco de dados em base 64 contendo caracteres incorretos. Qual é a melhor heurística: ignorar os caracteres incorretos (permitindo todos e quaisquer corretos) ou rejeitar as linhas, ou rejeitar o lote?
Jonathan Leffler
(continuação): a resposta curta é "depende" - da origem dos dados e do tipo de bagunça que você encontra neles.
Jonathan Leffler
(retomado): vejo pelos comentários à pergunta que você deseja aceitar qualquer coisa que possa ser de base 64. Portanto, simplesmente mapeie todos os caracteres que não estão em seu alfabeto de base 64 (observe que existem codificações seguras para URL e outras codificações variantes), incluindo novas linhas e dois-pontos, e pegue o que resta.
Jonathan Leffler
3

A melhor regexp que consegui encontrar até agora está aqui https://www.npmjs.com/package/base64-regex

que está na versão atual se parece com:

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
fonte
Talvez seja melhor sem \\n?.
Jin Kwon
Isso falhará em strings JSON
idleberg
3

Para validar a imagem base64 , podemos usar este 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);
  }
Jayani Sumudini
fonte