ทำความรู้จักกับ JWT

Narupon Tee Srisantitham
Konoe
Published in
2 min readJan 9, 2017

การทำเว็บขึ้นมาเว็บหนึ่งปัญหาหลักอย่างหนึ่งที่ผู้พัฒนาเว็บอย่างเราให้ความสนใจก็คือเรื่องของความปลอดภัยของระบบ การเข้าถึงข้อมูลของกลุ่มผู้ใช้งาน(user) ผู้ใช้งานใดบ้างที่จะมีสิทธิ์ในการเข้าถึงข้อมูลในแต่ละหน้า การตรวจสอบผู้ใช้งานจึงเป็นเรื่องสำคัญ บทความนี้ผมจึงขอแนะนำการใช้งาน JWT ตามที่ผมได้ทดลองใช้งานมาดูนะครับ

JWT ย่อมาจาก Json Web Token เค้าอ่านกันว่า jot (ตามที่ scotch ได้บอกไว้นะครับ ปกติผมเรียก เจ-ดับเบิ้ลยู-ที) อย่างที่รู้ ๆ กันอยู่แล้วว่าการ Authenticate นั้นจะอาศัยการทำงานระหว่าง client กับ server หรือก็คือ cookie กับ session เพื่อทำการเก็บข้อมูลของผู้ใช้งานที่กำลังใช้งานเว็บอยู่ขณะนั้นแล้วมันก็มีความปลอยภัยดีอยู่แล้ว ก็มีคำถามขึ้นมาแล้วเจ้า JWT จะเอามาทำอะไรคำตอบก็คือเว็บ Single Page Web Application (SPA) ไงครับหรือการทำ Web API ก็สามารถใช้งานได้ดีเช่นกันครับ

ข้อดีของ JWT
– เป็น JSON มันจึงสามารถใช้งานกับภาษาโปรแกรมได้หลายภาษา
– เก็บทุกอย่างไว้ในตัวของมันเองแม้กระทั้งข้อมูลทั่วไปของผู้ใช้งาน (ต้องเป็นข้อมูลที่เปิดเผยได้นะครับเพื่อความปลอดภัย)
– ง่ายต่อการส่งไปส่งมา เพราะว่ามันเป็น JSON และก็เก็บทุกอย่างที่จำเป็นไว้กับตัวเองจึงทำให้มันง่ายต่อการส่ง ไม่ว่าจะส่งไปกับ url, ajax, header หรืออะไรก็แล้วแต่ที่สามารถส่ง string ได้

ส่วนประกอบของ JWT ประกอบด้วย 3 ส่วนหลัก ๆ แต่ละส่วนจะถูกขั้นด้วยจุด(.) ซึ่งหน้าตาของมันจะเป็นประมาณนี้

aaaaaaaaaa.bbbbbbbbbbbb.ccccccccccc

ส่วนแรกคือ Header เป็นส่วนที่บอกรายละเอียดของ JWT ตัวนี้ครับโดยที่มันจะประกอบด้วย 2ส่วน
– ชนิดของข้อมูลชุดนี้ ในที่นี้คือ “JWT”
– วิธีการเข้ารหัส ในที่นี้คือ “HMAC SHA 256”


var header = {
‘typ’ : ‘JWT’,
‘alg’ : ‘HS256’
};

ส่วนที่สองคือ Payload หรือที่เรียกกันว่า JWT Claim คือส่วนที่ทำการเก็บข้อมูลต่าง ๆ ที่เราต้องการส่งไปใน Token ครับ เช่นข้อมูลเบื้องต้นของผู้ใช้งาน สถานะต่าง ๆ หรือว่าอะไรก็ได้ที่เราต้องการจะเก็บครับ โดยการเก็บจะเป็นข้อมูลในรูปของ JSON Object ครับ

ซึ่งข้อมูลที่จะเก็บก็แบ่งได้เป็น 3 ส่วนครับคือ Registered Claim, Public Claim, Private Claim ตัว Registered Claim จะมีมาตรฐานดังนี้ (แต่ก็ไม่ได้บังคับว่าจะต้องมีทั้งหมดนะครับ)

– iss (issuer) : เว็บหรือบริษัทเจ้าของ token
– sub (subject) : subject ของ token
– aud (audience) : ผู้รับ token
– exp (expiration time) : เวลาหมดอายุของ token
– nbf (not before) : เป็นเวลาที่บอกว่า token จะเริ่มใช้งานได้เมื่อไหร่
– iat (issued at) : ใช้เก็บเวลาที่ token นี้เกิดปัญหา
– jti (JWT id) : เอาไว้เก็บไอดีของ JWT แต่ละตัวนะครับ

