RegEx để phân tích cú pháp hoặc xác thực dữ liệu Base64


99

Có thể sử dụng RegEx để xác thực hoặc làm sạch dữ liệu Base64 không? Đó là câu hỏi đơn giản, nhưng các yếu tố thúc đẩy câu hỏi này là những gì làm cho nó khó khăn.

Tôi có bộ giải mã Base64 không thể hoàn toàn dựa vào dữ liệu đầu vào để tuân theo các thông số kỹ thuật RFC. Vì vậy, các vấn đề tôi gặp phải là các vấn đề như có lẽ dữ liệu Base64 có thể không được chia thành 78 (tôi nghĩ đó là 78, tôi phải kiểm tra lại RFC, vì vậy đừng ding tôi nếu số chính xác bị sai) dòng, hoặc các dòng có thể không kết thúc bằng CRLF; trong đó nó có thể chỉ có CR, hoặc LF, hoặc có thể không.

Vì vậy, tôi đã rất mất thời gian phân tích cú pháp dữ liệu Base64 được định dạng như vậy. Do đó, các ví dụ như sau không thể giải mã một cách đáng tin cậy. Tôi sẽ chỉ hiển thị một phần tiêu đề MIME cho ngắn gọn.

Content-Transfer-Encoding: base64

VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Ok, vì vậy việc phân tích cú pháp đó không có vấn đề gì và chính xác là kết quả mà chúng tôi mong đợi. Và trong 99% trường hợp, sử dụng bất kỳ mã nào để ít nhất xác minh rằng mỗi char trong bộ đệm là một char base64 hợp lệ, hoạt động hoàn hảo. Tuy nhiên, ví dụ tiếp theo ném một cờ lê vào hỗn hợp.

Content-Transfer-Encoding: base64

http://www.stackoverflow.com
VGhpcyBpcyBzaW1wbGUgQVNDSUkgQmFzZTY0IGZvciBTdGFja092ZXJmbG93IGV4YW1wbGUu

Đây là phiên bản mã hóa Base64 mà tôi đã thấy trong một số loại vi-rút và những thứ khác cố gắng lợi dụng một số người đọc thư mong muốn phân tích cú pháp kịch câm bằng mọi giá, so với những phiên bản hoàn toàn theo cuốn sách, hay đúng hơn là RFC; nếu bạn muốn.

Bộ giải mã Base64 của tôi giải mã ví dụ thứ hai cho luồng dữ liệu sau. Và hãy nhớ ở đây, luồng gốc là tất cả dữ liệu ASCII!

[0x]86DB69FFFC30C2CB5A724A2F7AB7E5A307289951A1A5CC81A5CC81CDA5B5C1B19481054D0D
2524810985CD94D8D08199BDC8814DD1858DAD3DD995C999B1BDDC8195E1B585C1B194B8

Bất cứ ai có một cách tốt để giải quyết cả hai vấn đề cùng một lúc? Tôi không chắc điều đó thậm chí có thể xảy ra, ngoài việc thực hiện hai phép chuyển đổi trên dữ liệu với các quy tắc khác nhau được áp dụng và so sánh kết quả. Tuy nhiên, nếu bạn thực hiện cách tiếp cận đó, bạn tin tưởng đầu ra nào? Có vẻ như ASCII heuristics là giải pháp tốt nhất , nhưng có bao nhiêu mã, thời gian thực thi và độ phức tạp sẽ thêm vào một thứ phức tạp như máy quét vi-rút, mà mã này thực sự có liên quan? Bạn sẽ đào tạo công cụ heuristics như thế nào để tìm hiểu Base64 có thể chấp nhận được và điều gì không?


CẬP NHẬT:

