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 # і шукає дуже швидкий спосіб принаймні виявити, чи містить рядок чи байт [] дійсні дані Base64 чи ні, я виявив, що наступне дуже добре для мене працює.

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

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


Я здогадуюсь, що ви повинні краще визначити завдання. Зовсім незрозуміло, яка ваша мета: бути суворим? проаналізувати 100% зразків? ...
ADEpt

Першим прикладом має бути "VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 = '
jfs

Чому б не використовувати стандартне рішення на вашій мові? Для чого потрібен рукописний аналізатор на основі регулярних виразів?
jfs

1
Чудове запитання. Хоча я спробував регулярний вираз UPDATE , запустивши його проти SHA, кодованого base64, який повертає NPM, і він не вдався, тоді як регулярний вираз у вибраній відповіді працює чудово .
Джош Хабдас

1
Не впевнений, як регекс UPDATE все ще розміщується без виправлення, але схоже, що автор мав намір поставити ^зовнішні дужки, як стартовий якор. Тим НЕ менше, набагато краще регулярний вираз, не отримуючи так складно , як загальноприйнятому відповідь буде^[-A-Za-z0-9+/]*={0,3}$
Кель

Відповіді:


145

З RFC 4648 :

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

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

Але якщо ви просто шукаєте регулярний вираз, який відповідає закодованим словам Base64, ви можете використовувати наступне:

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

10
Найпростішим рішенням було б видалити весь пробіл (який ігнорується відповідно до RFC) перед валідацією.
Бен Бланк

2
Остання невловлююча група для заповнення є необов’язковою.
Гамбо,

4
Спочатку я скептично ставився до складності, але це підтверджує досить добре. Якщо ви просто хочете підібрати base64-ish, я б придумав зробити ^ [a-zA-Z0-9 + /] = {0,3} $, це краще!
Lodewijk,

3
@BogdanNechyporenko Це тому, що nameє дійсним кодуванням Base64 послідовності байтів (шістнадцяткового) 9d a9 9e.
Мартен

3
^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$повинен уникнути люфту
khizar syed

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})$

2
Чому порожній рядок недійсний?
Джош Лі

8
це не. але якщо ви використовуєте регулярний вираз, щоб дізнатись, чи є дана рядок base64, чи не є базовою64, швидше за все, вас не цікавлять порожні рядки. Принаймні я знаю, що ні.
njzk2

4
@LayZee: якщо ви це зробите, ви змусите рядок base64 містити щонайменше 4-розмірний блок, відображаючи дійсні значення, такі як MQ==не збігаються з вашим виразом
njzk2

5
@ruslan не повинен. це не дійсний базовий 64 рядок. (розмір 23, що не // 4). AQENVg688MSGlEgdOJpjIUC=є дійсною формою.
njzk2

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: якщо немає перевірки цілісності даних, закодованих базою 64, ви не можете сказати, що робити з будь-яким блоком даних бази 64, що містить неправильні символи. Яка найкраща евристика: ігнорувати неправильні символи (дозволяючи будь-які і всі правильні) або відхиляти рядки, або відкидати партію?
Джонатан Леффлер

(продовження): коротка відповідь "це залежить" - від того, звідки беруться дані, та якого роду безладу ви знайдете в них.
Джонатан Леффлер

(відновлено): З коментарів до питання я бачу, що ви хочете прийняти все, що може бути базовим-64. Тож просто нанесіть на карту кожен символ, якого немає у вашому базовому алфавіті 64 (зауважте, що існують URL-безпечні та інші подібні кодування варіантів), включаючи нові рядки та двокрапки, і візьміть те, що залишилось.
Джонатан Леффлер

3

Найкращий регулярний вираз, який я міг знайти досі, знаходиться тут 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 ми можемо використовувати цей регулярний вираз

/ ^ дані: 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);
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.