JWT คืออะไรและวิธีถอดรหัสอย่างปลอดภัย
JSON Web Token ปรากฏทุกที่ในการพัฒนาเว็บสมัยใหม่ — ส่วนหัวการ authenticate, OAuth flow, API key, การจัดการ session หากคุณเคยทำงานกับ API ใดที่ต้องการ Bearer token, คุณได้ใช้ JWT แล้ว แต่นักพัฒนาส่วนใหญ่ถือพวกมันเป็น blob ทึบและไม่ค่อยดูข้างใน การเข้าใจโครงสร้างของ JWT, สิ่งที่แต่ละส่วนหมายถึง และวิธีถอดรหัสตัวหนึ่งโดยไม่ทำผิดพลาดด้านความปลอดภัยใช้เวลาประมาณสิบห้านาที คู่มือนี้ครอบคลุมทั้งหมด
กายวิภาคของ JWT
JWT ทุกตัวเป็นสตริงของสามส่วนที่เข้ารหัส Base64URL คั่นด้วยจุด:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzQ3MDAwMDAwLCJpYXQiOjE3NDY5OTY0MDAsInNjb3BlIjoicmVhZDp1c2VycyJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c รูปแบบคือ header.payload.signature แต่ละส่วนเข้ารหัสข้อมูลต่างกัน
Header
ส่วนแรกคือ header ถอดรหัสแล้ว ตัวอย่างด้านบนกลายเป็น:
{
"alg": "RS256",
"typ": "JWT"
} alg ระบุอัลกอริทึมที่ใช้เซ็น token — RS256 หมายถึง RSA กับ SHA-256 typ ระบุประเภท token ทั้งสองฟิลด์นี้เป็นขั้นต่ำ token บางตัวยังรวม kid (key ID) เพื่อบอกผู้ตรวจสอบว่าจะใช้ public key ใดเมื่อหลายคีย์อยู่ในการหมุนเวียน
Payload
ส่วนที่สองคือ payload — ข้อมูลจริง ถอดรหัสแล้ว:
{
"sub": "user_123",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1747000000,
"iat": 1746996400,
"scope": "read:users"
} Payload มี claim: คำกล่าวเกี่ยวกับ entity ที่ token แทน Claim บางตัวเป็นมาตรฐาน (เรียกว่า registered claim); ตัวอื่นเฉพาะแอปพลิเคชัน (เรียกว่า private claim) Payload ถูก Base64URL-encode ไม่ใช่เข้ารหัสลับ ใครก็ตามที่ได้สตริง token สามารถอ่าน claim ทุกตัวข้างใน
Signature
ส่วนที่สามคือ signature มันผลิตโดยการรับ header และ payload ที่เข้ารหัส, เชื่อมด้วยจุด แล้วเซ็นผลด้วย secret key หรือ private key ที่ระบุใน alg:
signature = sign(
base64url(header) + "." + base64url(payload),
secretOrPrivateKey
) Signature ให้ฝ่ายใดที่มี public key ที่สอดคล้อง (หรือ secret key สำหรับอัลกอริทึม symmetric) ตรวจสอบว่า token ออกโดยแหล่งที่ไว้ใจและทั้ง header และ payload ไม่ถูกแก้ไขหลังการออก ตัวอักษรเดียวที่เปลี่ยนใน payload ทำให้ signature ไม่ถูกต้องทั้งหมด
วิธีถอดรหัส JWT
การถอดรหัสต่างจากการตรวจสอบ การถอดรหัสเพียงอ่านข้อมูลภายใน token การตรวจสอบยืนยันว่า token เป็นของแท้และไม่หมดอายุ คุณควรตรวจสอบในโค้ด production เสมอ; การถอดรหัสลำพังมีประโยชน์สำหรับการดีบัก
ขั้น 1: แยก token
แยกสตริง JWT ที่ตัวอักษร . คุณจะได้สามส่วน หากได้น้อยกว่าสาม, token ผิดรูป
ขั้น 2: ถอดรหัส header และ payload
Base64URL ถอดรหัสแต่ละส่วนสองส่วนแรก Base64URL คล้าย Base64 มาตรฐานแต่ใช้ - แทน + และ _ แทน / โดยไม่มีตัวอักษร padding เมื่อถอดรหัส คุณสามารถ parse แต่ละส่วนเป็น JSON
ขั้น 3: Parse และตรวจสอบ
อ่าน claim เช็ค exp (หมดอายุ) กับ Unix timestamp ปัจจุบัน ดู iss และ aud เพื่อยืนยันว่า token ตั้งใจสำหรับแอปพลิเคชันของคุณ อย่าไว้ใจ claim ใดจนกว่าตรวจสอบ signature
สำหรับการตรวจสอบด่วนระหว่างการพัฒนา, ใช้ JWT Decoder มันทำงานในเบราว์เซอร์ของคุณทั้งหมด — token ของคุณไม่เคยออกจากอุปกรณ์ คุณยังสามารถถอดรหัสส่วน base64 ด้วยมือด้วย Base64 encoder/decoder หากต้องการเห็น byte ดิบ สำหรับการตรวจสอบ signature ในสคริปต์ทดสอบ, HMAC generator ให้คุณสร้าง signature HS256 ใหม่โดยไม่เขียนโค้ด
ขั้น 4: ตรวจสอบ signature ในโค้ด
ใน production ตรวจสอบ signature เสมอโดยใช้ library ที่สร้างสำหรับภาษาของคุณ อย่านำการตรวจสอบ signature ไปใช้ด้วยมือ ตัวเลือกยอดนิยมรวม jose หรือ jsonwebtoken สำหรับ Node.js, PyJWT สำหรับ Python, golang-jwt/jwt สำหรับ Go และ nimbus-jose-jwt สำหรับ Java
ส่งอัลกอริทึมโดยชัดเจนเสมอ อย่าให้ library อนุมานอัลกอริทึมจาก header ของ token
Claim มาตรฐาน (Registered Claim)
RFC 7519 นิยามชุดชื่อ claim ลงทะเบียน เหล่านี้ไม่บังคับ แต่เมื่อมีต้องตามความหมายมาตรฐาน
iss — ผู้ออก
Entity ที่สร้างและเซ็น token ปกติเป็น URL ที่ระบุ authorization server (เช่น https://auth.example.com) ผู้ตรวจสอบควรเช็คว่า iss ตรงกับค่าที่คาดหวังและปฏิเสธ token จากผู้ออกที่ไม่รู้จัก
sub — Subject
Principal ที่ token แทน — ปกติเป็น user ID, ชื่อบัญชีบริการ หรือตัวระบุอุปกรณ์ ค่าต้องเฉพาะภายในบริบทของผู้ออก แอปพลิเคชันของคุณใช้ sub ระบุว่า request เป็นของผู้ใช้ใด
aud — Audience
ผู้รับที่ตั้งใจ(หลายตัว)ของ token หากตัวระบุ API ของคุณคือ https://api.example.com, token ที่ออกสำหรับบริการอื่นควรถูกปฏิเสธ การล้มเหลวในการตรวจสอบ aud ให้ผู้โจมตีใช้ซ้ำ token ที่ได้จากบริการหนึ่งที่บริการต่าง
exp — เวลาหมดอายุ
Unix timestamp หลังจากที่ token ห้ามถูกยอมรับ ตรวจสอบ exp เสมอ token ที่ไม่มี expiration จริงๆ มีชีวิตตลอดไป — หากถูกขโมย ผู้โจมตีมีการเข้าถึงไม่จำกัด
iat — ออกเมื่อ
Unix timestamp เมื่อ token ถูกสร้าง มีประโยชน์สำหรับการตรวจจับ token ที่ทางเทคนิคไม่หมดอายุแต่เก่าผิดสังเกต คุณสามารถใช้ iat บังคับอายุ token สูงสุดอิสระจาก exp
nbf — ไม่ก่อน
Unix timestamp ก่อนที่ token ห้ามถูกยอมรับ พบน้อยกว่า exp แต่มีประโยชน์เมื่อออก token ล่วงหน้า — เช่น งานที่ตั้งเวลาที่ไม่ควรเริ่มจนกว่าเวลาเฉพาะ
jti — JWT ID
ตัวระบุเฉพาะสำหรับ token อนุญาตผู้ออกป้องกันการโจมตี token replay โดยเก็บค่า jti ที่ใช้แล้วและปฏิเสธ token ที่มี ID ซ้ำ จำเป็นเมื่อต้องการ token ใช้ครั้งเดียว เช่นลิงก์รีเซ็ตรหัสผ่าน
กับดักความปลอดภัย
การนำ JWT ไปใช้มีประวัติของจุดอ่อนละเอียด เหล่านี้คือสิ่งที่สำคัญที่สุดที่ต้องเข้าใจก่อนส่งโค้ดที่ยอมรับ JWT
การโจมตี "alg: none"
JWT library บางตัวจากยุคแรกของมาตรฐานจะยอมรับ token ที่ระบุ "alg": "none" ใน header ของมัน ผู้โจมตีสามารถรับ token ที่ถูกต้องใด, แทนอัลกอริทึมด้วย none, ถอด signature และ library จะยอมรับเป็นถูกต้องเต็ม — ไม่ต้องการ signature
การแก้ไข: ระบุอัลกอริทึมที่คาดหวังโดยชัดเจนเสมอเมื่อเรียก function การตรวจสอบของคุณ ถือ token ใดที่อ้าง alg: none เป็นไม่ถูกต้อง Library สมัยใหม่ส่วนใหญ่แก้แล้ว แต่ควรค่าแก่การยืนยันค่าเริ่มต้นของ library ก่อนไป production
ความสับสนอัลกอริทึม (Downgrade RS256 เป็น HS256)
Library บางตัวที่รองรับทั้งอัลกอริทึมจะใช้ฟิลด์ alg ใน header ของ token ตัดสินใจวิธีตรวจสอบ ผู้โจมตีสามารถเปลี่ยนอัลกอริทึมเป็น HS256 จากนั้นเซ็น token โดยใช้ public key ของเซิร์ฟเวอร์เป็น HMAC secret เซิร์ฟเวอร์เห็น HS256 จะตรวจสอบกับ public key และยอมรับ token ปลอม
การแก้ไขเหมือนกัน: ล็อกอัลกอริทึมในโค้ดการตรวจสอบของคุณ อย่าให้ header ของ token มีอิทธิพลต่ออัลกอริทึมใดที่ใช้สำหรับการตรวจสอบ
การยอมรับ Token หมดอายุ
การไม่ตรวจสอบ exp เป็นการมองข้ามที่พบบ่อย — บางครั้งแนะนำเมื่อนักพัฒนาเพิ่มช่วงเวลาผ่อนผันที่โตโดยไม่มีขอบเขต หรือเมื่อเส้นทางโค้ดการตรวจสอบถูกข้ามในสาขาโค้ด ถือ token ที่หมดอายุเหมือนกับ token ที่ขาด: ปฏิเสธด้วย 401 และต้องการให้ client re-authenticate หรือใช้ refresh token
ข้อมูลที่ละเอียดอ่อนใน Payload
Payload ถูก Base64URL-encode ไม่ใช่เข้ารหัสลับ ใครก็ตามที่ intercept token สามารถถอดรหัส อย่าใส่รหัสผ่าน, หมายเลขบัตรเครดิต, หมายเลข social security หรือข้อมูลที่ละเอียดอ่อนอื่นใน payload หากต้องส่ง claim ที่ละเอียดอ่อนอย่างปลอดภัย ใช้ token JWE (JSON Web Encryption) ซึ่งเข้ารหัสลับ payload JWT ที่เซ็นด้วย JWS (กรณีทั่วไป) รับประกันเพียงความแท้ ไม่ใช่ความลับ
การขาดการตรวจสอบ Audience
การข้ามการตรวจสอบ aud หมายถึง token ที่ออกสำหรับบริการ A สามารถถูก replay ที่บริการ B — ตราบใดที่ทั้งสองบริการแชร์คีย์เซ็นเดียวกันหรือไว้ใจผู้ออกเดียวกัน ในสถาปัตยกรรมหลายบริการ ตรวจสอบเสมอว่า audience ของ token ตรงกับตัวระบุของบริการคุณ
แนวปฏิบัติดีสำหรับ 2026
เลือก RS256 สำหรับการใช้งานส่วนใหญ่
RS256 ใช้ private key เซ็นและ public key ตรวจสอบ เฉพาะ authorization server ถือ private key บริการใดสามารถตรวจสอบ token โดยใช้ public key ซึ่งสามารถเผยแพร่อย่างเปิด (มักผ่าน JWKS endpoint) หากบริการรายบุคคลใดถูก compromise, ผู้โจมตีได้ความสามารถในการตรวจสอบ token — แต่ไม่ใช่การปลอม token ใหม่
HS256 ใช้ secret ที่แชร์เดียวสำหรับทั้งการเซ็นและการตรวจสอบ บริการใดที่สามารถตรวจสอบ token ก็สามารถสร้างได้ ใน setup microservice การแชร์ secret ข้ามบริการหลายตัวเพิ่มรัศมีระเบิดของการ breach
เก็บ Access Token อายุสั้น
ไม่มีกลไกการยกเลิกในตัวสำหรับ JWT — เมื่อออก, token ถูกต้องจนกว่าหมดอายุ (เว้นแต่คุณนำ blocklist ไปใช้ ซึ่งแนะนำสถานะฝั่งเซิร์ฟเวอร์อีก) เวลาหมดอายุสั้น (15 นาทีถึง 1 ชั่วโมง) จำกัดหน้าต่างความเสียหายหาก token ถูกขโมย จับคู่ access token กับ refresh token อายุยาวเก็บใน HttpOnly cookie ที่ปลอดภัย ไม่ใช่ใน localStorage
ใช้การหมุน Refresh Token
เมื่อ client ใช้ refresh token เพื่อได้ access token ใหม่ ออก refresh token ใหม่และทำให้ตัวเก่าไม่ถูกต้อง วิธีนี้ refresh token ที่ถูกขโมยถูกตรวจจับครั้งถัดไปที่ client ที่ถูกต้องพยายามใช้ตัวเดิม: เซิร์ฟเวอร์เห็น refresh token ที่ใช้ซ้ำและสามารถยกเลิก session ทั้งหมด รูปแบบนี้บันทึกใน RFC 6819 และรองรับกว้างโดย authorization server สมัยใหม่
เผยแพร่ JWKS Endpoint
JSON Web Key Set (JWKS) endpoint (/.well-known/jwks.json โดยธรรมเนียม) เผยแพร่ public key เซ็นของคุณใน format มาตรฐาน บริการอื่นสามารถ fetch JWKS และตรวจสอบ token โดยไม่ได้รับคีย์นอก band สิ่งนี้ยังทำให้การหมุนคีย์ตรงไปตรงมา: เพิ่มคีย์ใหม่ใน JWKS, เริ่มเซ็นด้วยมัน จากนั้นเอาคีย์เก่าออกเมื่อ token ที่มีอยู่ทั้งหมดหมดอายุ
หมุนคีย์เป็นประจำ
แม้หาก private key ของคุณไม่เคยถูก compromise, การหมุนคีย์เป็นประจำจำกัดหน้าต่างที่คีย์ที่ถูกขโมยอาจใช้ปลอม token ใช้ kid ใน header JWT อ้างคีย์เซ็น ดังนั้นผู้ตรวจสอบสามารถเลือก public key ที่ถูกจาก JWKS โดยไม่ทำลาย token ที่มีอยู่ระหว่างการหมุน
ตรวจสอบทุก Claim
เช็ค iss, aud, exp และ nbf ในทุก request อย่าข้ามใดของเหล่านี้เพราะดูซ้ำซ้อนใน setup ของคุณ — เงื่อนไขที่ทำให้ดูไม่จำเป็นวันนี้คือเงื่อนไขที่เปลี่ยนระหว่างเหตุการณ์
JWT vs. Session Cookie
JWT และ session ฝั่งเซิร์ฟเวอร์แก้ปัญหาเดียวกัน — การคงสถานะการ authenticate ข้าม HTTP ไร้สถานะ — แต่ด้วยการแลกเปลี่ยนต่างกัน
JWT เป็น self-contained เซิร์ฟเวอร์ไม่ต้อง query ฐานข้อมูลเพื่อ validate request สิ่งนี้ทำให้น่าสนใจสำหรับระบบกระจายและ API ที่บริโภคโดย client มือถือหรือบุคคลที่สาม ข้อเสียคือการยกเลิกต้องการ blocklist (ซึ่งแนะนำสถานะฝั่งเซิร์ฟเวอร์อีก) หรือทนต่ออายุที่เหลือของ token ที่ถูก compromise
Session cookie ถูกตรวจสอบฝั่งเซิร์ฟเวอร์ เซิร์ฟเวอร์เก็บสถานะ session และสามารถยกเลิกการเข้าถึงทันทีโดยลบ session Cookie ที่มีแฟล็ก HttpOnly และ Secure ถูกป้องกันจากการเข้าถึง JavaScript และการ intercept เครือข่าย ข้อเสียคือทุก request ต้องการการ lookup ฐานข้อมูล ซึ่งอาจกลายเป็นคอขวดที่ระดับใหญ่
สำหรับแอปพลิเคชันเว็บดั้งเดิมที่มีหน้า render ฝั่งเซิร์ฟเวอร์และ backend เดียว session cookie ยังเป็นตัวเลือกที่แข็งแกร่งและง่ายกว่า สำหรับ API ที่บริโภคโดยหลาย client, สถาปัตยกรรม microservice และแอปมือถือ, JWT พร้อมหมดอายุสั้นและการหมุน refresh token เป็นตัวเลือกปฏิบัติมากกว่า
ถอดรหัส Token ของคุณ
วิธีที่เร็วที่สุดในการตรวจสอบ JWT คือ paste เข้า JWT Decoder มันแยก token, ถอดรหัส header และ payload และ render claim ใน format อ่านได้ — ทั้งหมดโดยไม่ส่ง token ไปที่ใด หากต้องเข้ารหัสหรือถอดรหัสสตริง Base64URL ดิบด้วยมือ, Base64 encoder/decoder จัดการทั้งแบบมาตรฐานและ URL-safe สำหรับการตรวจสอบ HMAC signature ในสคริปต์ทดสอบ, HMAC generator ให้คุณสร้าง signature HS256 ใหม่และเปรียบเทียบกับสิ่งที่ token ของคุณบรรจุ
สำหรับข้อกำหนดเต็ม ดู RFC 7519 (JWT) และตัวอย่างเชิงโต้ตอบที่ jwt.io