ลงรายละเอียด Base64

เจาะให้ลึกแบบอ่านได้ยันดึกไปกับความสามารถของ Base64 กันดีกว่า

หลายคนเคยสงสัยเรื่อง Base64 กันบ้างไหมว่ามันมีที่มา และกลไกการทำงานอย่างไร ถ้าเคยละก็ ขอแนะนำว่า เนื้อหาต่อจากนี้จะเข้าสู่ MODE ที่ต้องใช้ สติ Concentrate ระดับนึง ขอแนะนำให้ พักสายตา แล้วรวบรวมสมาธิก่อนนะครับบบบ

คำเตือน : เนื้อหาที่นำมาเขียนนี้เป็นส่วนที่ผู้เขียนได้ทำการศึกษาและทำความเข้าใจด้วยตนเอง เพราะเช่นนั้น หากเจอจุดไหนที่ผิดพลาดต้องขออภัยไว้ ณ ที่นี้ และขอน้อมรับการแก้ไขให้ถูกต้องทุกกรณีครับ และก็ TL;DR (Too Long ; Didn’t Read)

หากจะขอให้ทุกคนช่วยนิยามเจ้า Base64 ว่ามันคืออะไร และสามารถนำไปทำอะไรได้ บ้างภายในเวลา 30 วิ เชื่อว่าหลายคนคิดใน “คุณคนเขียนจะบ้าหรอ เนี่ยตั้งใจมาอ่านไง” คนเขียน : พูดเป็นเล่นไป ขอบคุณนะครับ> < 555

Ok anyway, เรามาดูกันก่อนว่า Base64 มันคืออะไร และทำงานยังไงดีกว่า


Base64 คือ standard format ของข้อมูลรูปแบบหนึ่ง โดยรูปแบบข้อมูลจะประกอบไปด้วยตัวอักษรพิมพ์เล็ก พิมพ์ใหญ่ของภาษาอังกฤษ a-z, A-Z ตัวเลข 0–9และ อักขระอีก 2 ตัวคือ + กับ / ทั้งหมด 64 ตัวอักษร ซึ่งหากใครต้องการใช้ standard นี้ ต้องแปลงข้อมูลให้สอดคล้องกับ format ของ Base64 ซึ่งขั้นตอนนี้จะถูกเรียกว่า การ Encoding

หลักการ ENCODING

ทีนี้มาดูในส่วนของการแปลงข้อมูลกันบ้างดีกว่า ซึ่งขั้นตอนนี้จะเรียกว่า ง่ายก็ไม่ง่ายเท่าไหร่ แต่ถ้าเข้าใจละก็ สบม.(สบายมากๆ) เริ่มจากนำข้อมูลต้นทางมาแปลงเป็นรูปแบบ Byte ก่อน จากนั้นก็นำข้อมูลมาเรียง Bit ( 1 Byte = 8 Bits) ตามปกติแล้วนำมา Map กับตารางอักขระของ Base64 โดยใช้จำนวน 6 Bits เพื่อเป็นรหัสอ้างอิงแทนแต่ละอักษรครับ ตามตารางดังนี้



หาก Map แล้วเหลือ เศษ bits ที่หาร 6 ไม่ลงตัว เราจะเติมสิ่งที่เรียกว่า padding ลงไป ซึ่งเราจะเติม bit 0 ต่อท้ายของข้อมูลจนกว่าจะได้ จำนวน bit ที่หาร 6 ลงตัว และ character ที่ต่อท้ายจะใช้ตัวอักษร = เสมอ เพื่อไม่ให้สับสนกับ A (ข้อมูลนั้นเป็น data หรือ padding) เรามาดูตัวอย่างกัน


