Password Hashing ด้วย bcrypt

Password Hashing คือการแปลง password เป็นค่าทิศทางเดียวที่ไม่ควรถอดกลับได้ โดย bcrypt เพิ่ม Salt และ Cost Factor เพื่อทำให้การเดารหัสผ่านจำนวนมากทำได้ยากขึ้น

Timeline/ประวัติศาสตร์

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
flowchart LR
  subgraph Era1["ยุค Plain Text / Unsafe Storage"]
    A["Plain Password
รั่วแล้วใช้ได้ทันที"] end subgraph Era2["ยุค Fast Hash / Weak Hash"] B["MD5/SHA1
เร็วเกินไปสำหรับ password"] C["Rainbow Table
เดาจากตารางสำเร็จรูป"] end subgraph Era3["ยุค Password Hash / Adaptive Hash"] D["Salt
ค่าสุ่มต่อ password"] E["bcrypt Cost
ปรับความยากได้"] end A --> B --> C --> D --> E

แนวคิดสำคัญ

ตารางเปรียบเทียบ

วิธี ถอดกลับได้ เหมาะกับ Password หมายเหตุ
Plain Text ไม่ต้องถอด ไม่เหมาะอย่างยิ่ง เสี่ยงสูง
Encryption ได้เมื่อมีกุญแจ ไม่ควรใช้เป็นหลัก เหมาะกับข้อมูลที่ต้องอ่านคืน
MD5/SHA1 ไม่ได้ ไม่เหมาะ เร็วเกินไป
bcrypt ไม่ได้ เหมาะ มี salt และ cost

Mermaid Diagram: Hash and Compare

%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#fabd2f", "primaryTextColor": "#282828", "primaryBorderColor": "#b57614", "lineColor": "#7c6f64", "secondaryColor": "#83a598", "tertiaryColor": "#b8bb26", "background": "#fbf1c7", "mainBkg": "#ebdbb2", "fontFamily": "Tahoma, sans-serif"}}}%%
flowchart TD
  A["Register
สมัครสมาชิก"] --> B["Plain Password
รหัสผ่านที่กรอก"] B --> C["bcrypt.hash
hash + salt"] C --> D["Store Hash
เก็บ hash"] E["Login
เข้าสู่ระบบ"] --> F["bcrypt.compare
เทียบ input กับ hash"] D --> F F --> G{"Valid?
ถูกต้องไหม"} G -->|Yes| H["Login Success
เข้าสู่ระบบสำเร็จ"] G -->|No| I["Reject
ปฏิเสธ"]

Code Example

// bcrypt-demo.mjs
// ตัวอย่างสมัครสมาชิกและ login ด้วย bcryptjs
import express from 'express';
import bcrypt from 'bcryptjs';

const app = express();
app.use(express.json());

const users = [];
const saltRounds = 12;

app.post('/register', async (req, res) => {
  const { email, password } = req.body;
  const passwordHash = await bcrypt.hash(password, saltRounds);

  users.push({ id: Date.now(), email, passwordHash });
  res.status(201).json({ message: 'registered' });
});

app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = users.find(item => item.email === email);
  if (!user) return res.status(401).json({ message: 'invalid credentials' });

  const ok = await bcrypt.compare(password, user.passwordHash);
  if (!ok) return res.status(401).json({ message: 'invalid credentials' });

  res.json({ message: 'login success' });
});

app.listen(3000, () => console.log('bcrypt demo on http://localhost:3000'));

// ตัวอย่างการใช้งาน:
// npm install express bcryptjs
// POST /register { "email": "ana@example.com", "password": "secret123" }
// POST /login    { "email": "ana@example.com", "password": "secret123" }

วิดีโอแนะนำ

กิจกรรมท้ายบท

กลับสัปดาห์ที่ 12