04
Phase 1: Basic • Foundations

Day 4: Clients Anatomy — Public vs. Confidential Clients & Network Boundaries

Phase 1 Index ↗
Module 04 • First Principles ทำความเข้าใจตัวละครที่สอง: Client คือใครในสายตาของ Keycloak?

1. คำว่า "Client" ใน Keycloak คืออะไร? ทำไมแอปพลิเคชันต้องลงทะเบียนตัวตน?

ใน Day 3 เราเข้าใจแล้วว่า User คือมนุษย์หรือผู้ใช้งาน และ Realm คืออาณาจักรที่จัดเก็บข้อมูล แต่คำถามที่ผู้เริ่มต้นมักสับสนที่สุดคือ: "คำว่า Client ใน Keycloak หมายถึงเครื่องคอมพิวเตอร์ของลูกค้า หรือหมายถึงมนุษย์ที่มาซื้อของกันแน่?"

ในโลกของ IAM และโปรโตคอล OAuth 2.0 / OIDC คำว่า Client ไม่ใช่มนุษย์ (Not an End-User) แต่หมายถึง "ซอฟต์แวร์หรือแอปพลิเคชัน (Application Entity)" ที่ทำหน้าที่เป็นตัวกลางพาผู้ใช้มาล็อกอิน หรือเป็นโปรแกรมที่ต้องการขอ Token ไปเรียกใช้งานระบบอื่น

ลองจินตนาการ: หาก Keycloak ไม่มีการลงทะเบียน Client ใครก็ตามในโลกอินเทอร์เน็ตที่สร้างเว็บฟิชชิ่งปลอมขึ้นมา ก็สามารถสั่งให้ผู้ใช้กด Redirect มาล็อกอินที่ Keycloak ของบริษัทคุณ แล้วสั่งให้ Keycloak ส่ง Authorization Code กลับไปยังเว็บของโจรได้ทันที! การลงทะเบียน Client ใน Keycloak จึงเป็นกระบวนการ "สร้างบัตรประจำตัวแอปพลิเคชันและกำหนดขอบเขตความปลอดภัย (Whitelisting)" เพื่อให้ Keycloak รู้ว่ากำลังคุยกับซอฟต์แวร์ที่บริษัทเป็นเจ้าของจริงๆ หรือไม่

🌐 Public Client (แอปสาธารณะ) Cannot Hold Secrets

"แอปที่รันบนเครื่องผู้ใช้ โค้ดถูกเปิดเผยได้"

  • ตัวอย่าง: Single Page Applications (React, Vue, Angular), Mobile Apps (iOS, Android, Flutter), Desktop Apps
  • กฎเหล็ก: ห้ามมี Client Secret เด็ดขาด! (ตั้งค่า Client Authentication = OFF ใน Keycloak) เพราะผู้ใช้สามารถเปิด DevTools ดู Source Code หรือ Decompile ไฟล์ APK/IPA เพื่อขโมย Secret ได้ใน 5 วินาที
  • กลไกความปลอดภัย: ต้องใช้ Authorization Code Flow คู่กับ PKCE (S256) เท่านั้น
🔒 Confidential Client (แอปหลังบ้านที่ปลอดภัย) Holds Secure Credentials

"แอปที่รันบน Server ส่วนตัว บุคคลภายนอกเข้าไม่ถึง"

  • ตัวอย่าง: Backend Web Services (Node.js/Express, Spring Boot, Go, Python/Django, Next.js API Routes)
  • กฎเหล็ก: มีการยืนยันตัวตนระดับแอปพลิเคชัน (ตั้งค่า Client Authentication = ON ใน Keycloak) และครอบครอง Client Secret หรือ Private Key JWT
  • ประโยชน์: สามารถทำ Machine-to-Machine (Client Credentials Flow) และแลก Token ผ่านช่องทางปิดที่ไม่ผ่านเบราว์เซอร์
