บทที่ 8 · Part 2 — Identity & Web Security
Authorization & Access Control
RBAC, ABAC, ReBAC, BOLA/IDOR, function/property authorization, multi-tenant isolation และ maker-checker สำหรับ privileged action
API อาจตรวจ access token และ role ถูกต้องทุก request แต่ยังเปิด statement ของ account อื่นได้
เมื่อเปลี่ยน account_id ใน URL ปัญหาไม่ได้อยู่ที่ Authentication เพราะ identity ถูกต้อง
แต่อยู่ที่ระบบไม่ตรวจความสัมพันธ์ระหว่าง identity, action และ resource
Learning Outcomes
- เขียน authorization decision ด้วย subject, action, resource และ context ได้
- เลือกและผสม RBAC, ABAC, ReBAC ตามโดเมนได้
- ป้องกัน object-, function- และ property-level authorization failure ได้
- ออกแบบ Policy Decision Point และ Enforcement Point แบบ default deny ได้
- ทดสอบ multi-tenant isolation และ maker-checker ที่รักษา transaction integrity ได้
Authentication ไม่ได้ให้สิทธิ์อัตโนมัติ
authorization decision ที่ครบควรตอบ:
Can SUBJECT perform ACTION on RESOURCE under CONTEXT and CURRENT STATE?
ตัวอย่าง:
subject = operator:8472
action = payout:approve
resource = payout-batch:pb_01J...
context = tenant, amount band, device assurance, current time
state = batch version 7, status PENDING_APPROVAL, creator != subject
การตรวจเพียง role == admin ขาด resource, tenant, state และ purpose ทำให้ privilege กว้างเกิน
Access Control Models
| Model | Policy อาศัย | เหมาะกับ | จุดระวัง |
|---|---|---|---|
| RBAC | role/job function | coarse-grained workforce permissions | role explosion และ role กว้างเกิน |
| ABAC | subject/resource/environment attributes | amount, tenant, risk, data class | attribute authority/freshness และ policy ซับซ้อน |
| ReBAC | relationship graph | owner, member, delegated access | graph consistency, traversal และ revocation |
ระบบจริงมักผสมกัน เช่น role support-agent เปิด ticket ได้ แต่ดู account ได้เฉพาะ tenant ที่ assigned
และเฉพาะเมื่อมี relationship กับ case ที่ active
เริ่มจาก Domain Action
permission อย่าง database:write หรือ api:call กว้างและไม่สื่อ business invariant
ชื่ออย่าง refund:request, refund:approve, statement:read-masked review และ audit ได้ชัดกว่า
Default Deny และ Complete Mediation
- endpoint/action ใหม่ต้อง deny จนมี policy ชัด
- backend ตรวจทุก request ไม่เชื่อว่า frontend ซ่อนปุ่มแล้ว
- internal service ไม่เชื่อเพียง network location
- authorization ถูกตรวจอีกครั้งใกล้ side effect เมื่อ state เปลี่ยนได้
- deny/error path ไม่ fallback เป็น allow เมื่อ policy service timeout
PDP ตัดสิน policy ส่วน PEP บังคับผล ทั้ง 2อาจอยู่ library/service เดียวกัน แต่ ownership,
failure semantics, cache และ telemetry ต้องชัด indeterminate ไม่ควรถูกตีเป็น allow
Three Authorization Layers
Object-Level: BOLA และ IDOR
request นี้อ่าน object ชิ้นใด ได้หรือไม่:
GET /accounts/acc_02/statements
Authorization: Bearer <token>
token ที่ valid ไม่พิสูจน์ ownership ของ acc_02 UUID ที่เดายากลด enumeration แต่ไม่ใช่ control
query ควร scope ด้วย subject/tenant/relationship หรือ authorize object หลัง load อย่างสม่ำเสมอ
Function-Level
subject เรียก operation นี้ ได้หรือไม่ เช่น user endpoint กับ admin endpoint การเปลี่ยน HTTP method/path หรือเรียก internal route โดยตรงต้องไม่ข้าม policy
Property-Level
subject อ่านหรือเขียน field ใด ได้หรือไม่ ปัญหาที่พบบ่อย:
- response ส่ง field ลับแล้วหวังให้ frontend ซ่อน
- mass assignment รับ
role,status,approved_byจาก JSON - support เห็น account number เต็มแทน masked view
- PATCH อนุญาตเปลี่ยน owner/tenant โดยไม่ตั้งใจ
ใช้ explicit request/response schema ต่อ use case ไม่ bind domain/admin model ทั้งก้อน
Enforcement ต้องอยู่ใกล้ Data
รูปแบบ query ที่เสี่ยง:
SELECT * FROM statements WHERE account_id = $1;
รูปแบบที่ scope tenant/ownership ใน query ช่วยลดช่องว่างระหว่าง check กับ use:
SELECT s.*
FROM statements s
JOIN account_members m ON m.account_id = s.account_id
WHERE s.account_id = $1
AND m.subject_id = $2
AND m.tenant_id = $3;
ยังต้องพิจารณา role/action และ database policy ตาม architecture แต่หลักคืออย่า load object ด้วย untrusted ID แล้วลืมตรวจ relationship
Multi-Tenant Isolation ครอบคลุมมากกว่า API
| Layer | ตัวอย่าง failure |
|---|---|
| Query/database | ลืม tenant predicate หรือใช้ connection context ผิด |
| Cache | cache key ไม่มี tenant ID |
| Object storage | signed URL/prefix policy ข้าม tenant |
| Search/index | filter tenant ถูกส่งจาก client และแก้ได้ |
| Queue/job | message ไม่มี trusted tenant context |
| Export/report | background report รวมข้อมูลหลาย tenant |
| Log/trace | support search เห็นข้อมูลทุก tenant |
| Admin tool | impersonation ไม่มี scope/approval/audit |
tenant context ต้องมาจาก trusted identity/resource mapping ไม่เชื่อ X-Tenant-ID จาก public client
โดยไม่ validate membership
Cache Authorization อย่างระวัง
policy cache key ต้องรวม subject, action, resource, tenant, relevant attributes และ policy version พร้อม TTL/invalidation ที่สอดคล้องกับ revoke expectation
การ cache is_admin=true นาน 1 วันทำให้ถอด role แล้วสิทธิ์ยังอยู่ การ cache deny/allow มี trade-off
ต่างกัน และ privileged action อาจต้องอ่าน current state สดเสมอ
Maker-Checker และ Separation of Duties
flow อนุมัติที่ปลอดภัยไม่ได้จบที่ “มี 2 role”:
ต้องตรวจว่า:
- requester และ approver เป็นคนละ independent identity
- role conflict/temporary delegation ถูกพิจารณา
- approval ผูกกับ immutable version/content hash
- เปลี่ยน amount/beneficiary แล้วกลับไปขอ approval ใหม่
- final execution ตรวจ state/policy อีกครั้งเพื่อกัน TOCTOU
- break-glass มีขอบเขต, notification และ post-review
Approval Threshold เป็น Human-Owned Policy
วงเงิน, จำนวน approver, cooling period และ exception มีผลต่อเงินและการปฏิบัติงาน code ควร enforce policy ที่ version/audit ได้ แต่ผู้มีอำนาจจากธุรกิจและ Risk/Compliance ต้องเป็นผู้อนุมัติ policy ไม่ใช่ให้ engineer เลือกจากตัวอย่าง
Authorization for Service Identities
workload identity ต้องมีสิทธิ์แคบเหมือน human identity:
- service A เรียก action ใดของ service B
- acting as end user หรือทำในนามระบบ
- delegated subject/context ถูกพิสูจน์และลดสิทธิ์อย่างไร
- batch/queue message รักษา tenant/resource context อย่างไร
- credential compromise มี blast radius และ revoke path ใด
confused deputy เกิดเมื่อ service ที่มี privilege ถูกหลอกให้ใช้สิทธิ์แทน caller โดยไม่ตรวจ resource/tenant/delegation context
Decision Logging
บันทึก subject, action, resource reference, tenant, policy version, outcome/reason, correlation ID และ enforcement point โดยไม่เก็บ raw token หรือข้อมูลเกินจำเป็น
deny log มี volume สูงและ attacker สร้าง noise ได้ จึงต้อง sampling/aggregation อย่างระวัง แต่ privileged allow/deny และ policy change ควรมี evidence ครบพร้อม alert ตาม risk
Test เป็น Matrix ไม่ใช่ Happy Path
| Dimension | Cases |
|---|---|
| Subject | owner, member, non-member, suspended, service |
| Action | read, create, update, approve, delete, export |
| Resource | own, same tenant, other tenant, missing, stale version |
| Property | allowed field, hidden field, mass-assigned field |
| State | draft, pending, approved, executed, closed |
| Context | assurance, device, time, delegated/break-glass |
เพิ่ม tests สำหรับเปลี่ยน object ID, method/path, nested ID, bulk endpoint, cache hit, async job, concurrent state change และ policy service failure
Review Checklist
- decision ระบุ subject-action-resource-context-current state
- backend enforce ทุก operation ด้วย default deny
- RBAC/ABAC/ReBAC มี trusted source และ lifecycle ของ role/attribute/relationship
- object/function/property authorization ถูกทดสอบแยกกัน
- query/cache/storage/queue/export/log/admin ครอบคลุม tenant isolation
- policy cache key/version/TTL สอดคล้องกับ revocation
- approval ผูกกับ transaction version และ independent actor
- service identity/delegation ป้องกัน confused deputy
- deny/indeterminate/failure ไม่กลายเป็น allow
- decision log มี policy version/outcome โดยไม่มี raw credential
สรุป
Authorization คือการตัดสินความสัมพันธ์ระหว่าง identity, action, resource, context และ state ทุกครั้งที่เกิด side effect Authentication, UUID ที่เดายาก, network ภายใน และการซ่อน UI ไม่สามารถแทน server-side authorization แบบ default deny ได้