หากเราจะ Encode คำว่า BASE64 เราจะได้ผลลัพธ์ดังนี้ QkFTRTY0 โดยเริ่มจากการนำ BASE64 มาแปลงเป็นข้อมูล byte ก่อน ซึ่งเราจะพิจารณาทีละตัวอักษร โดย Map แต่ละตัวอักษรกับ ตาราง ASCII ก่อนเลย (1 ตัวอักษรใน computer จะแทนด้วยรหัส 0,1 ซึ่งโดยทั่วไปจะใช้จำนวน 8 ตัว แทนแต่ละตัวอักษร โดยรหัส 0,1 จำนวน 1 ตัว เรียกว่า 1 bit และ 8 bits จะเรียกว่า 1 Byte ฉะนั้น 1 ตัวอักษร เท่ากับ 8 bits หรือ 1 Byte)

B          A          S          E         6          4
0100 0010  0100 0001  0101 0011  0100 0101 0011 0110  0011 0100

ทีนี้มาแบ่งกลุ่ม bit เป็นทีละ 6 bits กัน

B          A          S          E         6          4
0100 0010  0100 0001  0101 0011  0100 0101 0011 0110  0011 0100

เมื่อเรียงต่อกันจะได้เป็นแบบนี้
010000100100000101010011010001010011011000110100

แบ่งกลุ่ม Bit ใหม่ กลุ่มละ 6 bits เพ่ือให้สามารถ Map กับ ตาราง Base64 ได้
010000 100100 000101 010011 010001 010011 011000 110100

เมื่อเทียบกลุ่ม Bit กับ ตาราง Base64 จะได้ว่า
Q      k      F      T      R      T      Y      0
010000 100100 000101 010011 010001 010011 011000 110100

และเมื่อนำมา Map กับ Table Base64 จะได้ Result = 'QkFTRTY0'

ขอเสริมอีกซักตัวอย่างนึง หากเราจะ Encode คำว่า Hello เราจะได้ผลลัพธ์ดังนี้

SGVsbG8= โดยหากเรามาพิจารณาจะได้ว่า

H          e          l          l          o        
0100 1000  0110 0101  0110 1100  0110 1100  0110 1111
แบ่งกลุ่ม Bit ใหม่ กลุ่มละ 6 bits
010010 000110 010101 101100 011011 000110 1111

จะเห็นว่า Bit กลุ่มสุดท้ายมี ไม่ครบ 6 ฉะนั้นเราต้องเติมสิ่งที่เรียกว่า Padding ลงไปเพื่อให้เราสามารถทำการ Decode ได้ง่ายขึ้น หลักการเติม Padding นั้นจะเป็นไปตามสัดส่วนดังนี้ 3 Bytes : 4 Base64 characters แปลว่า

ผลลัพธ์จากการ Encoding จะมี Length เป็น จำนวนที่หาร 4 ลงตัว หากได้ไม่ลงตัวจะต้องเติมสิ่งที่เรียกว่า Padding ลงไป
H          e          l          l          o        
0100 1000  0110 0101  0110 1100  0110 1100  0110 1111
แบ่งกลุ่ม Bit ใหม่ กลุ่มละ 6 bits
010010 000110 010101 101100 011011 000110 1111
สำหรับกรณีที่ผลลัพธ์ Encode จำนวนต้องเป็นจำนวนที่หาร 4 ลงตัว
เมื่อนับแล้วพบว่าตัวสุดท้ายหาร 4 ไม่ลงตัว
1      2      3      4      1      2      3      
ฉะนั้นจำเป็นต้องเติม Padding ลงไปเพื่อให้หาร 4 ลงตัว
โดยจะเป็นการเติม bit 0 ต่อไปเรื่อยๆ จนกว่าจะได้จำนวนที่หาร 4 ลงตัว
010010 000110 010101 101100 011011 000110 111100 000000
เมื่อเทียบกับตาราง Base64 จะมีค่าเท่ากับ
S      G      V      s      b      G      8      =
010010 000110 010101 101100 011011 000110 111100 000000

จำนวนตัวอักษรที่ได้จากการ Encode จะมีขนาดไม่เกิน

4*(n/3)

ปล. n คือ จำนวน Bytes และมีเศษให้ปัดขึ้นนะครับ