นิยามระดับรากฐาน (Root Technical Definitions): • Client Entity: อัตลักษณ์ของซอฟต์แวร์แอปพลิเคชันที่ได้รับการอนุญาตให้ทำ Authentication หรือขอรับ Token ภายใต้ Realm นั้นๆ • Client Authentication Toggle: สวิตช์ใน Keycloak สำหรับเลือกว่า Client นี้คือ Confidential (ON = มีรหัสลับประจำแอป) หรือ Public (OFF = ไม่มีรหัสลับ ต้องใช้ PKCE) • Valid Redirect URIs: ไวท์ลิสต์ URL ปลายทางที่ Keycloak อนุญาตให้ส่ง Authorization Code หรือ Tokens กลับไปหลังจากผู้ใช้ล็อกอินสำเร็จ (ห้ามใช้ Wildcard เด็ดขาด) • Web Origins (CORS): โดเมนต้นทางที่อนุญาตให้ยิง AJAX/fetch ข้าม Domain มายัง Keycloak เพื่อขอ Token ได้ (ป้องกัน Browser Same-Origin Policy บล็อก)

📚 ศัพท์เทคนิคและการตั้งค่าความปลอดภัยของ Keycloak Client

ตารางแจกแจงฟิลด์สำคัญในหน้าการตั้งค่า Client บน Keycloak Admin Console:

Parameter Name นิยามทางสถาปัตยกรรม หน้าที่ & ตัวอย่างการใช้งาน ข้อกำหนดความปลอดภัย
Client ID ชื่ออ้างอิงเฉพาะของแอปพลิเคชัน สตริงระบุตัวตนแอป เช่น frontend-spa หรือ billing-service ใช้ส่งในพารามิเตอร์ client_id ทุกครั้ง เปิดเผยได้ ไม่ใช่ความลับ
Client Authentication สวิตช์กำหนดประเภทความลับ OFF: เป็น Public Client (ไม่มีแท็บ Credentials)
ON: เป็น Confidential Client (เปิดแท็บ Credentials เพื่อออก Client Secret)
SPA/Mobile ต้องเซ็ตเป็น OFF
Valid Redirect URIs ที่อยู่ปลายทางที่ได้รับอนุญาต URL ที่ Keycloak จะยอม 302 Redirect พร้อม Auth Code เช่น http://localhost:3000/callback ห้ามใช้ * (Wildcard) เด็ดขาด
Web Origins การอนุญาต Cross-Origin (CORS) ระบุ Origin ของ Web Browser เช่น http://localhost:3000 เพื่อให้ Keycloak ส่ง Header Access-Control-Allow-Origin กลับมา หรือใส่เครื่องหมาย + เพื่อดึงทุกโดเมนจาก Valid Redirect URIs มาใช้อัตโนมัติ ใส่เฉพาะ Origin (ไม่มี Path)
Valid Post-Logout Redirect URIs ปลายทางหลังออกจากระบบ URL ที่ Keycloak อนุญาตให้ส่งผู้ใช้กลับไปเมื่อกด Logout สำเร็จ เช่น http://localhost:3000/login ป้องกันการถูกหลอกส่งไปเว็บมิจฉาชีพหลัง Logout ต้องตรงกับ URL แอปจริง
Standard Flow Enabled การเปิดใช้ Auth Code Flow เปิดใช้งานสำหรับแอปที่มีผู้ใช้ล็อกอินผ่านหน้าจอเบราว์เซอร์ (ทั้ง SPA, Web, และ Mobile) มาตรฐานหลักของ OIDC
Direct Access Grants Resource Owner Password (ROPC) โฟลว์ดั้งเดิมที่แอปขอ Username และ Password จากผู้ใช้ตรงๆ แล้วยิงส่งไปให้ Keycloak Legacy / Deprecated (ควรปิด)
Mental Model #04 ภาพเปรียบเทียบเชิงกายภาพในชีวิตประจำวัน

The Open Bicycle vs. The Armored Diplomatic Limousine

เปรียบเทียบผู้ใช้ (User) เสมือน "เจ้าหน้าที่เอกอัครราชทูต" ที่ต้องการเดินทางไปติดต่องานที่ด่านตรวจกลาง (Keycloak IdP):
• Public Client (เช่น React SPA / Flutter App) เปรียบเสมือน "จักรยานเปิดประทุน (Open Bicycle)" ที่วิ่งอยู่กลางถนนสาธารณะ ใครเดินผ่านก็มองเห็นทุกสิ่ง ไม่มีตู้เซฟเก็บของลับ หากคุณเอาตราประทับลับ (Client Secret) ไปใส่ไว้ในตะกร้าหน้ารถ โจรข้างทางก็ขโมยไปได้ทันที! ดังนั้นจักรยานจึงต้องใช้ระบบลายนิ้วมือเฉพาะกิจชั่วคราว (PKCE) ในการผ่านด่าน
• Confidential Client (เช่น Node.js / Spring Boot Backend) เปรียบเสมือน "รถลีมูซีนหุ้มเกราะทึบ (Armored Limousine)" วิ่งอยู่ในอุโมงค์ใต้ดินส่วนตัว มีตู้เซฟรหัสลับที่คนภายนอกมองไม่เห็น จึงสามารถเก็บและยื่นตราประทับลับ (Client Secret) เพื่อยืนยันตัวตนกับด่านตรวจได้อย่างปลอดภัย 100%!

