มาทำ Password Hashing ด้วย bcrypt พร้อมกับทำ JWT ด้วย Golang กัน
Bcrypt คือการเข้ารหัสโดยออกแบบโดย Niels Provos and David Mazières โดยใช้ blowfish Algorithm ในการมาสร้าง password แต่ละครั้งหน้าตาจะไม่เหมือนเดิมเลยซึ่ง hash หลุดไปก็เอาไปทำอะไรต่อไม่ได้ ต่างจาก md5 หรือ sha1 ที่ได้ผลลัพธ์การเข้ารหัสเหมือนเดิมทุกครั้ง ซึ่งหลุดไปคือเกมได้เลยเหมือนที่เป็นข่าวกันบ่อยๆ ซึ่งไม่ได้แปลว่ามีโปรแกรมที่สามารถ decrypt md5 หรือ sha1 ได้นะแต่มันได้ผลลัพธ์เหมือนเดิมก็มีบางเว็บไปทำ index password กับ hash ไว้ก็โดนเจาะได้สบายๆเลย แต่มันก็มีวิธีกันแบบอื่นนะอาจจะเข้า 2 ชั้น หรือ password + key ของระบบ แล้วค่อยเข้ารหัส ซึ่งสุดท้ายก็มีโอกาสโดนเจาะได้เหมือนเดิม แต่จะทำให้มันยุ่งยากทำไม ซึ่งจากตัว Bcrypt ที่ไม่ได้มีผลลัพธ์เหมือนเดิมฉนั้นก็ไม่สามารถไปทำ index ได้แน่นอนปลอดภัยกว่าเห็นๆ
JWT (JSON WEB TOKEN) คือ เป็นการเอาข้อมูล JSON Data ไปเข้ารหัส แบบ Base64Url Encoded ซึ่งนำมาแทน session ซึ่งโดยปกติเมื่อก่อน PHP ผมก็ใช้ตัว session เป็นหลักแหละก่อนมารู้จักคำว่า API เพราะสมัยนี้จะเป็นการยิง API เป็นหลัก ส่วน JWT ถามว่าปลอดภัยไหมก็ปลอดภัยอยู่ถ้าไม่มีคนเข้าถึงเครื่องผู้ใช้งานได้ TOKEN ตรงนั้นก็ไม่มีทางหลุด แล้วถ้าหลุดไปก็ได้แค่ข้อมูลของคนคนนั้น ก็อารมณ์ประมาณ TOKEN ของคนคนนั้นหลุดของ FACEBOOK เพราะทางผู้เจาะระบบไม่มีทางรู้ TOKEN SECRET ที่เราใช้สร้าง JWT ขึ้นมา และเรายังสามารถเปลี่ยน TOKEN SECRET บ่อยๆให้ระบบเรามีความปลอดภัยสูงยิ่งขึ้น
จากรูปด้านบน 3 รูปวันนี้ผมจะมาเขียนให้ตาม Jouney ที่วางเอาไว้ โดยจะเริ่มสมัครสมาชิก, Login, จนมาถึงทำ Midleware
โดยจะมีการ Set ค่าต่างๆลงไป โดย Bcrypt ผมจะให้หมุน 10 รอบ ยิ่งหมุนนานยิ่งเข้ารหัสนาน และถอดรหัสนาน ส่วน JWT ผมก็ Set secretKey, alogorithm ที่จะใช้ และ ให้มันหมดอายุเมื่อไหร่
ส่วนอันนี้จะเป็น Code ทั้งหมดของ Flow Register
“golang.org/x/crypto/bcrypt” โดยผมจะเน้นอธิบายคำสั่งอันนี้ที่เรา import เข้ามาคือเรื่องของ Bcrypt ส่วน Code ส่วนอื่นๆผมเคยอธิบายไว้ในบทความเก่าๆแล้วผมจะแปะไว้ด้านล่างสุดของบทความ
เข้ารหัสง่ายๆมากเพราะเรา import ที่เขาเขียนไว้พร้อมใช้งานหมดแล้ว ก็ส่งแค่ password ที่เป็น string และ จะหมุนกี่รอบยิ่งหมุนนานยิ่งเข้ารหัสนานและถอดรหัสนานแต่ไม่ได้มีผลอะไรมาก regis และ login เราไม่ได้ใช้บ่อยอยู่แล้วคำว่านานจริงๆก็แทบไม่รู้สึก
เมื่อเราเข้าไปดูใน mongodb ผลลัพธ์ก็คือ password เดียวกันแต่ hash ไม่เหมือนกัน เฉียบหลังจากทำ register แล้วเราไปดูการทำ login ดีกว่า
code ส่วนนี้จะเพิ่มการ Login จนไปถึงการทำ JWT ลองมาดูทีละส่วนกันครับ
“github.com/dgrijalva/jwt-go” ผมได้ import jwt มาเพิ่ม
และได้สร้าง Struct ของ Claims ขึ้นมาเพื่อจะได้ return json ไปถูกต้อง โดยจะมี standard ของ jwt ก็คือในส่วนของการทำ expire
และเพิ่มในส่วนของการ Login ขึ้นมา ส่วนแรกก็รับค่าจาก post ที่เป็น json เข้ามา และทำการนำ username ไป find หาข้อมูลของ client มาส่วนนี้ find ปกติไม่มีอะไร
จากนั้นก็ได้ result ที่เรา find มา ก็จะได้ password ที่เป็น hash ที่เรา stamp เข้า database และทำการเทียบกับ password ที่เราส่งผ่าน Postman เข้ามาถ้าผ่านแสดงว่าถูกต้องไม่ผ่านก็คือ password ผิด
จากนั้นก็ get ค่า algorithm และ secretKey ที่เราเซทไว้ใน config และทำการใส่ค่าเข้าไปใน claims ตาม json ที่เราต้องการ และใส่วันหมดอายุของ jwt หลังจากนั้นเราก็ เอา algorithm , claims โยนเข้าไปสร้าง token และทำการผูกเข้ากับ secretKey ของเราเพื่อที่จะให้ใช้แค่ในระบบเราถ้าหลุดมาเราเปลี่ยนแค่ secretKey ก็หลุดทั้งระบบละ
ซึ่งสามารถเอา token ที่ได้ไปเช็คกับ web https://jwt.io/ ของ jwt ได้เลย ซึ่งสามารถ custom ค่าได้ทั้งหมดแต่ก็มาใช้กับระบบเราไม่ได้นะเพราะไม่รู้ secretKey จากนั้นเราลองมาทำ Middleware กัน
มาถึง code ส่วนสุดท้ายกันแล้วคือการทำ Middleware
ส่วนแรกไม่มีอะไรเลยแค่ดัก header ถ้าน้อยกว่า 1 ก็คือไม่ได้ส่ง token มาเลย
ส่วนที่ 2 ก็แค่ดักว่า ส่ง Bearer มาใน Authorization ไหม
ส่วนสุดท้ายเป็นการ Check Token ว่าถูกต้องไหม หมดเวลาหรือยัง ถ้าถูกต้องหมดก็ใส่ค่าไปใน claims เพื่อนำไปใช้ต่อ
ส่วนอันนี้เป็นการเรียกใช้ Middleware แบบ basic ก่อนเข้าไปทำงาน
เป็นยังไงครับก็จบไปแล้วเท่านี้เราก็สามารถทำ Login แบบปลอดภัยได้ในระดับนึงแล้วเพราะการทำ security ไม่ได้มีแค่ปัจจัยแค่ Code มีอีกหลายปัจจัยมาก การทำ network , การทำ whitelist ip การเปลี่ยน config หรือ รหัสเข้า db สม่ำเสมอ และต่างๆอีกมากมาย
บทความอื่นๆที่คิดว่าน่าจะเกี่ยวข้อง
( CR. ธำรงค์ ไชยวงค์ )