🔵 Node.js

สอนทำระบบ Authentication ด้วย bcrypt และ JWT สำหรับมือใหม่

13 นาที 13 views บันทึกเป็น PDF
สอนทำระบบ Authentication ด้วย bcrypt และ JWT สำหรับมือใหม่

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

ทำไมเราต้องทำระบบล็อกอินให้ปลอดภัย

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

ลองจินตนาการว่าฐานข้อมูลของเราเปรียบเสมือนตู้เซฟเก็บเอกสารสำคัญ ถ้าเราเก็บรหัสผ่านเป็นข้อความธรรมดาๆ ใครก็ตามที่แอบเปิดตู้เซฟได้ก็จะเห็นรหัสผ่านของทุกคนทันที วิธีการที่ถูกต้องคือเราต้องทำให้รหัสผ่านเหล่านั้นกลายเป็น Ciphertext (ข้อความที่ถูกแปลงจนอ่านไม่รู้เรื่อง) เพื่อป้องกันไม่ให้คนอื่นนำไปใช้ได้ แม้จะแอบดูข้อมูลในฐานข้อมูลของเราไปได้ก็ตาม

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

การปกป้องรหัสผ่านด้วยการทำ Hashing

หลายคนอาจคิดว่าการเก็บรหัสผ่านคือการบันทึกลงฐานข้อมูลตรงๆ แต่ในทางปฏิบัติเราจะใช้เทคนิคที่เรียกว่า Hashing (การแปลงข้อมูลให้เป็นรหัสทางเดียวที่ย้อนกลับไม่ได้) ซึ่งต่างจากการเข้ารหัสทั่วไป เพราะเมื่อเราแปลงรหัสผ่านเป็น Hash (ผลลัพธ์จากการทำ Hashing) แล้ว เราจะไม่สามารถย้อนกลับมาเป็นรหัสผ่านเดิมได้อีกเลย วิธีนี้ช่วยให้เราตรวจสอบได้แค่ว่ารหัสที่ผู้ใช้กรอกมานั้นตรงกับที่เคยบันทึกไว้หรือไม่

เครื่องมือยอดนิยมที่เราจะใช้คือ bcrypt ซึ่งมีความพิเศษคือมันจะทำการ Salt (การเติมข้อมูลสุ่มเข้าไปในรหัสผ่านก่อนทำ Hashing) ให้โดยอัตโนมัติ การเติมข้อมูลสุ่มนี้จะช่วยป้องกันการโจมตีแบบ Brute Force (การสุ่มรหัสผ่านไปเรื่อยๆ จนกว่าจะถูก) เพราะแม้ผู้ใช้สองคนจะมีรหัสผ่านเหมือนกัน แต่ผลลัพธ์ของ Hash ที่ได้จะออกมาหน้าตาไม่เหมือนกันเลย ทำให้แฮกเกอร์เดารหัสผ่านได้ยากขึ้นหลายเท่าตัว

การติดตั้งและใช้งานใน Node.js นั้นง่ายมาก เริ่มต้นให้เราติดตั้งผ่านคำสั่ง npm install bcrypt ในโปรเจกต์ของเราก่อน หลังจากนั้นเราสามารถเรียกใช้ฟังก์ชันในการแปลงรหัสผ่านได้ทันที ขั้นตอนนี้เป็นสิ่งที่โปรแกรมเมอร์ทุกคนต้องทำก่อนบันทึกข้อมูลผู้ใช้ลงฐานข้อมูลเสมอ เพื่อให้มั่นใจว่ารหัสผ่านจะถูกเก็บอย่างปลอดภัยที่สุดตามหลักการพัฒนาซอฟต์แวร์ที่ดี

const bcrypt = require('bcrypt'); // เรียกใช้ไลบรารี bcrypt

const password = 'mySecretPassword'; // รหัสผ่านที่ผู้ใช้ตั้ง
const saltRounds = 10; // จำนวนรอบการสุ่มเพื่อให้ได้ความปลอดภัยที่เหมาะสม

// เริ่มต้นแปลงรหัสผ่านเป็น Hash
bcrypt.hash(password, saltRounds, (err, hash) => {
  console.log('รหัสผ่านที่แปลงแล้ว:', hash);
});

บรรทัดแรกเป็นการดึง bcrypt เข้ามาใช้งานในไฟล์ของเรา ส่วน saltRounds คือตัวกำหนดระดับความยากในการถอดรหัส ยิ่งเลขเยอะยิ่งปลอดภัยแต่ก็จะใช้เวลาประมวลผลนานขึ้น บรรทัดสุดท้ายคือการใช้ฟังก์ชัน bcrypt.hash เพื่อแปลงรหัสผ่านตามจำนวนรอบที่กำหนดไว้ ผลลัพธ์ที่ได้จะเป็นข้อความยาวๆ ที่ดูสุ่มมั่วๆ เช่น $2b$10$XyZ... ซึ่งปลอดภัยต่อการเก็บในฐานข้อมูล