Do số lượt xem mà câu hỏi này vẫn tiếp tục nhận được, tôi đã quyết định đăng RegEx đơn giản mà tôi đã sử dụng trong một ứng dụng C # trong 3 năm nay, với hàng trăm nghìn giao dịch. Thành thật mà nói, tôi thích câu trả lời do Gumbo đưa ra nhất, đó là lý do tại sao tôi chọn nó làm câu trả lời đã chọn. Nhưng đối với bất kỳ ai sử dụng C # và đang tìm kiếm một cách rất nhanh để ít nhất là phát hiện một chuỗi hoặc byte [] có chứa dữ liệu Base64 hợp lệ hay không, tôi thấy những điều sau đây hoạt động rất tốt cho tôi.

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

Và có, đây chỉ dành cho STRING dữ liệu Base64, KHÔNG PHẢI là thông báo RFC1341 được định dạng đúng . Vì vậy, nếu bạn đang xử lý dữ liệu kiểu này, hãy tính đến điều đó trước khi cố gắng sử dụng RegEx ở trên. Nếu bạn đang đối phó với Base16, Base32, Radix hoặc thậm chí Base64 cho các mục đích khác (URL, tên file, XML Encoding, vv), sau đó nó được đánh giá cao đề nghị bạn đọc RFC4648 rằng Gumbo đề cập trong câu trả lời của mình như là bạn cần phải hiểu rõ biết về bộ ký tự và ký tự kết thúc được sử dụng bởi triển khai trước khi cố gắng sử dụng các gợi ý trong bộ câu hỏi / câu trả lời này.


Tôi đoán rằng bạn phải xác định nhiệm vụ tốt hơn. Hoàn toàn không rõ mục đích của bạn là gì: nghiêm khắc? phân tích cú pháp 100% các mẫu? ...
ADEpt

Bạn ví dụ đầu tiên nên 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
JFS

Tại sao không sử dụng một giải pháp tiêu chuẩn trong ngôn ngữ của bạn? Tại sao bạn cần phân tích cú pháp viết tay dựa trên regexs?
jfs

1
Câu hỏi tuyệt vời. Mặc dù tôi đã thử UPDATE regex bằng cách chạy nó với SHA được mã hóa base64 do NPM trả lại và nó không thành công trong khi regex trong câu trả lời đã chọn hoạt động tốt .
Josh Habdas

1
Không chắc bằng cách nào mà UPDATE regex vẫn được đăng mà không cần chỉnh sửa, nhưng có vẻ như tác giả muốn đặt ^bên ngoài dấu ngoặc, như một ký tự bắt đầu. Tuy nhiên, một nhiều regex tốt hơn, mà không nhận được phức tạp như câu trả lời được chấp nhận, sẽ là^[-A-Za-z0-9+/]*={0,3}$
Kael

Câu trả lời:


145

Từ RFC 4648 :

Mã hóa cơ sở của dữ liệu được sử dụng trong nhiều trường hợp để lưu trữ hoặc chuyển dữ liệu trong các môi trường, có lẽ vì lý do kế thừa, bị hạn chế đối với dữ liệu US-ASCII.

Vì vậy, nó phụ thuộc vào mục đích sử dụng của dữ liệu được mã hóa nếu dữ liệu đó nên được coi là nguy hiểm.

Nhưng nếu bạn chỉ đang tìm kiếm một biểu thức chính quy để khớp với các từ được mã hóa Base64, bạn có thể sử dụng như sau:

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

10
Giải pháp đơn giản nhất là loại bỏ tất cả khoảng trắng (được bỏ qua theo RFC) trước khi xác nhận.
Ben Blank

2
Nhóm không chụp cuối cùng cho phần đệm là tùy chọn.
Gumbo

4
Lúc đầu tôi đã nghi ngờ về sự phức tạp, nhưng nó xác nhận khá tốt. Nếu bạn chỉ muốn đối sánh base64-ish, tôi sẽ thực hiện ^ [a-zA-Z0-9 + /] = {0,3} $, thì tốt hơn!
Lodewijk