KEYCLOAK CLIENT SECURITY & NETWORK BOUNDARIES PUBLIC CLIENT (SPA) clientId: "frontend-spa" Execution Environment: • Web Browser (Chrome/Safari) ❌ NO CLIENT SECRET (Client Auth=OFF) Security Guards Required: ✅ PKCE with S256 (RFC 7636) ✅ Exact Valid Redirect URIs ✅ Web Origins CORS Whitelist Origin: http://localhost:3000 ⚠️ อัตราย: ห้ามใส่ Secret ถ้าฝัง Secret ใน JS โค้ดจะถูกดูด ผ่าน F12 Inspect ได้ทันที 100% Keycloak IdP Server Realm: "app-dev" Client Validation Engine 1. Check: Is redirect_uri matching exact whitelist? 2. Check: Is Origin allowed in Web Origins? 3. Check: If Public, is PKCE S256 present? 4. Check: If Confidential, verify Client Secret CORS Response Headers: Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Credentials: true Web Origins Shortcut: ใส่ "+" = ดึง Redirect URIs มาทำ CORS อัตโนมัติ CONFIDENTIAL CLIENT clientId: "backend-service" Execution Environment: • Node.js / Java / Go Backend Server • Private VPC / Kubernetes Pod ✅ HAS CLIENT SECRET (Client Auth=ON) Secret stored in Environment / Vault Backchannel Capabilities: • Can call /token with Secret directly • Supports Client Credentials (M2M) • Protected from Browser Inspections 🛡️ High Trust Security Level Credentials rotated via Secret Manager
Case Study #04 Security Incident Post-Mortem

The $2.1M Open Redirect & Wildcard Callback Account Takeover Incident

Direct Financial Loss
$2,100,000

มูลค่าความเสียหายจากการถูกแฮกบัญชีลูกค้า VIP 1,400 ราย และค่าชดเชยด้านความปลอดภัย

Vulnerability Vector
Wildcard Redirect URI & Chained Open Redirect

ตั้งค่า Valid Redirect URIs เป็น https://app.corp.th/* ทำให้แฮกเกอร์ฉวยโอกาสเชื่อมต่อ Open Redirect ขโมย Code

Architectural Takeaway
Strict Exact Matching & Public Client Discipline

ห้ามใช้ Wildcard ใน Redirect URI โดยเด็ดขาด, ระบุ Exact Path เท่านั้น และห้ามใส่ Client Secret ในแอป SPA

สถานการณ์จริง: บริษัท Fintech ผู้ให้บริการกระเป๋าเงินดิจิทัลแห่งหนึ่ง ได้พัฒนา Single Page Application (React) เพื่อให้ลูกค้าเข้าถึงพอร์ตการลงทุน แต่ในขั้นตอนการลงทะเบียน Client บน Keycloak วิศวกรเกิดความรำคาญที่ต้องมาคอยเพิ่ม URL สำหรับหน้าเพจย่อยต่างๆ จึงตั้งค่าในช่อง Valid Redirect URIs แบบผ่อนปรนเป็น: https://wallet.fintech.th/*

ผู้โจมตีได้ค้นพบว่า บนเว็บไซต์ของบริษัทมีหน้าเว็บบอร์ดเก่าหน้าหนึ่งที่มีช่องโหว่ Open Redirect คือ https://wallet.fintech.th/forum/outbound?url=... ที่ยอมรับพารามิเตอร์ URL ปลายทางใดๆ ก็ได้โดยไม่มีการตรวจสอบ ผู้โจมตีจึงสร้างลิงก์หลอกลวง (Phishing Link) ที่ส่งคำขอ Login ไปยัง Keycloak โดยระบุค่า: redirect_uri=https://wallet.fintech.th/forum/outbound?url=https://attacker-server.com/steal

