การเข้ารหัส Base64
7 เครื่องมือ
การเข้ารหัสคืออะไร?
การเข้ารหัส (Encoding) คือกระบวนการแปลงข้อมูลจากรูปแบบหนึ่งไปยังอีกรูปแบบหนึ่ง ในการพัฒนาเว็บ การเข้ารหัสถูกใช้เพื่อส่งข้อมูลอย่างปลอดภัยผ่านช่องทางที่ออกแบบมาสำหรับชุดอักขระที่จำกัด — เช่น การส่งข้อมูลไบนารีผ่านโปรโตคอลแบบข้อความ หรือการรวมอักขระพิเศษใน URL
การเข้ารหัสอักขระกำหนดวิธีที่อักขระข้อความแมปกับไบต์ Base64 แปลงข้อมูลไบนารีเป็นข้อความ ASCII การเข้ารหัส URL ทำให้ข้อความใดก็ได้ปลอดภัยสำหรับการใช้งานใน URL การเข้าใจสามชั้นของการเข้ารหัสนี้ช่วยป้องกันข้อผิดพลาดและช่องโหว่ด้านความปลอดภัยที่ตรวจจับได้ยาก
ประวัติการเข้ารหัสอักขระ
การเข้ารหัสอักขระคือการแมประหว่างอักขระ (ตัวอักษร สัญลักษณ์) กับการแทนค่าแบบไบนารี วิวัฒนาการจาก ASCII ไปสู่ Unicode สะท้อนให้เห็นถึงการเป็นสากลของเว็บ:
รหัสมาตรฐานอเมริกัน 7 บิต มี 128 อักขระ ได้แก่ ตัวอักษรอังกฤษ ตัวเลข เครื่องหมายวรรคตอน และรหัสควบคุม ถือเป็นการเข้ารหัสพื้นฐานที่ระบบอื่น ๆ ทั้งหมดต่อยอดจาก
ASCII ที่ขยายสำหรับภาษาในยุโรปตะวันตก เพิ่มอีก 128 อักขระ (ตัวอักษรที่มีเครื่องหมายเน้นเสียง สัญลักษณ์) ไม่เหมาะสำหรับอักษรที่ไม่ใช่ละติน
ชุดอักขระสากลที่ครอบคลุมระบบการเขียนทุกชนิดในโลก กำหนดจุดรหัสสำหรับกว่า 149,000 อักขระใน 161 อักษร ไม่ได้กำหนดการเข้ารหัส — นั่นคือหน้าที่ของ UTF-8/16/32
การเข้ารหัสแบบความกว้างผันแปรสำหรับ Unicode โดยใช้ 1–4 ไบต์ต่ออักขระ รองรับ ASCII (จุดรหัส 128 ตัวแรกใช้ไบต์เดียว) เป็นการเข้ารหัสที่ครองตลาดบนเว็บ — มากกว่า 98% ของเว็บไซต์ทั้งหมด
การเข้ารหัสแบบความกว้างผันแปรโดยใช้ 2 หรือ 4 ไบต์ต่ออักขระ ใช้ภายในระบบ Windows, Java และสตริงของ JavaScript ไม่รองรับ ASCII
การเข้ารหัสแบบความกว้างคงที่: ใช้ 4 ไบต์ต่ออักขระเสมอ เรียบง่ายแต่สิ้นเปลืองพื้นที่ ใช้ในระบบฐานข้อมูลบางตัว แทบไม่พบบนเว็บ
เหตุใด UTF-8 จึงได้รับชัยชนะ
UTF-8 กลายเป็นมาตรฐานที่ครองตลาด เนื่องจากรองรับ ASCII ย้อนหลัง (อักขระ 128 ตัวแรกเข้ารหัสเหมือนกันทุกประการ) ซิงโครไนซ์ตัวเองได้ (สามารถค้นหาขอบเขตของอักขระได้โดยการสแกน) และประหยัดพื้นที่สำหรับข้อความภาษาละติน เอกสาร ASCII ใด ๆ ก็เป็นเอกสาร UTF-8 ที่ถูกต้อง ทำให้การย้ายข้อมูลเป็นไปอย่างราบรื่น
การเข้ารหัส Base64
Base64 แปลงข้อมูลไบนารีเป็นข้อความโดยใช้เพียง 64 อักขระ ASCII ที่พิมพ์ได้: A-Z, a-z, 0-9, + และ / สิ่งนี้จำเป็นเมื่อข้อมูลไบนารีต้องส่งผ่านช่องทางที่รับเฉพาะข้อความ — ไฟล์แนบอีเมล, data URI, JWT token และ HTTP Basic Auth ล้วนใช้ Base64
วิธีการทำงาน
Base64 จัดกลุ่มไบต์ของข้อมูลอินพุตเป็นกลุ่มละ 3 ไบต์ (24 บิต) และเข้ารหัสแต่ละกลุ่มเป็นอักขระ Base64 จำนวน 4 ตัว (6 บิตต่อตัว) หากข้อมูลอินพุตไม่เป็นจำนวนเท่าของ 3 ไบต์ จะมีการเติมอักขระ = เพื่อให้กลุ่มสุดท้ายครบ:
| อินพุต | ไบต์เลขฐานสิบหก | Base64 |
|---|---|---|
| "Man" | 77 61 6E | TWFu |
| "Ma" | 4D 61 | TWE= |
| "M" | 4D | TQ== |
อักขระ = ที่ท้ายบ่งบอกว่ากลุ่ม 3 ไบต์สุดท้ายขาดไบต์ไปกี่ตัว = หนึ่งตัวหมายความว่าต้องเติม 1 ไบต์ และ == หมายความว่าต้องเติม 2 ไบต์ Base64 มาตรฐานจะสร้างผลลัพธ์ที่มีความยาวเป็นจำนวนเท่าของ 4 เสมอ
การเข้ารหัส URL
URL สามารถมีเฉพาะชุดอักขระ ASCII ที่ปลอดภัยในจำนวนจำกัด อักขระใด ๆ ที่อยู่นอกชุดนั้น — รวมถึงช่องว่าง เครื่องหมายวรรคตอน อักขระที่ไม่ใช่ ASCII และอักขระ URL พิเศษ เช่น &, = และ # — ต้องถูกเข้ารหัสแบบเปอร์เซ็นต์ (URL-encoded) ก่อนนำไปใส่ใน URL
การเข้ารหัสแบบเปอร์เซ็นต์จะแทนที่แต่ละไบต์ที่ไม่ปลอดภัยด้วย % ตามด้วยตัวเลขฐานสิบหกสองหลักที่แทนค่าของไบต์นั้น ช่องว่างกลายเป็น %20 เครื่องหมาย & กลายเป็น %26 เป็นต้น
อักขระที่มักถูกเข้ารหัส
| อักขระ | ที่เข้ารหัสแล้ว | หมายเหตุ |
|---|---|---|
| Space | %20 | พบบ่อยที่สุด ใช้ในการส่งแบบฟอร์มในรูป + สำหรับ application/x-www-form-urlencoded |
| & | %26 | ตัวคั่น query string ต้องเข้ารหัสเมื่อใช้เป็นค่าตามตัวอักษร |
| = | %3D | ตัวคั่นคีย์-ค่าใน query string ต้องเข้ารหัสเมื่อใช้เป็นข้อมูล |
| + | %2B | ถูกตีความเป็นช่องว่างใน application/x-www-form-urlencoded ต้องเข้ารหัสเพื่อรักษา + ตามตัวอักษร |
| # | %23 | ตัวระบุ fragment ต้องเข้ารหัสเมื่อใช้เป็นข้อมูลตามตัวอักษรใน path หรือ query |
| / | %2F | ตัวคั่น path segment ต้องเข้ารหัสเมื่อใช้เป็นข้อมูลตามตัวอักษร ไม่ใช่เป็นตัวคั่น path |
| : | %3A | ตัวคั่น scheme ต้องเข้ารหัสใน path และ query |
| @ | %40 | ใช้ใน mailto: และการยืนยันตัวตน ต้องเข้ารหัสเมื่อใช้เป็นข้อมูลตามตัวอักษร |
encodeURI vs encodeURIComponent
JavaScript มีฟังก์ชันเข้ารหัสสองตัวที่มีขอบเขตต่างกัน encodeURI เข้ารหัส URL ทั้งหมด — โดยปล่อยอักขระที่มีความหมายใน URL (:, /, ?, #, @) ไว้ไม่เข้ารหัส ส่วน encodeURIComponent เข้ารหัสองค์ประกอบ URL (ค่าพารามิเตอร์ query เดียว หรือ path segment) — โดยเข้ารหัสอักขระทุกตัวยกเว้น A-Z, a-z, 0-9, -, _, ., ~ ควรใช้ encodeURIComponent สำหรับค่าแต่ละตัว และใช้ encodeURI สำหรับ URL ทั้งหมด
บริบทที่การเข้ารหัสปรากฏในการพัฒนาเว็บ
ส่วนหัว Authorization: Basic เข้ารหัสข้อมูลประจำตัวเป็น Base64(username:password) นี่คือการเข้ารหัสเพื่อความสะดวกในการส่ง ไม่ใช่ความปลอดภัย — Base64 ถอดรหัสได้ทันที ควรใช้ HTTPS ร่วมกับ Basic Auth เสมอ
Data URI ฝังเนื้อหาไฟล์โดยตรงใน HTML หรือ CSS: data:image/png;base64,.... การเข้ารหัส Base64 สำหรับรูปภาพและฟอนต์แบบ inline ช่วยลดการร้องขอ HTTP แลกกับขนาดเอกสารที่เพิ่มขึ้น (~33% overhead)
อีเมลได้รับการออกแบบมาสำหรับ ASCII 7 บิต ไฟล์แนบไบนารี (รูปภาพ, PDF) จะถูก MIME เข้ารหัสด้วย Base64 ก่อนส่ง โปรแกรมอีเมลของคุณถอดรหัสโดยอัตโนมัติเมื่อแสดงผล
JWT token ใช้การเข้ารหัส Base64url (รูปแบบที่แทน + ด้วย - และ / ด้วย _ โดยไม่มี padding) สำหรับทั้งสามส่วน (header, payload, signature) ทำให้ token ปลอดภัยสำหรับ URL โดยไม่ต้องเข้ารหัสแบบเปอร์เซ็นต์เพิ่มเติม
ข้อมูลที่ผู้ใช้ป้อนใน query string ของ URL ต้องถูกเข้ารหัสแบบเปอร์เซ็นต์ การไม่เข้ารหัส & หรือ = ในค่าจะทำให้การแยกวิเคราะห์ query string เสียหายอย่างเงียบ ๆ ควรใช้ encodeURIComponent กับค่าแต่ละตัวเสมอ
ชื่อโดเมนที่ไม่ใช่ ASCII (เช่น münchen.de) ถูกเข้ารหัสด้วย Punycode (คำนำหน้า xn--) เพื่อความเข้ากันได้กับระบบ DNS เบราว์เซอร์แสดงรูปแบบ Unicode แต่ส่งรูปแบบ Punycode ไปยัง DNS resolver
คำถามที่พบบ่อย
ไม่ใช่ Base64 คือการเข้ารหัส (encoding) ไม่ใช่การเข้ารหัสลับ (encryption) เป็นการแปลงแบบกลับได้โดยไม่มีคีย์ลับ ใครก็ตามที่เห็นสตริง Base64 สามารถถอดรหัสได้ทันที ห้ามใช้ Base64 เป็นมาตรการด้านความปลอดภัย
Base64 ประมวลผลข้อมูลอินพุตเป็นกลุ่มละ 3 ไบต์ หากข้อมูลอินพุตไม่เป็นจำนวนเท่าของ 3 ไบต์ จะมีการเติม = เพื่อให้กลุ่มสุดท้ายครบ = หนึ่งตัวหมายถึงเติม 1 ไบต์ และ == หมายถึงเติม 2 ไบต์ บางการติดตั้งไม่ใส่ padding (เช่น Base64url สำหรับ JWT)
Base64url เป็นรูปแบบของ Base64 ที่ปลอดภัยสำหรับ URL โดยแทน + ด้วย - และ / ด้วย _ และมักไม่ใส่ = padding ทำให้ใช้งานได้อย่างปลอดภัยใน URL และ HTTP header โดยไม่ต้องเข้ารหัสแบบเปอร์เซ็นต์ JWT ใช้ Base64url สำหรับทั้งสามส่วน
ใช้ encodeURIComponent สำหรับค่าแต่ละตัว (ค่าพารามิเตอร์ query, ค่า path segment) ใช้ encodeURI สำหรับสตริง URL ทั้งหมดเมื่อต้องการรักษาอักขระโครงสร้าง URL (/, :, ?, #) หากไม่แน่ใจ ให้ใช้ encodeURIComponent
UTF-8 รองรับ ASCII และประหยัดพื้นที่สำหรับข้อความภาษาละติน (URL, แท็ก HTML และโค้ดส่วนใหญ่เป็น ASCII) UTF-16 สิ้นเปลืองพื้นที่สำหรับเนื้อหา ASCII และไม่รองรับย้อนหลัง HTTP และ HTML ใช้ UTF-8 เป็นค่าเริ่มต้น
การเข้ารหัสแบบเปอร์เซ็นต์ (URL encoding) แทนอักขระด้วย % ตามด้วยค่าไบต์เลขฐานสิบหกสองหลัก ตัวอย่างเช่น ช่องว่างคือ %20 (ทศนิยม 32, เลขฐานสิบหก 20) อักขระ UTF-8 หลายไบต์จะเข้ารหัสแต่ละไบต์แยกกัน: é คือ %C3%A9
Base64 เพิ่มขนาดข้อมูลประมาณ 33% และเพิ่มภาระการประมวลผล CPU สำหรับการเข้ารหัสและถอดรหัส สำหรับระบบที่มีปริมาณงานสูง ควรใช้โปรโตคอลไบนารี การเข้ารหัส URL เพิ่มภาระน้อยมาก แต่อาจทำให้ URL ยาวขึ้นอย่างมากเมื่อมีอักขระพิเศษจำนวนมาก
Punycode คือการเข้ารหัสสำหรับแทน Unicode ใน DNS ที่รองรับ ASCII ชื่อโดเมนนานาชาติ เช่น münchen.de ถูกเข้ารหัสเป็น xn--mnchen-3ya.de ใน DNS query เบราว์เซอร์แสดงรูปแบบ Unicode แต่ใช้ Punycode ภายใน