บทความทั้งหมด

JWT: โครงสร้างโทเคนและความหมายของการ "ถอดรหัส" JWT

JWT (JSON Web Token) เป็นรูปแบบที่กระชับสำหรับส่งข้อมูลที่มีการลงนาม ซึ่งส่วนใหญ่มักเป็นข้อมูลเกี่ยวกับผู้ใช้ที่ผ่านการยืนยันตัวตน โทเค็นประกอบด้วยสามส่วนคั่นด้วยจุด: header.payload.signature ในไทย ระบบ e-wallet, พร้อมเพย์ และแอปธนาคารดิจิทัลจำนวนมากใช้ JWT เป็นกลไกยืนยันตัวตนหลักของ API

ข้อความไทยใน claim: ไม่มีการเว้นวรรค แต่ยาวกว่าที่คิด

payload อย่าง {"name": "สมชาย ใจดี", "city": "เชียงใหม่"} ถูกเข้ารหัสเป็น Base64URL เหมือน JSON ทั่วไป เพราะกระบวนการนี้ทำงานกับไบต์ UTF-8 เท่านั้น ไม่สนใจว่าเป็นภาษาไทยหรือไม่ ในทางปฏิบัติ อักขระไทยแต่ละตัว (รวมสระและวรรณยุกต์ที่ลอยอยู่) ใช้พื้นที่ 3 ไบต์ใน UTF-8 ต่อหนึ่งอักขระ ทำให้โทเค็นที่มีชื่อและที่อยู่เป็นภาษาไทยจริงยาวกว่าโทเค็นที่ใช้ข้อมูลภาษาอังกฤษล้วนอย่างเห็นได้ชัด

สามส่วนของโทเค็น

  • Header — JSON ที่มีประเภทของโทเค็นและอัลกอริทึมลายเซ็น (เช่น HS256 หรือ RS256) เข้ารหัสด้วย Base64URL
  • Payload — JSON ที่มี "claim" ต่าง ๆ: ข้อมูลผู้ใช้ เวลาที่ออก วันหมดอายุ ฯลฯ ก็เข้ารหัสด้วย Base64URL เช่นกัน
  • Signature — ลายเซ็นที่คำนวณจาก header และ payload โดยใช้คีย์ลับหรือคีย์ส่วนตัว เพื่อยืนยันว่าโทเค็นไม่ได้ถูกแก้ไข

Base64URL ไม่ใช่ Base64 ทั่วไป

JWT ใช้ Base64 รูปแบบหนึ่งที่มีชุดตัวอักษรปลอดภัยสำหรับ URL: อักขระ + และ / ถูกแทนที่ด้วย - และ _ และมักจะละเว้นการเติม = ซึ่งทำให้สามารถใส่โทเค็นลงใน URL หรือ header ได้โดยไม่ต้องเข้ารหัสเพิ่มเติม

ความแตกต่างสำคัญ: การถอดรหัส ≠ การตรวจสอบ

header และ payload เป็นเพียง Base64URL เท่านั้น — ใครก็สามารถถอดรหัสและอ่านเนื้อหาได้โดยไม่ต้องใช้คีย์ใด ๆ เนื้อหาของโทเค็นจะเชื่อถือได้ก็ต่อเมื่อได้ตรวจสอบลายเซ็นด้วยคีย์ที่เหมาะสมแล้วเท่านั้น — และการตรวจสอบนี้ทำโดยเซิร์ฟเวอร์ ไม่ใช่โดยไคลเอนต์ที่แค่ดูสิ่งที่อยู่ในโทเค็น

การโจมตีที่อันตราย: การสวมรอยอัลกอริทึมเป็น "none"

สเปกของ JWT อนุญาตให้ใช้อัลกอริทึม none ซึ่งคือโทเค็นที่ไม่มีลายเซ็น หากแบ็กเอนด์เชื่อฟิลด์ alg จาก header ของโทเค็นอย่างไร้เดียงสาแทนที่จะตรวจสอบลายเซ็นด้วยอัลกอริทึมที่กำหนดไว้ล่วงหน้าอย่างแน่นอน ผู้โจมตีก็สามารถสวมรอย alg ให้เป็น none ตัดลายเซ็นทิ้งได้ ไลบรารี JWT ที่เชื่อถือได้จะบังคับให้ระบุอัลกอริทึมที่คาดหวังไว้อย่างชัดเจนตอนตรวจสอบ

เลขบัตรประชาชนไม่ควรอยู่ใน claim

เนื่องจาก header และ payload ถอดรหัสได้โดยไม่ต้องมีคีย์ใด ๆ การใส่เลขบัตรประจำตัวประชาชน 13 หลักลงใน claim โดยตรงจึงหมายความว่าใครก็ตามที่ดักจับโทเค็นได้จะอ่านเลขนั้นเป็นข้อความธรรมดาได้ทันที แม้ลายเซ็นจะถูกต้องสมบูรณ์ก็ตาม ระบบที่ต้องปฏิบัติตาม PDPA ของไทยจึงมักเก็บเพียง user ID ไว้ใน claim แล้วดึงข้อมูลส่วนบุคคลจริงจากฝั่งเซิร์ฟเวอร์แยกต่างหาก

ลองใช้เครื่องมือ