3
@BogdanNechyporenko Đó là bởi vì đây namelà mã hóa Base64 hợp lệ của chuỗi 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})$phải thoát khỏi phản ứng dữ dội
khizar syed

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

Cái này tốt, nhưng sẽ khớp với một chuỗi trống

Cái này không khớp với chuỗi trống:

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

2
Tại sao một chuỗi rỗng không hợp lệ?
Josh Lee

8
không phải vậy. nhưng nếu bạn đang sử dụng regex để tìm xem một chuỗi đã cho có phải là base64 hay không, rất có thể bạn không quan tâm đến các chuỗi rỗng. Ít nhất tôi biết tôi không.
njzk2

4
@LayZee: nếu bạn làm như vậy, bạn buộc chuỗi base64 để chứa ít nhất một khối 4 kích thước, khiến giá trị hợp lệ như MQ==không phù hợp với cụm của bạn
njzk2

5
@ruslan cũng không nên. đây không phải là một chuỗi cơ sở 64 hợp lệ. (kích thước là 23, không phải là // 4). AQENVg688MSGlEgdOJpjIUC=là hình thức hợp lệ.
njzk2

1
@JinKwon base64 kết thúc bằng 0, 1 hoặc 2 =. Cuối cùng ?cho phép 0 =. Thay thế nó bằng {1}đòi hỏi 1 hoặc 2 kết thúc=
njzk2

4

Cả " : " và " . " Sẽ không hiển thị trong Base64 hợp lệ, vì vậy tôi nghĩ bạn có thể rõ ràng loại bỏ http://www.stackoverflow.comdòng này. Trong Perl, nói, một cái gì đó như

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

say decode_base64($sanitized_str);

có thể là những gì bạn muốn. Nó sản xuất

Đây là ASCII Base64 đơn giản cho bản đồ ngoại vi StackOverflow.


Tôi có thể đồng ý ở đó, nhưng tất cả các chữ cái KHÁC trong URL thực sự là base64 hợp lệ ... Vì vậy, bạn vẽ dòng ở đâu? Chỉ cần ngắt dòng? (Tôi đã thấy những nơi có chỉ là một vài ký tự ngẫu nhiên ở giữa dòng không thể quăng phần còn lại của dòng chỉ vì lý do đó, IMHO.) ...
LarryF

@LarryF: trừ khi có kiểm tra tính toàn vẹn trên dữ liệu được mã hóa base-64, bạn không thể biết phải làm gì với bất kỳ khối dữ liệu base-64 nào chứa các ký tự không chính xác. Phương pháp nào là tốt nhất: bỏ qua các ký tự không chính xác (cho phép bất kỳ và tất cả các ký tự chính xác) hoặc từ chối các dòng, hoặc từ chối rất nhiều?
Jonathan Leffler

(tiếp theo): câu trả lời ngắn gọn là "nó phụ thuộc" - dữ liệu đến từ đâu và các loại hỗn độn bạn tìm thấy trong đó.
Jonathan Leffler

(tiếp tục): Tôi thấy từ nhận xét cho đến câu hỏi rằng bạn muốn chấp nhận bất kỳ thứ gì có thể là base-64. Vì vậy, chỉ cần ánh xạ từng ký tự không có trong bảng chữ cái cơ sở 64 của bạn (lưu ý rằng có các mã hóa biến thể an toàn cho URL và các biến thể khác) bao gồm các dòng mới và dấu hai chấm, và lấy những gì còn lại.
Jonathan Leffler

3

Regexp tốt nhất mà tôi có thể tìm thấy cho đến bây giờ là ở đây https://www.npmjs.com/package/base64-regex

trong phiên bản hiện tại trông giống như sau:

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

Có lẽ tốt hơn mà không có \\n?.
Jin Kwon

Điều này sẽ thất bại trên dây JSON
idleberg

3

Để xác thực hình ảnh base64, chúng ta có thể sử dụng regex này

/ ^ 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);
  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.