การตรวจสอบรหัสผ่านเมื่อผู้ใช้ล็อกอิน

เมื่อเราเก็บรหัสผ่านแบบ Hash ไว้ในฐานข้อมูลแล้ว เวลาผู้ใช้กลับมาล็อกอิน เราไม่สามารถเปรียบเทียบรหัสผ่านที่เขากรอกมากับค่าในฐานข้อมูลตรงๆ ได้ เพราะมันไม่มีทางเท่ากันแน่นอน เราจึงต้องใช้ฟังก์ชันของ bcrypt เพื่อเปรียบเทียบว่ารหัสที่เขากรอกมาเมื่อนำไปทำ Hashing แล้ว จะได้ค่าเดียวกับที่เก็บไว้หรือไม่

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

มือใหม่มักจะพลาดโดยการพยายามเขียนฟังก์ชันเปรียบเทียบเอง หรือลืมเช็คผลลัพธ์ที่ได้รับจาก bcrypt ซึ่งอาจทำให้ระบบล็อกอินผิดพลาดได้ เราควรตรวจสอบเสมอว่าค่าที่คืนกลับมาเป็น true หรือ false เพื่อนำไปใช้ตัดสินใจว่าจะส่งข้อความตอบกลับไปยังผู้ใช้งานอย่างไร การเขียนโค้ดในส่วนนี้ให้แม่นยำจะช่วยลดช่องโหว่ของระบบได้เป็นอย่างดี

// สมมติว่า hash จากฐานข้อมูลคือค่าที่ได้จากหัวข้อก่อนหน้า
const storedHash = '$2b$10$XyZ...'; 
const inputPassword = 'mySecretPassword'; // รหัสที่ผู้ใช้กรอกมา

bcrypt.compare(inputPassword, storedHash, (err, result) => {
  if (result) {
    console.log('รหัสผ่านถูกต้อง เข้าสู่ระบบได้');
  } else {
    console.log('รหัสผ่านไม่ถูกต้อง');
  }
});

ในโค้ดชุดนี้เรานำ inputPassword มาเปรียบเทียบกับ storedHash ที่เราเก็บไว้ ฟังก์ชัน bcrypt.compare จะจัดการเรื่องการถอดค่า Salt ที่ซ่อนอยู่ใน Hash ออกมาเองโดยอัตโนมัติ ผลลัพธ์ที่ได้ในตัวแปร result จะเป็นค่าความจริง (Boolean) ถ้าเป็น true แสดงว่ารหัสถูกต้อง และถ้าเป็น false แสดงว่ารหัสผ่านผิดพลาด

การรู้จักกับ JSON Web Token (JWT)

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

โครงสร้างของ JWT จะประกอบด้วย 3 ส่วนหลัก คือ Header (ข้อมูลส่วนหัวที่บอกประเภทของ Token), Payload (ข้อมูลของผู้ใช้งานที่ต้องการเก็บไว้ เช่น ID หรือชื่อผู้ใช้) และ Signature (ลายเซ็นดิจิทัลเพื่อยืนยันว่า Token นี้ถูกสร้างโดยเซิร์ฟเวอร์เราจริงๆ) ทั้งสามส่วนนี้จะถูกรวมเข้าด้วยกันเป็นสตริงยาวๆ ที่มีจุดเชื่อมต่อกัน ทำให้มันเป็นกุญแจที่ปลอมแปลงได้ยากมาก

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

การสร้างและใช้งาน JWT ใน Node.js

ในการสร้าง JWT เรานิยมใช้ไลบรารีชื่อ jsonwebtoken ซึ่งช่วยให้เราจัดการเรื่องการสร้างและการตรวจสอบ Token ได้อย่างสะดวกสบาย ก่อนเริ่มใช้งานให้ติดตั้งผ่านคำสั่ง npm install jsonwebtoken เมื่อติดตั้งเสร็จแล้ว เราจะใช้ฟังก์ชัน jwt.sign เพื่อสร้าง Token ขึ้นมาหลังจากที่ผู้ใช้ล็อกอินผ่านแล้ว โดยเราต้องกำหนด Secret Key (กุญแจลับที่ใช้สร้างลายเซ็น) ไว้ด้วย