วิธี Proof

​Encode A​มีค่าเท่ากับ QQ==

Encode AB​มีค่าเท่ากับ QUI=

​Encode ABC ​มีค่าเท่ากับ QUJD

​Encode ABCD​มีค่าเท่ากับ QUJDRA==

Encode ABCDE​มีค่าเท่ากับ QUJDREU=

Encode ABCDEF​มีค่าเท่ากับ QUJDREVG

………

Encode ABCDEFGHIJKLMNOPQRSTUVWXYZ​มีค่าเท่ากับ QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=


เมื่อพิจารณาดู

พบว่า 1 ตัวอักษร encode ได้ 4 Base64

พบว่า 2 ตัวอักษร encode ได้ 4 Base64

พบว่า 3 ตัวอักษร encode ได้ 4 Base64

พบว่า 4 ตัวอักษร encode ได้ 8 Base64

พบว่า 5 ตัวอักษร encode ได้ 8 Base64

พบว่า 6 ตัวอักษร encode ได้ 8 Base64

ให้ n แทนตัวอักษร

n=1, 4*(n/3) = 4

n=2, 4*(n/3) = 4

n=3, 4*(n/3) = 4

n=4, 4*(n/3) = 8

n=5, 4*(n/3) = 8

n=6, 4*(n/3) = 8

n=26, 4*(n/3) = 36


∴ หลักการ Encode ข้อมูลของ Base64 จะได้ขนาดข้อมูลเท่ากับ 4*(n/3) โดยที่ n เป็นขนาดของ Byte เสมอ

หลักการ Decoding

เมื่อเรา Encode แล้ว เราก็มาทำความเข้าใจหลักการ decoding กัน สำหรับวิธีการแปลงข้อมูลกลับ เราจะทำคล้ายๆ Encoding แต่เป็นการทำย้อนกระบวนตั้งแต่ขั้นตอนสุดท้าย ย้อนไปถึงขั้นตอนแรก หรือง่ายๆคือ ย้อนแย้ง 😁 มาดูวิธีการกันเลย


ก่อนอื่นเอาค่าที่ encoding นั้น ขอใช้เป็น QkFTRTY0 ซึ่งจะตรงกับข้อมูลตัวอย่าง มา

1. Map กับ ตาราง Base64 Character แปลงค่าจาก เลขฐาน 64 เป็น เลขฐาน 2

2. แล้วทำการแปลงเป็นตัว character อีกที ด้วยการนับ เลขฐาน 2 จำนวน 8 bits

เพียงเท่านี้เราก็จะได้ข้อมูลที่ถอดรหัสจากการ encode เรียบร้อย แล้ว

ในทำนองเดียวกัน หากมี Padding ให้ตัด Padding ออกก่อน ก็จะได้ Bit ที่หาร 8 ลงตัวแล้ว Map ค่าย้อนกลับได้ตามปกติ


Highlight

แต่ในบางครั้งการ encode ข้อมูลทำให้ไม่สามารถนำข้อมูลที่ ​Encode แล้วไปใช้งานได้ เพราะ break rule หรือ violate convention ของ context ที่ใช้อยู่หรือพูดง่ายคือ อักขระ encoding บางตัว ไม่สามารถใช้งานกับสภาพแวดล้อมของสิ่งนั้นได้ เช่น การแปลงเป็น Base64 เพื่อนำไปเป็น parameter ใน url หากสังเกตดีๆ จะเห็นว่าตัวอักขระ / ใน Base64 จะเป็นปัญหาในทันที หากเอามาใช้งาน เพราะ จะถูกมองว่าเป็น Path delimiter ของ URL ดังนั้นเพื่อให้เหมาะสมกับการใช้งาน จะมี Encoder Decoder บางตัวที่มีการปรับ format ของข้อมูลไปเพื่อให้เหมาะสม เช่น Base64URLEncoder, Base64 encoding for IMAP, Nmtoken เป็นต้น


