RegEx เพื่อแยกวิเคราะห์หรือตรวจสอบข้อมูล Base64


100

เป็นไปได้หรือไม่ที่จะใช้ 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 ปีแล้วโดยมีธุรกรรมหลายแสนรายการ จริงๆแล้วฉันชอบคำตอบของGumbo มากที่สุดนั่นคือเหตุผลที่ฉันเลือกเป็นคำตอบที่เลือก แต่สำหรับทุกคนที่ใช้ C # และกำลังมองหาวิธีที่รวดเร็วอย่างน้อยที่สุดในการตรวจสอบว่าสตริงหรือไบต์ [] มีข้อมูล Base64 ที่ถูกต้องหรือไม่ฉันพบว่าสิ่งต่อไปนี้ทำงานได้ดีสำหรับฉัน

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

และใช่นี่เป็นเพียงSTRINGของข้อมูล Base64 ไม่ใช่ข้อความRFC1341 ที่มีรูปแบบถูกต้อง ดังนั้นหากคุณกำลังจัดการกับข้อมูลประเภทนี้โปรดคำนึงถึงสิ่งนั้นก่อนที่จะพยายามใช้ RegEx ข้างต้น หากคุณกำลังจัดการกับ Base16, Base32, Radix หรือแม้แต่ Base64 เพื่อวัตถุประสงค์อื่น ๆ (URL, ชื่อไฟล์, การเข้ารหัส XML และอื่น ๆ ) ขอแนะนำให้คุณอ่านRFC4648ที่Gumboกล่าวถึงในคำตอบของเขาเนื่องจากคุณจำเป็นต้องเป็นอย่างดี ตระหนักถึงชุดอักขระและตัวยุติที่ใช้โดยการนำไปใช้ก่อนที่จะพยายามใช้คำแนะนำในชุดคำถาม / คำตอบนี้


ฉันเดาว่าคุณต้องกำหนดงานให้ดีขึ้น ยังไม่ชัดเจนว่าเป้าหมายของคุณคืออะไร: เข้มงวด? แยกวิเคราะห์ตัวอย่าง 100%? ...
ADEpt

ตัวอย่างแรกของคุณควรเป็น 'VGhpcyBpcyBhIHNpbXBsZSBBU0NJSSBCYXNlNjQgZXhhbXBsZSBmb3IgU3RhY2tPdmVyZmxvdy4 ='
jfs

ทำไมไม่ใช้โซลูชันมาตรฐานในภาษาของคุณ เหตุใดคุณจึงต้องใช้โปรแกรมแยกวิเคราะห์ที่เขียนด้วยมือตาม regexs
jfs

1
คำถามที่ดี แม้ว่าฉันพยายามUPDATE regex โดยการทำงานกับเข้ารหัส base64 SHA กลับโดย NPM และมันล้มเหลวขณะ regex ในคำตอบที่เลือกทำงานได้ดี
Josh Habdas

1
ไม่แน่ใจว่าUPDATE regex ยังคงโพสต์โดยไม่มีการแก้ไขได้อย่างไร แต่ดูเหมือนว่าผู้เขียนตั้งใจจะวาง^วงเล็บไว้ด้านนอกเป็นจุดเริ่มต้น อย่างไรก็ตาม regex ที่ดีกว่ามากโดยไม่ต้องซับซ้อนเท่าคำตอบที่ยอมรับจะเป็น^[-A-Za-z0-9+/]*={0,3}$
kael

คำตอบ:


149

จากRFC 4648 :

การเข้ารหัสข้อมูลพื้นฐานถูกนำมาใช้ในหลาย ๆ สถานการณ์เพื่อจัดเก็บหรือถ่ายโอนข้อมูลในสภาพแวดล้อมที่อาจเป็นเพราะเหตุผลเดิมถูก จำกัด ไว้ที่ข้อมูล US-ASCII

ดังนั้นจึงขึ้นอยู่กับวัตถุประสงค์ของการใช้ข้อมูลที่เข้ารหัสว่าข้อมูลควรได้รับการพิจารณาว่าเป็นอันตราย

แต่ถ้าคุณแค่มองหานิพจน์ทั่วไปที่ตรงกับคำที่เข้ารหัส Base64 คุณสามารถใช้สิ่งต่อไปนี้:

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

10
วิธีแก้ไขที่ง่ายที่สุดคือการตัดช่องว่างทั้งหมดออก (ซึ่งจะถูกละเว้นตาม RFC) ก่อนการตรวจสอบความถูกต้อง
Ben Blank

2
กลุ่มสุดท้ายที่ไม่จับภาพสำหรับช่องว่างภายในเป็นทางเลือก
Gumbo

