RegEx untuk mengurai atau memvalidasi data Base64

99

Apakah mungkin menggunakan RegEx untuk memvalidasi, atau membersihkan data Base64? Itu pertanyaan sederhana, tetapi faktor yang mendorong pertanyaan ini adalah yang membuatnya sulit.

Saya memiliki decoder Base64 yang tidak dapat sepenuhnya mengandalkan data input untuk mengikuti spesifikasi RFC. Jadi, masalah yang saya hadapi adalah masalah seperti mungkin data Base64 yang mungkin tidak dipecah menjadi 78 (menurut saya 78, saya harus memeriksa ulang RFC, jadi jangan tanya saya jika nomor pastinya salah) karakter garis, atau garis mungkin tidak diakhiri dengan CRLF; dalam hal ini mungkin hanya CR, atau LF, atau mungkin tidak keduanya.

Jadi, saya mengalami kesulitan mengurai data Base64 yang diformat seperti itu. Karenanya, contoh seperti berikut menjadi tidak mungkin untuk didekode dengan andal. Saya hanya akan menampilkan header MIME parsial agar singkatnya.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Oke, jadi parsing itu tidak masalah, dan persis seperti hasil yang kita harapkan. Dan dalam 99% kasus, menggunakan kode apa pun untuk setidaknya memverifikasi bahwa setiap karakter dalam buffer adalah karakter base64 yang valid, berfungsi dengan sempurna. Tapi, contoh berikutnya melempar kunci pas ke dalam campuran.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ini adalah versi pengkodean Base64 yang telah saya lihat di beberapa virus dan hal-hal lain yang mencoba memanfaatkan keinginan pembaca email untuk mengurai pantomim dengan segala cara, versus yang hanya sesuai dengan buku, atau lebih tepatnya RFC; jika kamu mau.

Dekoder Base64 saya menerjemahkan contoh kedua ke aliran data berikut. Dan perlu diingat di sini, streaming aslinya adalah semua data ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Adakah yang punya cara bagus untuk menyelesaikan kedua masalah sekaligus? Saya tidak yakin itu bahkan mungkin, di luar melakukan dua transformasi pada data dengan aturan berbeda yang diterapkan, dan membandingkan hasilnya. Namun jika Anda mengambil pendekatan itu, keluaran mana yang Anda percayai? Tampaknya heuristik ASCII adalah tentang solusi terbaik , tetapi berapa banyak lagi kode, waktu eksekusi, dan kerumitan yang akan ditambahkan ke sesuatu yang serumit pemindai virus, di mana kode ini sebenarnya terlibat? Bagaimana Anda melatih mesin heuristik untuk mempelajari apa yang dapat diterima Base64, dan apa yang tidak?


MEMPERBARUI:

Lakukan terhadap jumlah tampilan yang terus didapat pertanyaan ini, saya telah memutuskan untuk memposting RegEx sederhana yang telah saya gunakan dalam aplikasi C # selama 3 tahun sekarang, dengan ratusan ribu transaksi. Jujur, saya paling suka jawaban yang diberikan oleh Gumbo , itulah mengapa saya memilihnya sebagai jawaban terpilih. Tetapi bagi siapa pun yang menggunakan C #, dan mencari cara yang sangat cepat untuk setidaknya mendeteksi apakah sebuah string, atau byte [] berisi data Base64 yang valid atau tidak, saya telah menemukan yang berikut ini bekerja dengan sangat baik untuk saya.

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

Dan ya, ini hanya untuk STRING data Base64, BUKAN pesan RFC1341 yang diformat dengan benar . Jadi, jika Anda berurusan dengan data jenis ini, harap pertimbangkan itu sebelum mencoba menggunakan RegEx di atas. Jika Anda berurusan dengan Base16, Base32, Radix atau bahkan Base64 untuk tujuan lain (URL, nama file, Pengkodean XML, dll.), Maka itu adalah sangat disarankan agar Anda membaca RFC4648 yang disebutkan Gumbo dalam jawabannya karena Anda perlu melakukannya dengan baik mengetahui charset dan terminator yang digunakan oleh implementasi sebelum mencoba menggunakan saran dalam set pertanyaan / jawaban ini.

LarryF
sumber
Saya rasa Anda harus mendefinisikan tugas dengan lebih baik. Tidak jelas apa tujuan Anda: bersikap tegas? mengurai 100% sampel? ...
ADEpt
Contoh pertama Anda harus 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs
Mengapa tidak menggunakan solusi standar dalam bahasa Anda? Mengapa Anda memerlukan parser tulisan tangan berdasarkan regex?
jfs
1
Pertanyaan bagus. Meskipun saya mencoba UPDATE regex dengan menjalankannya terhadap SHA berenkode base64 yang dikembalikan oleh NPM dan gagal sedangkan regex dalam jawaban yang dipilih berfungsi dengan baik .
Josh Habdas
1
Tidak yakin bagaimana ekspresi reguler UPDATE masih diposting tanpa koreksi, tetapi sepertinya penulis bermaksud meletakkan di ^luar tanda kurung, sebagai jangkar-awal. Namun, regex yang jauh lebih baik, tanpa serumit jawaban yang diterima, adalah^[-A-Za-z0-9+/]*={0,3}$
kael

