ข้าม​ไป​ยัง​เนื้อหา

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 ไป​ใน​ตัว

เนื่องจาก 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) คือ​แยก​เป็น​สาม​ชั้น:

  1. Authentication service — ตัวกลาง​ที่ middleware เรียก​ใช้​เพื่อ​ทำ authenticate / challenge / forbid
  2. Scheme — ชื่อ​ที่​ผูก​กับ authentication handler ตัว​หนึ่ง​และ options การ​ตั้ง​ค่า​ของ​มัน (เช่น cookie scheme, JWT bearer scheme)
  3. 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:

Program.cs
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 ที่​รับ​มา​เสมอ