4
ตอนแรกฉันไม่แน่ใจในความซับซ้อน แต่มันก็ตรวจสอบได้ดีทีเดียว ถ้าคุณต้องการจับคู่ base64-ish ฉันจะทำ ^ [a-zA-Z0-9 + /] = {0,3} $ ดีกว่า!
Lodewijk

3
@BogdanNechyporenko นั่นเป็นเพราะnameเป็น Base64 เข้ารหัสที่ถูกต้องของ (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})$ต้องหนีฟันเฟือง
khizar syed

38
^(?:[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
เหตุใดสตริงว่างจึงไม่ถูกต้อง
Josh Lee

8
มันไม่ใช่. แต่ถ้าคุณใช้ regex เพื่อดูว่าสตริงที่ระบุเป็นหรือไม่ใช่ base64 มีโอกาสที่คุณจะไม่สนใจสตริงว่าง อย่างน้อยฉันก็รู้ว่าฉันไม่ใช่
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 แบบง่ายสำหรับ Exmaple ของ StackOverflow


ฉันเห็นด้วย แต่ตัวอักษรอื่น ๆ ทั้งหมดใน URL เป็น base64 ที่ถูกต้อง ... แล้วคุณจะลากเส้นตรงไหน? แค่แบ่งบรรทัด? (ฉันเคยเห็นคนที่มีตัวอักษรสุ่มสองตัวอยู่ตรงกลางบรรทัดไม่สามารถโยนส่วนที่เหลือของบรรทัดได้เพราะเหตุนั้น IMHO) ...
LarryF

@LarryF: เว้นแต่จะมีการตรวจสอบความสมบูรณ์ของข้อมูลที่เข้ารหัสฐาน 64 คุณไม่สามารถบอกได้ว่าจะทำอย่างไรกับบล็อกข้อมูลฐาน 64 ที่มีอักขระไม่ถูกต้อง ฮิวริสติกแบบใดดีที่สุด: ละเว้นอักขระที่ไม่ถูกต้อง (อนุญาตให้ถูกต้องทุกตัว) หรือปฏิเสธบรรทัดหรือปฏิเสธล็อต
Jonathan Leffler

(ต่อ): คำตอบสั้น ๆ คือ "ขึ้นอยู่กับ" - ขึ้นอยู่กับที่มาของข้อมูลและประเภทของความยุ่งเหยิงที่คุณพบในนั้น
Jonathan Leffler

(ดำเนินการต่อ): ฉันเห็นจากความคิดเห็นต่อคำถามที่คุณต้องการยอมรับสิ่งที่อาจเป็นฐาน -64 ดังนั้นเพียงจับคู่อักขระแต่ละตัวที่ไม่ได้อยู่ในตัวอักษรพื้นฐาน -64 ของคุณ (โปรดทราบว่ามีการเข้ารหัส URL ที่ปลอดภัยและการเข้ารหัสรูปแบบอื่น ๆ ) รวมถึงบรรทัดใหม่และเครื่องหมายทวิภาคและนำสิ่งที่เหลือ
Jonathan Leffler

4

regexp ที่ดีที่สุดที่ฉันสามารถหาได้จนถึงตอนนี้อยู่ที่นี่ 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?อาจจะดีกว่าโดยไม่
Jin Kwon

สิ่งนี้จะล้มเหลวในสตริง JSON
idleberg

3

ในการตรวจสอบความถูกต้องของภาพ base64เราสามารถใช้ regex นี้

/ ^ 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+/]*
  • สามารถมีอักขระ padding (=) ที่ส่วนท้ายได้สูงสุดสองตัว - ={0,2}
  • ยอมรับสตริงว่าง

0

คำตอบที่นำเสนอจนถึงตอนนี้ไม่สามารถตรวจสอบได้ว่าสตริง Base64 มีบิตแพดทั้งหมดที่ตั้งค่าเป็น 0 ตามที่กำหนดเพื่อให้เป็นตัวแทนมาตรฐานของ Base64 (ซึ่งมีความสำคัญในบางสภาพแวดล้อมโปรดดูที่https://tools.ietf.org/ html / rfc4648 # section-3.5 ) ดังนั้นจึงอนุญาตให้ใช้นามแฝงที่มีการเข้ารหัสต่างกันสำหรับสตริงไบนารีเดียวกัน นี่อาจเป็นปัญหาด้านความปลอดภัยในบางแอปพลิเคชัน

นี่คือ regexp ที่ตรวจสอบว่าสตริงที่กำหนดไม่ใช่แค่ base64 ที่ถูกต้องเท่านั้น แต่ยังรวมถึงสตริง 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 ) ดังนั้น regex ข้างต้นก็ทำเช่นกัน

นิพจน์ทั่วไปที่เทียบเท่ากันสำหรับ base64url (อีกครั้งอ้างถึง RFC ด้านบน) คือ:

^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-][AQgw]==|[A-Za-z0-9_-]{2}[AEIMQUYcgkosw048]=)?$
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.