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

99

Можно ли использовать 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.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

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

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

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

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


ОБНОВИТЬ:

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

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

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

ЛарриФ
источник
Думаю, нужно лучше сформулировать задачу. Совершенно непонятно, какова ваша цель: быть строгим? разобрать 100% образцов? ...
ADEpt
Ваш первый пример должен быть 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs
Почему бы не использовать стандартное решение на вашем языке? Зачем нужен рукописный парсер на основе регулярных выражений?
jfs
1
Отличный вопрос. Хотя я пробовал регулярное выражение UPDATE , запустив его для SHA в кодировке base64, возвращаемого NPM и это не удалось, тогда как регулярное выражение в выбранном ответе работает нормально .
Джош Хабдас
1
Не знаю, как регулярное выражение UPDATE по-прежнему публикуется без исправлений, но похоже, что автор хотел поставить ^за скобки в качестве начальной привязки. Однако гораздо лучшим регулярным выражением, не усложняющим принятый ответ, было бы^[-A-Za-z0-9+/]*={0,3}$
kael

Ответы:

147

Из RFC 4648 :

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

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

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

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
Гамбо
источник
10
Самым простым решением было бы удалить все пробелы (которые игнорируются согласно RFC) перед проверкой.
Бен Бланк,
2
Последняя не захватывающая группа для заполнения необязательна.
Gumbo
4
Сначала я скептически относился к сложности, но это подтверждается довольно хорошо. Если вы просто хотите сопоставить base64-ish, я бы сделал ^ [a-zA-Z0-9 + /] = {0,3} $, это лучше!
Lodewijk
3
@BogdanNechyporenko Это потому, что nameэто действительная кодировка Base64 (шестнадцатеричной) байтовой последовательности 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})$должен избежать негативной реакции
Хизар Сюд
37
^(?:[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})$
njzk2
источник
2
Почему пустая строка недействительна?
Джош Ли
8
это не. но если вы используете регулярное выражение, чтобы узнать, является ли данная строка base64 или нет, скорее всего, вас не интересуют пустые строки. По крайней мере, я знаю, что нет.
njzk2
4
@LayZee: если вы это сделаете, вы заставите строку base64 содержать как минимум блок размером 4, отображая допустимые значения, такие как MQ==
несоответствие
5
@ruslan и не должно. это недопустимая строка с основанием 64. (размер 23, что не // 4). AQENVg688MSGlEgdOJpjIUC=это действительная форма.
njzk2 02
1
@JinKwon base64 заканчивается на 0, 1 или 2 =. Последний ?допускает 0 =. Замена на {1}требует 1 или 2 концовки=
njzk2
4

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

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

say decode_base64($sanitized_str);

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

Это простой пример ASCII Base64 для StackOverflow.

ойленшпигуль
источник
Я могу согласиться с этим, но все ДРУГИЕ буквы в URL действительно являются действительными base64 ... Итак, где вы проводите черту? Просто при переносе строки? (Я видел такие, где есть всего пара случайных символов в середине строки. Не могу выбросить остальную часть строки только из-за этого, ИМХО) ...
LarryF
@LarryF: если нет проверки целостности данных в кодировке base-64, вы не можете сказать, что делать с любым блоком данных base-64, содержащим неправильные символы. Какая эвристика является наилучшей: игнорировать неправильные символы (разрешая любые и все правильные), или отклонять строки, или отклонять партию?
Джонатан Леффлер,
(продолжение): краткий ответ - «это зависит» - от того, откуда берутся данные, и от того, какой беспорядок вы в них обнаружите.
Джонатан Леффлер,
(возобновлено): Из комментариев к вопросу я вижу, что вы хотите принять все, что может быть base-64. Так что просто сопоставьте каждый символ, который не входит в ваш алфавит base-64 (обратите внимание, что есть URL-безопасные и другие подобные варианты кодирования), включая символы новой строки и двоеточия, и возьмите то, что осталось.
Джонатан Леффлер,
4

Лучшее регулярное выражение, которое я мог найти до сих пор, находится здесь 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');
};
Богдан Нечипоренко
источник
Может лучше без \\n?.
Джин Квон
Это не удастся для строк JSON
idleberg
3

Чтобы проверить изображение 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);
  }
Джаяни Сумудини
источник
0

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

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

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

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