RegEx для анализа или проверки данных Base64

Можно ли использовать RegEx для проверки или дезинфекции данных Base64? Это простой вопрос, но факторы, которые движут этим вопросом, и делают его трудным.

У меня есть декодер Base64, который не может полностью полагаться на входные данные, чтобы следовать спецификациям RFC. Итак, проблемы, с которыми я сталкиваюсь, это проблемы, такие как, например, данные Base64, которые не могут быть разбиты на 78 (я думаю, что это 78, мне придется перепроверить RFC, так что не звоните мне, если точное число неверно) или что строки могут не заканчиваться на CRLF; в том, что он может иметь только CR, или LF, или, может быть, ни того, ни другого.

Итак, я чертовски разобрался с данными Base64, отформатированными как таковые. Из-за этого примеры, подобные следующим, становятся невозможными для надежного декодирования. Для краткости я буду отображать только частичные заголовки MIME.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Итак, разбор это не проблема, и это именно тот результат, который мы ожидаем. И в 99% случаев использование любого кода, по крайней мере, для проверки того, что каждый символ в буфере является действительным символом base64, работает отлично. Но, следующий пример бросает гаечный ключ в смесь.

Content-Transfer-Encoding: base64

http://www.stackru.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Это версия кодировки Base64, которую я видел в некоторых вирусах и других вещах, которые пытаются использовать преимущества некоторых почтовых читателей, желающих анализировать mime любой ценой, по сравнению с теми, которые строго соблюдаются в книге, или, скорее, RFC; если вы будете.

Мой Base64 декодер декодирует второй пример в следующий поток данных. И имейте в виду, что исходный поток - это все данные ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

У кого-нибудь есть хороший способ решить обе проблемы одновременно? Я не уверен, что это даже возможно, за исключением двух преобразований данных с применением различных правил и сравнения результатов. Однако, если вы выбрали такой подход, какому выходу вы доверяете? Кажется, что эвристика ASCII - это лучшее решение, но насколько больше кода, времени выполнения и сложности это добавит к чему-то столь сложному, как антивирусный сканер, в который этот код на самом деле вовлечен? Как бы вы обучили эвристический движок, чтобы узнать, что является приемлемым Base64, а что нет?


ОБНОВИТЬ:

Учитывая количество просмотров, которое продолжает получать этот вопрос, я решил опубликовать простой RegEx, который я использую в приложении C# уже 3 года, с сотнями тысяч транзакций. Честно говоря, мне больше всего нравится ответ, данный Gumbo, поэтому я выбрал его в качестве выбранного. Но для любого, кто использует C# и ищет очень быстрый способ, по крайней мере, определить, содержит ли строка или byte[] допустимые данные Base64 или нет, я обнаружил, что следующее очень хорошо работает для меня.

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

И да, это только для STRING данных Base64, а НЕ для правильно отформатированного сообщения RFC1341. Итак, если вы имеете дело с данными такого типа, пожалуйста, примите это во внимание, прежде чем пытаться использовать вышеупомянутый RegEx. Если вы имеете дело с Base16, Base32, Radix или даже Base64 для других целей (URL-адреса, имена файлов, кодировка XML и т. Д.), То настоятельно рекомендуем вам прочитать RFC4648, который Гамбо упомянул в своем ответе, так как вам нужно хорошо знать о кодировке и терминаторах, используемых реализацией, прежде чем пытаться использовать предложения из этого набора вопросов / ответов.

10 ответов

Решение

Из RFC 4648:

Базовое кодирование данных используется во многих ситуациях для хранения или передачи данных в средах, которые, возможно, по старым причинам, ограничены данными US-ASCII.

Таким образом, это зависит от цели использования закодированных данных, если данные следует считать опасными.

Но если вы просто ищете регулярное выражение для соответствия словам в кодировке Base64, вы можете использовать следующее:

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

Это хорошо, но будет соответствовать пустой строке

Этот не соответствует пустой строке:

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

В представленных до сих пор ответах не проверяется, что в строке Base64 все биты заполнения установлены на 0, что требуется для того, чтобы она была каноническим представлением Base64 (что важно в некоторых средах, см. https://tools.ietf.org/html/rfc4648#section-3.5) и, следовательно, они позволяют использовать псевдонимы с разными кодировками для одной и той же двоичной строки. Это может быть проблемой безопасности в некоторых приложениях.

Вот регулярное выражение, которое проверяет, что данная строка является не только допустимой базой 64, но и канонической строкой base64 для двоичных данных:

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

В процитированном RFC пустая строка считается действительной (см. https://tools.ietf.org/html/rfc4648#section-10), поэтому указанное выше регулярное выражение также работает.

Эквивалентное регулярное выражение для base64url (опять же, см. Вышеупомянутый RFC):

^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-][AQgw]==|[A-Za-z0-9_-]{2}[AEIMQUYcgkosw048]=)?$

Вот альтернативное регулярное выражение:

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

Он удовлетворяет следующим условиям:

  • Длина строки должна быть кратной четырем - (?=^(.{4})*$)
  • Содержимое должно состоять из буквенно-цифровых символов или + или / -. [A-Za-z0-9+/]*
  • Он может иметь до двух символов заполнения (=) на конце - ={0,2}
  • Принимает пустые строки

Чтобы проверить изображение base64, мы можем использовать это регулярное выражение

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

Ни ":", ни "." Не будут отображаться в действительном Base64, поэтому я думаю, что вы можете однозначно выбросить http://www.stackru.com линия. В Perl, скажем, что-то вроде

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

say decode_base64($sanitized_str);

может быть, что вы хотите. Производит

Это простой ASCII Base64 для Stackru exmaple.

Лучшее регулярное выражение, которое я мог найти до сих пор, находится здесь https://www.npmjs.com/package/base64-regex

который в текущей версии выглядит так:

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');
};

Кратчайшее регулярное выражение для проверки соответствия стандарту RFC-4648, обеспечивающего каноническое кодирование (т.е. все биты заполнения установлены в 0):

      ^(?=(.{4})*$)[A-Za-z0-9+/]*([AQgw]==|[AEIMQUYcgkosw048]=)?$

На самом деле это сочетание этого и что ответы.

Моя упрощенная версия регулярного выражения Base64:

^[A-Za-z0-9+/]*={0,2}$

Упрощение заключается в том, что он не проверяет, что его длина кратна 4. Если вам это нужно - используйте другие ответы. Мой фокусируется на простоте.

Чтобы проверить это: https://regex101.com/r/zdtGSH/1

Я нашел решение, которое очень хорошо работает

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

Он будет соответствовать следующим строкам

      VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu
YW55IGNhcm5hbCBwbGVhcw==
YW55IGNhcm5hbCBwbGVhc3U=
YW55IGNhcm5hbCBwbGVhc3Vy

в то время как он не будет соответствовать ни одному из этих недействительных

      YW5@IGNhcm5hbCBwbGVhcw==
YW55IGNhc=5hbCBwbGVhcw==
YW55%%%%IGNhcm5hbCBwbGVhc3V
YW55IGNhcm5hbCBwbGVhc3
YW55IGNhcm5hbCBwbGVhc
YW***55IGNhcm5hbCBwbGVh=
YW55IGNhcm5hbCBwbGVhc==
YW55IGNhcm5hbCBwbGVhc===
Другие вопросы по тегам