JWT คืออะไร ?

JWT ย่อมาจาก JSON Web Token เป็น standard ตัวหนึ่ง ใช้เพื่อส่ง claims ที่เป็น JSON ระหว่างระบบ ใน environment ที่ต้องการ URL-Safe เช่น HTTP Header หรือ Query Param

JWT ประกอบด้วย 3 ส่วน คือ

  1. Header
  2. Payload
  3. Signature

Header ประกอบด้วยประเภทของ Token และ Algorithm ที่ใช้ เช่น

{
"alg": "HS256",
"typ": "JWT"
}

บอกว่า token นี้เป็น JWT และใช้ algorithm HMAC-SHA256 ในการสร้าง signature

Payload คือส่วน claims ที่เป็น JSON เช่น

{
"sub": "acoshift",
"role": "admin"
}

Signature คือลายเซ็นต์​เพื่อบอกว่า header และ payload ถูกต้อง

วิธีการสร้าง signature

  1. เอา header มา encode เป็น base64
$ echo -n '{"alg":"HS256","typ":"JWT"}' | base64
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2. เอา payload มา encode เป็น base64

$ echo -n '{"sub":"acoshift","role":"admin"}' | base64
eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9

3. เอาผลลัพท์จาก 1 กับ 2 มาต่อกัน คั่นด้วย .

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9

4. Sign ข้อความในข้อ 3 ด้วย HMAC-SHA256 ด้วย secret แล้ว encode เป็น base64 เช่น เราใช้ secret เป็น supersecret

$ echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9' | openssl dgst -binary -sha256 -hmac "supersecret" | base64
61FOVw8g1kvrwgx4K15lWwu0/OXMQ1lUSe8Xf3LxMmI=

5. เราสามารถเอา base64 padding ออกได้ หรือไม่เอาออกก็ได้

61FOVw8g1kvrwgx4K15lWwu0/OXMQ1lUSe8Xf3LxMmI

Token เราสามารถสร้างได้โดนการเอา

  1. base64 ของ header
  2. base64 ของ payload
  3. base64 ของ signature

มาต่อกัน โดยใช้ . คั่น จะได้เป็น

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9.61FOVw8g1kvrwgx4K15lWwu0/OXMQ1lUSe8Xf3LxMmI
ลองใช้ jwt.io มา verify token ที่เราสร้าง

Verify JWT

คราวนี้ เมื่อเรามี Token แล้ว เราสามารถ verify ได้ โดย

  1. แบ่ง part ของ token ออกเป็น 3 ส่วน จาก
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9.61FOVw8g1kvrwgx4K15lWwu0/OXMQ1lUSe8Xf3LxMmI

จะได้เป็น

header

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

payload

eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9

signature

61FOVw8g1kvrwgx4K15lWwu0/OXMQ1lUSe8Xf3LxMmI

2. decode header เพื่อดูว่า เป็น token อะไร และใช้ algorithm อะไร

$ echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' | base64 -D
{"alg":"HS256","typ":"JWT"}

3. เอา header กับ payload มาต่อกัน คั่นด้วย . แล้วสร้าง signature จาก secret ที่เรามี เช่นเรามี secret ว่า test123

$ echo -n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY29zaGlmdCIsInJvbGUiOiJhZG1pbiJ9' | openssl dgst -binary -sha256 -hmac "test123" | base64
r30os+lCL9RP0gts136S9WtHQe9tytzVq7emyi3Yz1o=

4. จะเห็นว่า signature ของ token กับที่เราสร้างขึ้นมาไม่ตรงกัน แสดงว่า signature ผิด

ถ้าเราเอา secret ที่ตรงกับที่ใช้สร้าง token เราจะได้ signature ที่ตรงกัน ทำให้ token นี้ valid


สรุป

  1. Payload ใน JWT เป็นแค่ base64 ของ JSON ไม่ได้ encrypt ถึงแม้ว่าจะไม่มี secret ที่ถูกต้อง ก็สามารถ decode payload ออกมาดูได้ ดังนั้น เราจึงไม่ควรเก็บ sensitive data
  2. Secret ใช้เพื่อ verify ว่า token ถูก sign จาก secret เดียวกันไหม ไม่สามารถบอกได้ว่า token หมดอายุหรือยัง เราจึงต้องใส่วันหมดอายุลงใน payload ของ token ด้วย
  3. Token เมื่อถูกสร้างมาแล้วจะไม่สามารถแก้ไข้ได้ นอกจากจะสร้างใหม่ เราจึงควรตั้งให้ token หมดอายุเร็ว ๆ ใช้แค่ครั้งเดียว เช่นให้หมดอายุภายใน 1 นาที ถ้าจะใช้อีกก็ค่อยสร้างใหม่
  4. เราสามารถใช้ RSA เพื่อสร้าง signature แทน HMAC ได้ โดยการใช้ private key เพื่อ sign และใช้ public key เพื่อ verify
อย่าสับสนกับ encryption ที่ใช้ public key เพื่อ encrypt และใช้ private key เพื่อ decrypt

5. ปกติใน JWT จะใช้ base64url encode แบบ no-padding

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.