ประโยชน์ จากการทำ Base64 Encoding ที่เห็นได้ชัด

คือการ Encode ข้อมูลตรงๆ เพื่อป้องกันไม่ให้เห็นข้อมูล Plain ๆ แต่การทำแค่ Encode Base64 ไม่ปลอดภัยเพราะ หากใครรู้หลักการ Decode ก็จะสามารถอ่านข้อมูลได้ทันที ฉะนั้นก็อาจจะมีคนประสงค์ร้ายที่จะใช้รูโหว่ ของ Base64 เพื่อทำการ Hack ข้อมูล จึง อยากจะขอเตือนให้ทุกคนระมัดระวังการใช้งานด้วย หากจะใช้งานควรนำ Security Approach มา Apply ร่วมด้วย เพื่อลดความเสี่ยงความเสียหาย เรื่องการโจรกรรมข้อมูลได้


การประยุกต์ที่เคยเห็น

เคยได้ยินเรื่องการใช้ Image บน Web ที่เป็นแบบ Base64 กันไหมครับ ซึ่ง Base64 Image คือ sixty-fournary image โดยเป็นการนำมาแทนการ Download source รูป จากสมัยก่อนจะมีการ request ขอไฟล์รูปมาที่ web และมีการ Download resource อื่นๆเช่น css, javascript, รูปภาพ, เสียง, วิดีโอ, ฟอนต์ ฯลฯ ต่างๆนานา มากมายด้วยเช่นกัน หาก Webpage ของเราต้องโหลดรูป 10 ไฟล์ในหน้า Client ก็ต้องทำการเปิด Request มาขอรูปภาพ 10 รอบ ซึ่งจะเห็นว่า มันมี overhead ตอนเปิด Request มาขอรูป ตอนแจ้ง client ว่ารูปโหลดและเรนเดอร์เสร็จแล้ว และอื่นๆ...... แต่หากเราเปลี่ยนรูปเป็น Base64 ละก็ Client ก็ไม่จำเป็นต้องโหลด มากมายขนาดนั้น ซึ่งจะช่วยให้ประหยัดทรัพยากรอย่างมากมาย เพราะตัดพวก Holding Request ออกไปได้ แต่ก็ต้องคำนึกถึงขนาดของรูปภาพก่อนด้วย หากมีขนาดใหญ่เกินไป ก็จะไม่เหมาะสม เพราะ client จะต้องดาวน์โหลด Content ที่ใหญ่กว่าปกติ ซึ่งมีความไม่เหมาะสมกับการใช้งาน


แต่แล้ว ความสงสัยที่มี ก็ยังไม่หมดไปเคยสงสัยมะว่า Base64 Image เนี่ยเอาไปทำงานยังไงต่อ 555 เกริ่นมาตั้งนาน มาเข้าประเด็นคอลัมน์ นี้กันดีกว่า อันนี้จุดเริ่มต้นของความสงสัยที่แท้จริง บอกเลยว่า มันเป็นสิ่งที่อยากรู้โดย ความรู้สึกมันถูกซ่อนอยู่มานาน ถึงวันที่ต้องดับระเบิดความสงสัยกันซักที Base64 ที่ใช้แทนไฟล์รูป บน HTML นั้นมีที่มาอย่างไร


สรุป Base64 คือ เป็น Format การแทนค่าข้อมูลคล้ายกับ Binary, Hexadecimal Number เพียงแต่ขยาย จากตัวแทนรหัส 2 ตัว, 16 ตัว มาเป็น 64 ตัว เพื่อให้เพิ่มขนาดในการแทนค่า และประหยัดปริมาณข้อมูลในการแปลงความหมาย

ขอบคุณทุกๆคน ที่อ่านมาจนถึงบรรทัดนี้ครับ ปรบมือให้กับตัวเองกันหน่อย พวกคุณ เก่งมาก มาถึงตรงนี้ Blog จบละครับ บายยยยยย


0
124