Authentication
“คุณเป็นคนที่อ้างว่าเป็นจริงหรือ?”
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”“Are you who you claim to be?” นี่คือแนวคิดหลักของ authentication (ย่อว่า AuthN) — กระบวนการยืนยันว่าผู้ใช้ ระบบ หรือ entity หนึ่งเป็นสิ่งที่มันอ้างว่าเป็นจริง ๆ ก่อนจะอนุญาตให้เข้าถึงทรัพยากรใด ๆ authentication คือด่านแรกที่ต้องผ่านก่อนเสมอ เพราะทุกการตัดสินใจเรื่องสิทธิ์ที่ตามมาล้วนอาศัยความเชื่อมั่นว่า “ผู้ใช้คนนี้คือใคร” ที่ถูกยืนยันไว้แล้ว
Authentication (AuthN) vs Authorization (AuthZ): AuthN ถามว่า “คุณเป็นใครจริง ๆ หรือ?” ส่วน AuthZ (ดู Authorization) ถามว่า “คุณมีสิทธิ์ทำสิ่งที่กำลังจะทำหรือไม่?” — คนละคำถามและมักถูกสับสนกัน Wikipedia อธิบายไว้ชัดว่า authentication ไม่ได้แปลว่า authorization โดยอัตโนมัติ (authentication does not imply authorization) ระบบหนึ่งอาจยืนยันตัวตนผู้ใช้สำเร็จ แต่ผู้ใช้นั้นอาจยังไม่มีสิทธิ์ทำสิ่งที่ร้องขอเลยก็ได้ ในโลก AAA (Authentication, Authorization, Accounting) ทั้งสามส่วนนี้ทำงานร่วมกัน: authentication พิสูจน์ตัวตน, authorization กำหนดสิทธิ์, และ accounting เก็บ audit trail ว่าใครทำอะไรไปบ้าง
สำหรับการยืนยันตัวตนแบบพบหน้า คุณอาจต้องแสดงบัตรประชาชนที่มีรูป ใบขับขี่ หรือพาสปอร์ต ส่วนการยืนยันตัวตนแบบดิจิทัลมีหลายรูปแบบและมักผสมผสานกันเพื่อความปลอดภัยที่สูงขึ้น
รูปแบบของการยืนยันตัวตน
หัวข้อที่มีชื่อว่า “รูปแบบของการยืนยันตัวตน”- Password-based: วิธีพื้นฐานที่สุด ใช้ชื่อผู้ใช้/อีเมลกับรหัสผ่าน มีจุดอ่อนมาก ทั้งรหัสอ่อนแอ การใช้รหัสซ้ำ (password reuse) และการถูกขโมย ยิ่งเกิด password fatigue (ความล้าจากการต้องจำรหัสจำนวนมาก) ผู้ใช้ก็ยิ่งใช้รหัสซ้ำ
- Token-based: ใช้อุปกรณ์หรือ app ที่สร้างรหัสผ่านครั้งเดียว (OTP) เช่น key fob หรือ authenticator app (Microsoft/Google Authenticator) หรือในบริบท web API มักหมายถึง bearer token อย่าง JWT ที่ client แนบมากับทุก request
- Certificate-based: ใช้ digital certificate ที่ออกโดย Certificate Authority (CA) ที่เชื่อถือได้ อาศัยคู่กุญแจ private/public key และลายเซ็นดิจิทัลของ CA (certificate ที่นิยมคือ X.509)
- Biometric: ใช้ลักษณะทางกายภาพหรือพฤติกรรม เช่น ใบหน้า เสียง ม่านตา ลายนิ้วมือ ซึ่งผูกกับตัวบุคคลจึงปลอดภัยกว่ารหัสผ่านแบบดั้งเดิม
- Passwordless / Passkeys (WebAuthn, FIDO2): มาตรฐานสมัยใหม่จาก W3C และ FIDO Alliance ใช้คู่กุญแจ public/private key ที่สร้างและเก็บไว้บนอุปกรณ์ของผู้ใช้ ตัว server เก็บเฉพาะ public key เท่านั้น ไม่มีความลับใด ๆ วิ่งผ่านเครือข่ายให้ถูก phishing ได้ ทำให้ passkey ต้านทาน phishing ได้ในตัว และเมื่อรวมกับ biometric/PIN บนอุปกรณ์ก็ทำหน้าที่เป็น multi-factor ไปในตัว
Multi-Factor Authentication 2FA / MFA
หัวข้อที่มีชื่อว่า “Multi-Factor Authentication 2FA / MFA”เนื่องจาก password-based ถูกเจาะได้ง่าย จึงมีการเรียกใช้ 2-factor (2FA) หรือ multi-factor (MFA) มากขึ้น โดยทั่วไปเริ่มจากรหัสผ่าน แล้วต้องมีปัจจัยเพิ่มอย่างน้อยอีกหนึ่งอย่างจากสามประเภท:
- Knowledge factor — สิ่งที่คุณ รู้ (รหัสผ่าน, PIN, คำถามความปลอดภัย)
- Possession factor — สิ่งที่คุณ มี (token generator, certificate, security key)
- Inherence factor — สิ่งที่คุณ เป็น (ลายนิ้วมือ ดวงตา ใบหน้า เสียง)
OWASP ระบุว่า MFA เป็นแนวป้องกันที่ดีที่สุดสำหรับการโจมตีที่เกี่ยวกับรหัสผ่านส่วนใหญ่ รวมถึง brute-force attack และควรเปิดใช้ทุกที่ที่ทำได้ แม้จะไม่สามารถบังคับใช้กับผู้ใช้ทุกกลุ่มได้เสมอไป
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”ในทางปฏิบัติ authentication มักไม่ใช่ code ที่คุณเขียนเองทั้งหมด แต่เป็นบริการ/library ที่ทำหน้าที่เฉพาะทาง แล้ว application เรียกใช้ผ่าน middleware หรือ service layer เพื่อรักษาแนวคิด Separation of Concerns — ตรรกะทางธุรกิจไม่ควรรู้รายละเอียดว่าใช้ cookie, JWT หรือ OAuth provider ใด
รูปแบบทั่วไปของ web framework สมัยใหม่ (เช่น ASP.NET Core) คือแยกเป็นสามชั้น:
- Authentication service — ตัวกลางที่ middleware เรียกใช้เพื่อทำ authenticate / challenge / forbid
- Scheme — ชื่อที่ผูกกับ authentication handler ตัวหนึ่งและ options การตั้งค่าของมัน (เช่น cookie scheme, JWT bearer scheme)
- Handler — code จริงที่สร้าง
ClaimsPrincipalจาก request เช่น อ่าน cookie แล้วตรวจสอบ หรือถอดรหัสและตรวจลายเซ็นของ JWT
เมื่อผู้ใช้ที่ยังไม่ได้ authenticate พยายามเข้าถึงทรัพยากรที่ต้องมีสิทธิ์ ระบบจะเรียก challenge (เช่น redirect ไปหน้า login หรือคืน 401 พร้อม header WWW-Authenticate) ส่วนถ้า authenticate แล้วแต่ไม่มีสิทธิ์ในทรัพยากรนั้น ระบบจะเรียก forbid (คืน 403) — ทั้งสองอย่างนี้เป็นคนละ action กัน และมักถูกใช้ผิดสลับกัน
แนวทางที่ควรยึดเมื่อออกแบบ authentication:
- อย่าจัดเก็บรหัสผ่านแบบ plaintext ให้ hash ด้วย algorithm ที่ออกแบบมาสำหรับรหัสผ่านโดยเฉพาะ (bcrypt, scrypt, Argon2) พร้อม salt
- บังคับใช้ TLS เสมอ ทั้งหน้า login และทุกหน้าที่ authenticated แล้ว เพื่อป้องกันการดักฟัง credential ระหว่างทาง
- ทำ login throttling / rate limiting เพื่อกันการ brute-force เดารหัสผ่าน
- ใช้ session identifier ที่สุ่มคาดเดายากและไม่ซ้ำต่อผู้ใช้ และหมุนเวียน (rotate) เมื่อสิทธิ์เปลี่ยน (เช่นหลัง login สำเร็จ) เพื่อกัน session fixation
- แยก concern ระหว่าง authentication กับ authorization อย่างชัดเจน — อย่าให้ handler ตัวเดียวทำทั้งสองหน้าที่ปนกัน
- พิจารณา OAuth 2.0 / OpenID Connect (OIDC) เมื่อจะมอบหมายการยืนยันตัวตนให้ผู้ให้บริการภายนอก (Google, Microsoft Entra ID, ฯลฯ) — OAuth เป็น authorization framework สำหรับมอบสิทธิ์การเข้าถึง API ส่วน OIDC คือ identity layer ที่วางอยู่บน OAuth และนิยามวิธีที่ client ยืนยันตัวตนผู้ใช้ปลายทางผ่าน ID Token ซึ่งเป็น JWT ที่มีลายเซ็น
sequenceDiagram
participant User
participant App as Web App
participant Auth as Authentication Handler
participant IdP as Identity Provider
User->>App: ขอเข้าถึงหน้าที่ต้องมีสิทธิ์
App->>Auth: authenticate request
Auth-->>App: ไม่มี valid session
App->>User: challenge redirect ไปหน้า login
User->>IdP: กรอกรหัสผ่าน หรือ ยืนยัน passkey
IdP-->>User: ออก token หรือ set cookie
User->>App: ส่งคำขอพร้อม credential
App->>Auth: authenticate อีกครั้ง
Auth-->>App: สร้าง ClaimsPrincipal สำเร็จ
App-->>User: อนุญาตเข้าถึงทรัพยากร
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่างการตั้งค่า authentication ใน ASP.NET Core ที่รองรับทั้ง cookie (สำหรับ browser session) และ JWT bearer (สำหรับ API call) พร้อมกัน — เป็นรูปแบบที่พบบ่อยในระบบที่มีทั้งหน้าเว็บและ API:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // scheme สำหรับ API ที่ verify JWT bearer token จาก header Authorization .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => builder.Configuration.Bind("JwtSettings", options)) // scheme สำหรับ browser session ที่เก็บ ClaimsPrincipal ไว้ใน encrypted cookie .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => builder.Configuration.Bind("CookieSettings", options));
var app = builder.Build();
// ลำดับสำคัญ: UseAuthentication ต้องมาก่อน middleware ใด ๆ// ที่อาศัยว่าผู้ใช้ authenticated แล้ว (เช่น authorization)app.UseAuthentication();app.UseAuthorization();
app.MapGet("/profile", (ClaimsPrincipal user) =>{ // ClaimsPrincipal ถูกสร้างโดย authentication handler // ที่ authorization จะใช้ต่อในการตัดสินใจเรื่องสิทธิ์ var name = user.Identity?.Name ?? "ไม่ทราบชื่อ"; return Results.Ok(new { name });}).RequireAuthorization();
app.Run();หัวใจของ code นี้คือแนวคิด scheme และ handler: AddJwtBearer กับ AddCookie ต่างลงทะเบียน handler คนละตัวภายใต้ scheme คนละชื่อ แล้ว [Authorize] หรือ RequireAuthorization() สามารถระบุ scheme เฉพาะได้ถ้าต้องการแยก endpoint ที่ต้องการ JWT ออกจาก endpoint ที่ใช้ cookie session
ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”ประโยชน์
- เป็นรากฐานของ security model ทั้งระบบ — ไม่มี authentication ที่เชื่อถือได้ ก็ไม่มี authorization ที่มีความหมาย
- แยก concern ของ “ใครคือใคร” ออกจาก business logic ทำให้เปลี่ยนวิธียืนยันตัวตน (เช่นจากรหัสผ่านไปเป็น passkey) โดยไม่กระทบ domain logic
- การใช้ MFA/passkey ลดความเสี่ยงจาก credential-based attack ได้อย่างมีนัยสำคัญ เพราะไม่มีความลับที่ถูกขโมยแล้วนำไปใช้ซ้ำได้
ข้อควรระวัง
- อย่าปนกันระหว่าง AuthN กับ AuthZ — code ที่ตรวจ “login แล้วหรือยัง” ไม่ควรเป็นตัวเดียวกับที่ตรวจ “มีสิทธิ์ทำสิ่งนี้ไหม” มิฉะนั้นจะเกิด Divergent Change เมื่อกฎสิทธิ์เปลี่ยนบ่อยกว่าวิธียืนยันตัวตน
- อย่าเก็บรหัสผ่านหรือ session token แบบไม่ปลอดภัย การ hash ผิดวิธี (เช่นใช้ MD5/SHA1 เปล่า ๆ) หรือเก็บ token ใน localStorage ที่เสี่ยงต่อ XSS ล้วนเป็นช่องโหว่ที่พบบ่อย
- User ID ไม่ควรเดาง่ายหรือเรียงลำดับได้ เพราะอาจถูกใช้สำรวจ enumerate บัญชีผู้ใช้อื่นได้
- password-based เพียงอย่างเดียวไม่เพียงพอ สำหรับระบบที่มีข้อมูลอ่อนไหว ควรผลักดันให้ใช้ MFA หรือ passkey เป็นค่าเริ่มต้น
- การมอบหมายให้ IdP ภายนอก (OAuth/OIDC) ลดภาระการดูแล credential เอง แต่ก็เพิ่ม dependency และ attack surface ใหม่ (เช่น redirect URI ที่ไม่ปลอดภัย) จึงต้อง Defensive Programming กับ callback และ token ที่รับมาเสมอ
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- Authorization
- Defensive Programming
- Dependency Injection
- Kerckhoffs’s Law
- Divergent Change
- Separation of Concerns