เนื่องจาก Keycloak ถูกตั้งค่าแบบ Wildcard (https://wallet.fintech.th/*) Keycloak จึงมองว่า URL นี้ถูกต้องตามกฎ! เมื่อเหยื่อกรอกรหัสผ่านและผ่าน 2FA บนหน้า Keycloak สำเร็จ Keycloak จึง 302 Redirect พร้อมแนบ code=AUTH_CODE_SECRET ไปยังหน้าเว็บบอร์ด และหน้าเว็บบอร์ดก็ Forward ค่า Authorization Code ทั้งหมดตรงเข้าเซิร์ฟเวอร์ของแฮกเกอร์ทันที! แฮกเกอร์จึงนำ Code นั้นไปแลก Token แล้วเข้าควบคุมกระเป๋าเงินของเหยื่อได้ทั้งหมด

Defense Argument สำหรับคุยกับทีม: "ในโลกของ Keycloak กฎความปลอดภัยข้อสำคัญคือ Valid Redirect URIs คือเกราะป้องกันการสกัดกั้น Token (Token Interception Shield) ห้ามใช้เครื่องหมายดอกจัน Wildcard เช่น https://domain/* หรือ * เป็นอันขาด! มาตรฐาน OAuth 2.1 บังคับให้ใช้ Exact String Matching (เช่น https://wallet.fintech.th/callback) เท่านั้น และสำหรับ Web SPA ให้ตั้ง Client Authentication = OFF คู่กับ PKCE เสมอ!"
Hands-on Breakdown แกะโครงสร้างคำสั่ง CLI & ลงมือสร้างจริง 3 เลเวล

Anatomy of Client Creation via kcadm.sh CLI

การสร้าง Client สามารถสั่งงานผ่าน Keycloak Admin CLI (kcadm.sh) เพื่อสร้างระบบอัตโนมัติ (DevOps Automation) ได้ทั้ง Public Client และ Confidential Client:

LEVEL 1: Provisioning a Public SPA Client with Exact URI & Web Origins Public Client

สร้าง Public Client สำหรับ React Single Page Application โดยระบุ Exact Redirect URI และเปิด CORS Web Origins

# 1. เข้าสู่ Container และล็อกอินผ่าน kcadm
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh config credentials \
  --server http://localhost:8080 --realm master --user superadmin --password AdminSuperSecret2026!

# 2. สร้าง Public Client ชื่อ 'frontend-spa' ใน Realm 'app-dev'
# ข้อสังเกต: publicClient=true (ไม่มี Secret) และระบุ exact redirectUris
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh create clients -r app-dev \
  -s clientId=frontend-spa \
  -s name="Customer React Web SPA" \
  -s enabled=true \
  -s publicClient=true \
  -s standardFlowEnabled=true \
  -s directAccessGrantsEnabled=false \
  -s 'redirectUris=["http://localhost:3000/callback"]' \
  -s 'webOrigins=["http://localhost:3000"]'

# 3. ตรวจสอบข้อมูล Client ที่เพิ่งสร้าง
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh get clients -r app-dev -q clientId=frontend-spa --fields id,clientId,publicClient,redirectUris
LEVEL 2: Provisioning a Confidential Client with Auto-Generated Client Secret ⚡ Confidential Client

สร้าง Confidential Client สำหรับ Backend Node.js/Spring Boot ที่มี Client Secret ในตัว พร้อมดึงคีย์ลับออกมาเพื่อนำไปใส่ใน Environment Variables

# 1. สร้าง Confidential Client ชื่อ 'backend-api' (publicClient=false)
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh create clients -r app-dev \
  -s clientId=backend-api \
  -s name="Backend API Service" \
  -s enabled=true \
  -s publicClient=false \
  -s clientAuthenticatorType=client-secret \
  -s standardFlowEnabled=true \
  -s serviceAccountsEnabled=true \
  -s 'redirectUris=["https://api.corp.th/oauth2/callback"]'

# 2. ดึง Internal ID ของ Client
CLIENT_ID_UUID=$(docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh get clients -r app-dev -q clientId=backend-api --fields id --format csv | tail -n 1 | tr -d '\r')

# 3. ดึง Client Secret ที่ Keycloak สร้างให้โดยอัตโนมัติ
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh get clients/$CLIENT_ID_UUID/client-secret -r app-dev

# 4. ทดสอบ regenerate (หมุนเวียน Secret ใหม่)
docker exec -it keycloak-core /opt/keycloak/bin/kcadm.sh create clients/$CLIENT_ID_UUID/client-secret -r app-dev
LEVEL 3: Architectural Client Hardening (Anti-Pattern vs Production-Ready) Security Architecture
❌ Flawed & Dangerous Setup
  • ใส่ Wildcard ใน Valid Redirect URIs เช่น https://myapp.com/* หรือ *
  • ตั้งแอป SPA เป็น Confidential Client แล้วฝัง client_secret ไว้ในไฟล์ .env ของ React
  • ใส่เครื่องหมาย * ใน Web Origins (CORS) ยอมรับการยิง Request จากทุกโดเมนในโลก
  • เปิดใช้งาน Direct Access Grants (ROPC) ทิ้งไว้โดยไม่จำเป็น
  • ไม่กำหนด Valid Post-Logout Redirect URIs ปล่อยให้ Redirect ไปที่ใดก็ได้
# เสี่ยง: เสี่ยงต่อ Open Redirect, Token Extraction ผ่าน XSS, และการโจมตีข้ามไซต์ (CSRF)
✅ Production-Grade Hardened Client
  • ระบุ Exact Valid Redirect URIs เช่น https://app.corp.th/callback เท่านั้น
  • SPA และ Mobile ต้องตั้งเป็น Public Client (Client Auth = OFF) คู่กับ PKCE S256 เสมอ
  • ระบุ Exact Web Origins เช่น https://app.corp.th หรือใช้เครื่องหมาย +
  • ปิด Direct Access Grants และเปิดเฉพาะโฟลว์ที่จำเป็น
  • กำหนด Valid Post-Logout Redirect URIs ให้ตรงกับหน้า Login ของระบบจริง
# ปลอดภัยสูง: สอดคล้องกับมาตรฐาน OAuth 2.1 และคำแนะนำของ OWASP Security BCP
Simulator #04 Live Client Security & Redirect URI Matcher Sandbox

Redirect URI Pattern Matcher & CORS Web Origins Sandbox

ทดลองป้อนการตั้งค่า Client บน Keycloak และจำลองคำขอ Login Request จากผู้ใช้ เพื่อดูว่ากลไกการจับคู่ URL และ CORS Preflight ตรวจจับการโจมตีแบบ Real-time อย่างไร:

ตัวอย่างแบบปลอดภัย: Exact Match | ลองพิมพ์แบบมี * ดูเพื่อทดสอบ
ค่าที่เบราว์เซอร์ส่งมาใน GET /auth?redirect_uri=...
โดเมนที่ได้รับอนุญาตให้ส่ง AJAX Header มา (หรือพิมพ์ + เพื่อดึงจาก URI)
URI Matching Verdict
ALLOWED
Redirect Authorized
CORS Preflight Status
PASS (200 OK)
Origin Whitelisted
Security Evaluation & Guardrail Alert SECURE MATCH

ยอดเยี่ยม! การจับคู่ Redirect URI เป็นแบบ Exact Match 100% ป้องกันการทำ Open Redirect ได้สมบูรณ์แบบ

Key takeaway: หากกรอก Redirect URI ไม่ตรงกับที่ลงทะเบียนไว้ Keycloak จะแสดงหน้าจอ "Invalid parameter: redirect_uri" ทันทีและปฏิเสธการพาผู้ใช้ไปหน้าล็อกอิน เพื่อปกป้องไม่ให้แฮกเกอร์ขโมย Authorization Code!
Active Recall Comprehensive Exam 12 Production Scenarios (100% Constructive Alignment)

Day 4 Clients Anatomy & Network Boundaries Exam

ครอบคลุม Public vs Confidential Clients, Redirect URI Security, CORS Web Origins พร้อมข้อสอบ Spaced Repetition จาก Day 1, Day 2 และ Day 3

Live Score
0/12 answered
0 / 12
🎯

Day 4 Practical Deliverable (ภารกิจประจำวัน)

ก่อนก้าวไปสู่ Day 5 (Service-to-Service Security & Client Credentials Grant) คุณต้องทำภารกิจต่อไปนี้ให้สำเร็จบน Keycloak ของคุณ: