บทที่ 14 · Part 3 — API & Mobile Security
OAuth, OIDC & Service Authentication
Authorization Code + PKCE, OIDC, JWT/opaque token, BFF, refresh rotation, mTLS/DPoP, workload identity และ FAPI 2.0
mobile application ที่ฝัง client_secret ใน binary ไม่ได้เป็น confidential client เพราะผู้ที่ดาวน์โหลด
application สามารถอ่าน secret ได้ หรือ API อาจรับ ID Token เป็น access token แล้วเปิด resource
เพราะ claim ดูคล้ายกัน ทั้ง 2 กรณีเกิดจากการใช้ artifact ผิดบทบาทใน protocol
Learning Outcomes
- แยก OAuth authorization, OIDC authentication และ API authorization ได้
- ออกแบบ Authorization Code + PKCE พร้อม state/nonce/redirect validation ได้
- เลือก public/confidential client และ browser BFF ตาม trust boundary ได้
- validate opaque/JWT access token และ OIDC ID Token ตามชนิดได้
- ป้องกัน refresh-token theft ด้วย rotation/reuse detection หรือ sender constraint ได้
- เลือก workload identity, private_key_jwt, mTLS, DPoP และ FAPI profile ตาม use case ได้
OAuth และ OIDC แก้คนละปัญหา
| Protocol/artifact | หน้าที่ |
|---|---|
| OAuth | delegated authorization เพื่อให้ client ขอ access token ไปเรียก resource server |
| OpenID Connect | authentication/federation layer บน OAuth ผ่าน ID Token และ UserInfo |
| Access token | credential สำหรับ resource server ตาม audience/scope |
| ID Token | assertion เกี่ยวกับ authentication event สำหรับ OIDC client |
| Refresh token | credential ขอ access token ใหม่จาก authorization server |
client ไม่ควรอ่าน access token เพื่อสรุป identity เว้นแต่ profile กำหนด และ resource server ไม่ควรรับ ID Token แทน access token
Actors และ Trust
- Resource Owner / End User — ผู้อนุญาต access
- Client — application ที่ขอ token
- Authorization Server (AS) — authenticate/authorize และออก token
- Resource Server (RS) — API ที่ตรวจ token และบังคับ resource authorization
OAuth ให้สิทธิ์ตาม delegation ไม่ได้บอกว่า subject ทำธุรกรรมใดก็ได้ Resource server ยังต้องตรวจ subject, scope, audience, object, tenant, state และ policy
Authorization Code + PKCE
RFC 9700 แนะนำ Authorization Code และใช้ PKCE กับทุก client type ที่เหมาะสม; Resource Owner Password Credentials grant ถูกห้าม และ implicit grant ไม่ควรถูกใช้ใน design ใหม่
PKCE:
code_verifier = high-entropy random value held by client
code_challenge = BASE64URL(SHA256(code_verifier))
method = S256
authorization server ผูก code กับ challenge และแลก code ได้เมื่อ verifier ตรง PKCE ลด code interception/injection แต่ไม่ authenticate public client, ไม่ sender-constrain access token, ไม่แก้ XSS และไม่แทน redirect/state validation
State, Nonce และ PKCE ไม่แทนกัน
| Value | ปกป้องหลัก |
|---|---|
state | bind authorization response กับ client transaction / CSRF defense |
OIDC nonce | bind ID Token กับ authentication request และลด replay |
| PKCE verifier | ผู้แลก authorization code ต้องถือ secret ต่อ transaction |
ค่าต้องสุ่ม/ผูก transaction, short-lived, single-use และ compare อย่างปลอดภัย อย่าใส่ open redirect, PII หรือ state ทั้งก้อนโดยไม่มี integrity/confidentiality design
Redirect URI ต้อง Exact
- authorization server ใช้ exact registered redirect URI matching
- native app ใช้ claimed HTTPS/app link เมื่อ platform รองรับ หรือ loopback/custom scheme ตาม RFC 8252
- custom scheme อาจถูก application อื่น claim จึงต้องใช้ PKCE และ platform binding
- native authorization ใช้ external user-agent ไม่ใช้ embedded WebView
- client ตรวจ issuer/mix-up defenses ตาม protocol/profile
การ allow wildcard/subdomain/path กว้างทำให้ authorization code/token ถูกส่งไป endpoint ที่ attacker ควบคุม และ open redirect ใต้ allowlisted domain ยังเป็นความเสี่ยง
Public กับ Confidential Client
| Client | รักษา long-lived credential ได้ | ตัวอย่าง |
|---|---|---|
| Public | ไม่ได้ | mobile/native app, code ใน browser |
| Confidential | ได้ภายใต้ server boundary | backend service, server-side web/BFF |
secret ที่ฝังใน mobile/browser bundle เป็น public information ใช้ client_id ระบุ application ได้
แต่ห้ามถือ client secret นั้นเป็น authentication proof
Browser-Based Application และ BFF
RFC 10017 อธิบาย 3 pattern และให้ Backend-for-Frontend (BFF) เป็น pattern ที่ปลอดภัยที่สุด ในชุดนั้นสำหรับ application ที่จัดการ sensitive/personal data:
BFF ลด token exposure ต่อ browser JavaScript แต่เพิ่ม server/session/CSRF/scale responsibility และ XSS ยังสั่ง action ผ่าน authenticated BFF ได้ จึงต้องมี output/CSRF/authorization controls
Token Scope, Audience และ Resource
access token ควร:
- มี audience/resource server ที่เจาะจง
- scope/authorization details แคบเท่าที่ต้องใช้
- lifetime สอดคล้องกับ theft/revocation risk
- ไม่รวมหลาย audience โดยไม่จำเป็น
- ไม่ใส่ข้อมูลละเอียดที่ consumer ไม่ต้องเห็น
resource server ต้องตรวจ audience ก่อน scope และตรวจ object/action/state ต่อ request
scope transfers:write ไม่ได้อนุญาตให้โอนจากทุก account
Opaque Token กับ JWT
Opaque token มัก introspect กับ AS ส่วน JWT อาจ validate local ได้ Trade-off:
| Concern | Opaque | JWT |
|---|---|---|
| Client visibility | ไม่มี claim ให้ตีความ | payload อ่านได้ |
| Revocation/state | central/introspection ง่ายกว่า | มัก valid ถึง expiry ถ้าไม่มี state เพิ่ม |
| Availability/latency | พึ่ง introspection/cache | local validation |
| Key/claim complexity | อยู่ AS มากกว่า | ทุก RS ต้อง validate ถูกและ rotate key |
OAuth client ควร treat access token เป็น opaque แม้ format เป็น JWT เพื่อไม่ผูกกับ implementation
JWT Access Token Validation
เมื่อ profile กำหนด JWT access token resource server ต้อง:
- pin algorithm และ key source
- verify signature/crypto input
- ตรวจ exact issuer, intended audience และ expiry/not-before
- ตรวจ token type เช่น
typ=at+jwtตาม RFC 9068 profile - ใช้ mutually exclusive validation rules ต่อ token type
- ไม่ตาม
jku/x5uURL อิสระจาก token - ตรวจ scope/claim แล้วทำ authorization กับ current resource state
JWT signature ไม่ให้ confidentiality และ token ไม่ได้ revocable โดยธรรมชาติ
OIDC ID Token Validation
OIDC client ตรวจอย่างน้อย:
issตรง issuer ที่เริ่ม transactionaudมี client ID และจัดการหลาย audience ตาม spec- signature/key/algorithm ตาม metadata/policy
expและ time claimsnonceตรงเมื่อถูกส่งใน request- authorization code flow binding อื่นตาม OIDC/profile
ID Token ใช้กับ client authentication session ไม่ส่งไป resource API เป็น bearer access token
Refresh Token Theft
สำหรับ public client ที่ได้รับ refresh token RFC 9700 กำหนดให้ sender-constrain หรือใช้ refresh-token rotation เพื่อตรวจ replay
rotation flow:
- R1 แลก token สำเร็จ → ออก R2 และ mark R1 used
- R1 ถูกใช้อีก → สันนิษฐาน token copy/compromise
- revoke active token family/grant ตาม policy
- บังคับ authorization ใหม่และสร้าง telemetry
rotation ต้องเป็น atomic และมี family state ไม่ใช่เพียงออกค่าใหม่ ส่วน confidential/sender-constrained profile อาจมี requirement ต่างกัน ต้องไม่ผสมคำแนะนำต่าง profile โดยไม่ดู assumptions
Sender-Constrained Tokens
| Mechanism | Bind token กับ | จุดระวัง |
|---|---|---|
| OAuth mTLS | client certificate/private key | certificate lifecycle, proxy identity, key protection |
| DPoP | application key + per-request proof | replay cache/nonce, endpoint normalization, key storage |
DPoP proof ผูก key, HTTP method/URI และ token hash ตาม protocol แต่ไม่ sign request body/headers ส่วนใหญ่ จึงไม่ใช่ transaction signing ของ amount/beneficiary และทั้งคู่ไม่แทน TLS/authorization
Service-to-Service Authentication
ลำดับ preference ตาม platform/threat model:
- workload identity + short-lived token/role
- OAuth Client Credentials ด้วย asymmetric client auth
private_key_jwtหรือ mTLS- managed API key/HMAC เมื่อ protocol/partner จำกัด พร้อม rotation/replay controls
- หลีกเลี่ยง shared long-lived secret ที่กระจายหลาย service
service identity ต้องมี owner, environment/audience, least privilege, rotation/revoke, telemetry และ blast radius แยกจาก end-user delegation อย่าให้ backend service แอบกลายเป็น global admin
FAPI 2.0 สำหรับ High-Value APIs
FAPI 2.0 Security Profile เป็น OpenID Final profile สำหรับ high-security APIs และ confidential clients โดยรวม Authorization Code, PKCE S256, client-authenticated PAR, issuer checks, asymmetric client authentication และ sender-constrained access tokens
ไม่ควรหยิบ requirement บางข้อออกมาโดยไม่ใช้ profile assumptions/interoperability tests ทั้งชุด และการใช้ FAPI ไม่แทน transaction authorization, fraud control, data protection หรือ compliance review
OAuth 2.1 ยังไม่ใช่ Final Standard
ณ baseline ของคอร์ส OAuth 2.1 ยังเป็น IETF Internet-Draft จึงอ้าง RFC 9700 และ RFC ที่ final เป็น normative source พร้อมระบุ draft status เมื่อกล่าวถึง OAuth 2.1
Common Failures
- ใช้ implicit หรือ password grant ใน design ใหม่
- mobile/browser ฝัง client secret แล้วถือว่า confidential
- redirect URI wildcard/open redirect
- ไม่ผูก state/nonce/PKCE กับ transaction เดียวกัน
- resource server ไม่ตรวจ audience หรือรับ ID Token
- JWT เลือก algorithm/key URL จาก untrusted header
- refresh token ไม่มี rotation/reuse detection หรือ sender constraint
- token มี scope/audience/expiry กว้างเกิน
- DPoP/mTLS ถูกอ้างว่า sign transaction body
- log authorization code/access/refresh/ID token
Testing Checklist
- OAuth, OIDC, access token และ ID Token ถูกใช้ตามบทบาท
- Authorization Code + PKCE S256; ไม่มี implicit/ROPC ใน design ใหม่
- state/nonce/verifier single-use, short-lived และ bind transaction
- redirect URI exact และ native app ใช้ external user-agent
- public client ไม่มี embedded secret ที่ใช้เป็น proof
- browser architecture ประเมิน BFF/session/CSRF/XSS trade-off
- RS ตรวจ issuer/audience/type/time/scope และ resource authorization
- refresh token มี rotation+reuse detection หรือ sender constraint ตาม profile
- mTLS/DPoP มี key/replay lifecycle และไม่ถูกอ้างว่า sign body
- service credential เป็น short-lived, scoped, revocable และ auditable
สรุป
OAuth/OIDC security ขึ้นกับการใช้ artifact ตามบทบาท, transaction binding และ validation ครบ PKCE ป้องกัน code flow บาง threat, sender constraint ลด bearer theft และ FAPI กำหนด profile สำหรับ high-value API แต่ resource authorization และ transaction integrity ยังต้องออกแบบแยก