Public Claim
คือข้อมูลที่เราทำการสร้างขึ้นเองครับ เช่น username ข้อมูลของ user เป็นต้นครับ

Private Claim
คือข้อมูลที่ผู้สร้างและผู้ใช้งานได้ตกลงกันเองแล้ว เป็นข้อมูลที่เป็นส่วนตัวระหว่างผู้สร้าง token กับผู้ใช้งาน token ต้องใช้งานข้อมูลส่วนนี้อย่างระมัดระวังนะครับ

var payload = {
“iss”: “konoe”,
“aud”: “everyone”,
“exp”: 1460046010,
“name”: “Narupon Srisantitham”
}

ส่วนสุดท้ายเป็นส่วนที่สำคัญสำหรับความปลอดภัยของ JWT เลยครับ มันคือส่วนของ signature ซึ่งมันเกิดจากการรวมกันของสองส่วนแรกครับ โดยการเอาส่วนของ header และ payload เข้ารหัส Base64 (เข้ารหัสแยกกันนะครับ) แล้วมาต่อกันด้วยจุด แล้วจึงเอา string ตัวใหม่ที่ได้ไปทำการเข้ารหัส HMAC SHA 256 กับ secret key อีกทีหนึ่ง จึงจะได้ส่วนของ signature มาครับ (secret key ตัวนี้เว็บใครเว็บมันนะครับ ผู้พัฒนาเว็บต้องเก็บไว้ให้ดีเลยนะครับ)

var encoded = base64Encode(header) + “.” + base64Encode(payload);
var signature = HMACSHA256(encoded);

ทีนี้เราก็เอาทั้งสามส่วนมาต่อกับครับ เราก้จะได้ JWT แล้วดังนี้

var jwt_token = encoded + “.” + signature;

สำหรับการใช้งาน JWT ในตอนแรกเราก็ต้องทำการตรวจสอบมันก่อนครับว่ามันถูกแก้ไขกลางทางรึเปล่าโดยการแยกส่วน signature ออกมาครับ แต่เราต้องรู้ก่อนว่า JWT ตัวนี้ใช้วิธีเข้ารหัสแบบไหนจึงต้องถอดรหัส header ด้วย Base64 ออกดูก่อนซึ่งในที่นี้คือ HMAC SHA 256 แล้วเราจึงทำการสร้าง signature อีกครั้งจาก header และ payload (ตามวิธีสร้าง signature ข้างบน) แล้วจึงเอา signature ตัวใหม่ มาเทียบกับ signature ที่เราแยกออกมาก่อนหน้านี้ ถ้าตรงกันแสดงว่าข้อมูลไม่ได้ถูกแก้ไขกลางทางครับ เป็นข้อมูลที่น่าเชื่อถือสามารถนำไปใช้งานต่อได้เลยครับ

var split = jwt_token.split(“.”);
var header = base64Decode(split[0]);
var payload = base64Decode(split[1]);
var signature= base64Decode(split[2]);
if(header.alg == ‘HS256’){
var new_signature = HMACSHA256(split[0] + “.” + split[1]);
if(signature == new_signature){
//Do something
}
}

สรุป

JWT สามารถใช้ได้กับทุกภาษาที่รองรับข้อมูลแบบ JSON และสามารถส่งผ่าน HTTP header, POST, URL ได้ง่าย รวดเร็ว และมีความปลอดภัยด้วยตัวของมันเอง (ทั้งนี้ทั้งนั้นขึ้นอยู่กับการ implement ด้วยนะครับ) อย่างที่ผมได้ย้ำอยู่เสมอว่าข้อมูลนี้จะนำมาทำเป็น token จะต้องเป็นข้อมูลที่เปิดเผยได้นะครับ เพราะยังไงการเข้ารหัสก็เป็นการเข้ารหัสด้วย Base64 ซึ่งทุกคนสามารถเปิดมันได้จึงต้องใช้มันด้วยความรอบคอบนะครับ

ถ้าใครไม่อยากที่จะ implement เองก็สามารถหา plungin มาใช้ได้นะครับ
https://github.com/auth0/node-jsonwebtoken
https://github.com/jwt-dotnet/jwt
https://github.com/firebase/php-jwt
https://jwt.io
อันนี้เป็น Plungin ของภาษาต่าง ๆ ครับ ยังมีตัวอื่นอีกเยอะครับ สามารถเลือกใช้ได้ตามสะดวกเลยครับ

บทความอ้างอิง
https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html

--

--