สิ่งสำคัญที่ต้องระวังคือ Secret Key ต้องเก็บไว้เป็นความลับที่สุด ห้ามนำไปเขียนรวมในโค้ดแล้วอัปโหลดขึ้น GitHub หรือพื้นที่สาธารณะเด็ดขาด ให้ใช้การเก็บผ่าน Environment Variable (การเก็บค่าตั้งค่าในตัวแปรระบบ) แทน เพื่อป้องกันไม่ให้คนอื่นขโมยกุญแจนี้ไปปลอมแปลง Token ของเราได้ ถ้ากุญแจนี้หลุดออกไป ระบบความปลอดภัยทั้งหมดของเราจะถือว่าล้มเหลวทันที

นอกจากนี้เราควรตั้งค่า Expiration Time (เวลาหมดอายุของ Token) ไว้เสมอ เช่น ให้ Token หมดอายุภายใน 1 ชั่วโมง เพื่อลดความเสี่ยงหาก Token ถูกขโมยไปใช้งานต่อ การวางแผนจัดการเรื่องวันหมดอายุเป็นสิ่งที่โปรแกรมเมอร์มือโปรต้องคิดถึงอยู่เสมอ เพื่อสร้างความปลอดภัยให้กับผู้ใช้งานในระยะยาวและป้องกันการถูกโจมตีในรูปแบบต่างๆ

const jwt = require('jsonwebtoken');

const user = { id: 1, username: 'dev_user' };
const secretKey = 'your-very-secret-key'; // กุญแจลับ ห้ามเปิดเผย!

// สร้าง Token โดยมีอายุการใช้งาน 1 ชั่วโมง
const token = jwt.sign(user, secretKey, { expiresIn: '1h' });
console.log('Token ที่สร้างได้:', token);

ในตัวอย่างนี้เราใช้ jwt.sign เพื่อสร้าง Token โดยส่งข้อมูลผู้ใช้และ Secret Key เข้าไป ผลลัพธ์ที่ได้จะเป็นชุดตัวอักษรยาวๆ ที่เราจะส่งกลับไปให้ผู้ใช้เก็บไว้ใน Browser (โปรแกรมเปิดเว็บ) เพื่อใช้ยืนยันตัวตนในคำสั่งถัดๆ ไป เช่น การขอเข้าถึงข้อมูลส่วนตัวที่ต้องล็อกอินก่อนถึงจะดูได้

การตรวจสอบ JWT เพื่อเข้าถึงข้อมูล

เมื่อผู้ใช้ส่งคำขอมาพร้อมกับ Token ที่ถืออยู่ เราต้องมีหน้าที่ตรวจสอบว่า Token นั้นยังใช้ได้อยู่หรือไม่ โดยใช้ฟังก์ชัน jwt.verify ในขั้นตอนการตรวจสอบนี้ เราจะใช้ Secret Key ตัวเดิมที่ใช้สร้าง Token มาตรวจสอบ หาก Token ถูกแก้ไขหรือปลอมแปลงขึ้นมา ฟังก์ชันนี้จะแจ้งเตือนทันที ทำให้เราสามารถปฏิเสธคำขอที่ไม่ได้รับอนุญาตได้อย่างแม่นยำ

มือใหม่มักลืมตรวจสอบว่า Token หมดอายุไปหรือยัง หรือลืมจัดการกรณีที่ Token หายไปจากการส่งคำขอ เราควรเขียนโค้ดในรูปแบบ Middleware (ฟังก์ชันที่คั่นกลางเพื่อตรวจสอบก่อนทำงานหลัก) ในเฟรมเวิร์กอย่าง Express เพื่อให้ทุกเส้นทาง (Route) ที่ต้องการความปลอดภัยถูกตรวจสอบโดยอัตโนมัติ วิธีนี้จะช่วยให้โค้ดของเราสะอาดและเป็นระเบียบ ไม่ต้องเขียนคำสั่งตรวจสอบซ้ำๆ ในทุกจุด

การจัดการกับข้อผิดพลาด (Error Handling) ก็สำคัญมาก ถ้า Token ไม่ถูกต้องหรือหมดอายุ เราต้องส่งสถานะ HTTP Status Code 401 Unauthorized (รหัสสถานะบอกว่ายังไม่ได้ล็อกอิน) กลับไปให้ผู้ใช้เสมอ เพื่อแจ้งให้ฝั่งหน้าบ้านทราบว่าต้องให้ผู้ใช้ล็อกอินใหม่ การทำระบบที่สื่อสารข้อผิดพลาดได้ชัดเจนแบบนี้จะช่วยให้การพัฒนาโปรเจกต์ของคุณเป็นระบบและง่ายต่อการดูแลรักษา