Jawaban:

145

Dari RFC 4648 :

Pengkodean dasar data digunakan dalam banyak situasi untuk menyimpan atau mentransfer data di lingkungan yang, mungkin karena alasan lama, dibatasi untuk data US-ASCII.

Jadi itu tergantung pada tujuan penggunaan data yang disandikan jika data tersebut dianggap berbahaya.

Namun jika Anda hanya mencari ekspresi reguler untuk mencocokkan kata-kata yang dikodekan Base64, Anda dapat menggunakan yang berikut ini:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
Gumbo
sumber
10
Solusi paling sederhana adalah menghapus semua spasi (yang diabaikan sesuai RFC) sebelum validasi.
Ben Blank
2
Grup non-penangkap terakhir untuk padding adalah opsional.
Gumbo
4
Pada awalnya saya skeptis terhadap kerumitannya, tetapi validitasnya cukup baik. Jika Anda hanya ingin mencocokkan base64-ish yang akan saya lakukan ^ [a-zA-Z0-9 + /] = {0,3} $, ini lebih baik!
Lodewijk
3
@BogdanNechyporenko Itu karena nameadalah pengkodean Base64 valid dari urutan byte (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})$harus menghindari serangan balik
khizar syed
37
^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$

Yang ini bagus, tetapi akan cocok dengan String kosong

Yang ini tidak cocok dengan string kosong:

^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$
njzk2.dll
sumber
2
Mengapa string kosong tidak valid?
Josh Lee
8
bukan itu. tetapi jika Anda menggunakan regex untuk mengetahui apakah string yang diberikan adalah atau bukan base64, kemungkinan Anda tidak tertarik dengan string kosong. Setidaknya saya tahu saya tidak.
njzk2
4
@LayZee: jika Anda melakukannya, Anda memaksa string base64 untuk memuat setidaknya blok 4 ukuran, memberikan nilai yang valid seperti MQ==tidak cocok dengan ekspresi Anda
njzk2
5
@ruslan juga tidak seharusnya. ini bukan string basis 64 yang valid. (ukurannya 23, yang bukan // 4). AQENVg688MSGlEgdOJpjIUC=adalah formulir yang valid.
njzk2
1
@JinKwon base64 diakhiri dengan 0, 1 atau 2 =. Yang terakhir ?memungkinkan untuk 0 =. Menggantinya dengan {1}membutuhkan 1 atau 2 ending=
njzk2
4

Baik " : " maupun " . " Tidak akan muncul di Base64 yang valid, jadi menurut saya Anda dapat membuang http://www.stackoverflow.comgaris secara jelas. Di Perl, katakanlah, sesuatu seperti

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

say decode_base64($sanitized_str);

mungkin yang Anda inginkan. Itu menghasilkan

Ini adalah ASCII Base64 sederhana untuk contoh StackOverflow.

oylenshpeegul.dll
sumber
Saya setuju di sana, tetapi semua huruf LAIN di URL kebetulan adalah base64 yang valid ... Jadi, di mana Anda menarik garis? Hanya saat jeda baris? (Saya telah melihat yang hanya ada beberapa karakter acak di tengah baris. Tidak dapat membuang sisa baris hanya karena itu, IMHO) ...
LarryF
@ LarryF: kecuali ada pemeriksaan integritas pada data yang dikodekan base-64, Anda tidak dapat mengetahui apa yang harus dilakukan dengan blok data base-64 yang berisi karakter yang salah. Manakah heuristik terbaik: mengabaikan karakter yang salah (memperbolehkan salah satu dan semua karakter yang benar) atau menolak baris, atau menolak lot?
Jonathan Leffler
(lanjutan): jawaban singkatnya adalah "tergantung" - dari mana datanya berasal dan jenis kekacauan yang Anda temukan di dalamnya.
Jonathan Leffler
(dilanjutkan): Saya melihat dari komentar untuk pertanyaan bahwa Anda ingin menerima apa pun yang mungkin menjadi basis 64. Jadi cukup petakan setiap karakter yang tidak ada dalam alfabet base-64 Anda (perhatikan bahwa ada pengkodean yang aman untuk URL dan varian lainnya) termasuk baris dan titik dua baru, dan ambil yang tersisa.
Jonathan Leffler
3

Regexp terbaik yang dapat saya temukan hingga saat ini ada di sini https://www.npmjs.com/package/base64-regex

yang di versi saat ini terlihat seperti:

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
sumber
Mungkin lebih baik tanpa \\n?.
Jin Kwon
Ini akan gagal pada string JSON
idleberg
3

Untuk memvalidasi gambar base64 kita bisa menggunakan regex ini

/ ^ data: gambar / (?: 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
sumber