Notes & software courses · Free to learn
Aph's Blog
On this page

Authentication

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

ทำให้แอป "รู้ว่าใครเป็นใคร" — login, hash รหัสผ่าน, และป้องกัน endpoint ด้วย token

แอปจริงเกือบทุกตัวต้องรู้ว่าผู้ใช้คนไหนกำลังเรียก เพื่อแสดงข้อมูลของเขาและจำกัดสิทธิ์ หัวข้อนี้ปูพื้นฐาน authentication: เก็บรหัสผ่านอย่างปลอดภัย, login, และป้องกัน endpoint

session vs token

แบบหลักการเก็บที่
Sessionserver จำว่าใคร login ผ่าน session idserver (+cookie)
Token (JWT)client ถือ token ที่ลงนามไว้ ส่งมาทุก requestclient

API สมัยใหม่นิยม token (JWT) เพราะ stateless — server ไม่ต้องจำ session แค่ตรวจลายเซ็น token ที่ client ส่งมา

hash รหัสผ่าน — ห้ามเก็บ plaintext

เก็บรหัสผ่านเป็นข้อความตรง ๆ คือหายนะ ถ้า DB หลุดผู้ใช้ทุกคนซวย ต้อง hash (แปลงทางเดียว) + salt เสมอ — ตอน login เอารหัสที่กรอกมา hash แล้วเทียบ

python
from passlib.hash import bcrypt   # pip install passlib bcrypt

# ตอนสมัคร: hash ก่อนเก็บ
hashed = bcrypt.hash("my_password")
print(hashed)   # $2b$12$....  (เก็บค่านี้ใน DB ไม่ใช่รหัสจริง)

# ตอน login: ตรวจรหัสที่กรอกกับ hash ที่เก็บไว้
bcrypt.verify("my_password", hashed)   # True
bcrypt.verify("wrong", hashed)         # False
อย่าเก็บรหัสผ่านเป็น plaintext เด็ดขาด

ห้ามเก็บรหัสผ่านตรง ๆ และห้าม hash แบบธรรมดา (เช่น md5/sha1 เปล่า ๆ) ใช้ bcrypt/argon2 ที่ออกแบบมาเพื่อรหัสผ่านโดยเฉพาะ (ช้าโดยตั้งใจ + มี salt) นี่คือมาตรฐานขั้นต่ำที่ละเมิดไม่ได้

login flow & ป้องกัน endpoint

ภาพรวม: ผู้ใช้ส่ง user/pass → server ตรวจ → ออก token → client เก็บ token แล้วแนบใน header ทุก request → server ตรวจ token ก่อนให้เข้า endpoint ที่ป้องกันไว้

python
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()
oauth2 = OAuth2PasswordBearer(tokenUrl="login")

def get_current_user(token: str = Depends(oauth2)):
    user = verify_token(token)        # ตรวจ/ถอด token
    if user is None:
        raise HTTPException(status_code=401, detail="ยังไม่ได้ล็อกอิน")
    return user

@app.get("/me")
def read_me(user = Depends(get_current_user)):   # endpoint ที่ต้อง login
    return {"user": user}

สรุปหัวข้อนี้

  • authentication = รู้ว่าใครเป็นใคร; API นิยม token (JWT, stateless)
  • hash รหัสผ่านด้วย bcrypt/argon2 + salt เสมอ — ห้ามเก็บ plaintext
  • ตอน login เทียบรหัสที่กรอกกับ hash (verify) ไม่ถอดรหัสกลับ
  • ป้องกัน endpoint ด้วย dependency ที่ตรวจ token (401 ถ้าไม่ผ่าน)
แบบฝึกหัด

1) เขียนฟังก์ชัน register ที่ hash รหัสผ่านด้วย bcrypt ก่อนเก็บ 2) เขียน login ที่ verify รหัสกับ hash 3) ทำ endpoint /me ที่ต้องมี token 4) อธิบายว่าทำไมห้ามเก็บรหัสผ่านเป็น plaintext