// ตรวจสอบ Token ที่ได้รับจากผู้ใช้
jwt.verify(token, secretKey, (err, decoded) => {
  if (err) {
    console.log('Token ไม่ถูกต้อง หรือหมดอายุ');
  } else {
    console.log('ข้อมูลผู้ใช้ที่ถอดรหัสได้:', decoded);
  }
});

ในโค้ดนี้ jwt.verify จะรับ Token มาตรวจสอบกับ secretKey ถ้าไม่มีปัญหาอะไร ตัวแปร decoded จะเก็บข้อมูลผู้ใช้ที่เราเคยใส่ไว้ตอนสร้าง Token กลับมาให้เราใช้งานต่อได้ทันที แต่ถ้ามีข้อผิดพลาดตัวแปร err จะถูกเติมค่าเข้ามา ซึ่งเราสามารถเช็คได้ว่าทำไม Token ถึงใช้งานไม่ได้

สรุป: เริ่มต้นเส้นทางโปรแกรมเมอร์ด้วยความปลอดภัย

การทำระบบ Authentication ด้วย bcrypt และ JWT เป็นจุดเริ่มต้นที่ยอดเยี่ยมสำหรับคนที่อยากเป็นโปรแกรมเมอร์สาย Backend เพราะมันคือพื้นฐานที่ใช้กันจริงในบริษัทเทคโนโลยีชั้นนำทั่วโลก การเข้าใจหลักการทำงานของเครื่องมือเหล่านี้ช่วยให้เราสร้างแอปพลิเคชันที่น่าเชื่อถือและปลอดภัยสำหรับผู้ใช้งาน และยังเป็นทักษะที่นำไปใช้ต่อยอดในโปรเจกต์ต่างๆ ใน Portfolio (แฟ้มสะสมผลงาน) ของคุณได้เป็นอย่างดี

ลองนำสิ่งที่เรียนรู้วันนี้ไปใช้ในโปรเจกต์ทำเว็บแอปพลิเคชันจัดการงานส่วนตัว (To-do List) ที่คุณอาจจะกำลังทำอยู่ โดยเริ่มจากการเพิ่มระบบสมัครสมาชิกและล็อกอินแบบง่ายๆ ดู การลองผิดลองถูกด้วยตัวเองจะทำให้คุณจำวิธีเขียนได้แม่นยำกว่าการอ่านเพียงอย่างเดียว อย่าลืมว่าความปลอดภัยคือหัวใจของระบบซอฟต์แวร์ที่ดี และคุณสามารถเริ่มสร้างนิสัยนี้ได้ตั้งแต่วันนี้ผ่านการเขียนโค้ดที่รัดกุม

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

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

คู่มือการทำ Production Deployment: รันโปรเจกต์ด้วย PM2 และ Docker ให้เสถียรเหมือนมือโปร
Node.js

คู่มือการทำ Production Deployment: รันโปรเจกต์ด้วย PM2 และ Docker ให้เสถียรเหมือนมือโปร

เรียนรู้วิธีนำโปรเจกต์ขึ้นเซิร์ฟเวอร์จริงให้รันได้ 24 ชั่วโมงด้วย PM2 และใช้ Docker ช่วยคุมสภาพแวดล้อมให้เหมือนกันทุกเครื่อง ลดปัญหาโค้ดพังเมื่อย้ายที่

1 week ago 11 นาที
14 views
เปลี่ยนโปรเจกต์ Node.js เป็น TypeScript: วิธีตั้งค่าและใช้งานสำหรับมือใหม่
Node.js

เปลี่ยนโปรเจกต์ Node.js เป็น TypeScript: วิธีตั้งค่าและใช้งานสำหรับมือใหม่

อยากเปลี่ยนจาก Node.js มาใช้ TypeScript แต่ไม่รู้เริ่มยังไง? บทความนี้สอนตั้งค่า tsconfig.json, การรันด้วย tsx และการทำ Type Checking ให้ Express แบบเข้าใจง่าย

1 week ago 9 นาที
12 views
สอนเขียน Automated Testing ด้วย Jest และ Supertest สำหรับมือใหม่
Node.js

สอนเขียน Automated Testing ด้วย Jest และ Supertest สำหรับมือใหม่

เลิกทดสอบโค้ดด้วยมือแบบเดิม ๆ มาหัดเขียน Automated Testing ด้วย Jest และ Supertest เพื่อเช็กฟังก์ชันและ API ให้ทำงานถูกต้องแม่นยำ ลดบั๊กกวนใจ

1 week ago 9 นาที
15 views