บทที่ 7 · Part 2 — Identity & Web Security
Session & Token Security
Cookie, bearer token, JWT/opaque token, fixation, hijacking, timeout, rotation, revocation และ refresh-token reuse detection
หลัง authentication สำเร็จ ระบบมักไม่ขอ passkey หรือ password ทุก request แต่สร้าง session secret ให้ client ถือไว้ ผู้ที่ขโมย secret นี้ได้จึงอาจใช้งานแทนเจ้าของบัญชีจนกว่า session จะหมดอายุ แม้ไม่เคยรู้ credential หลัก Session security จึงเป็นส่วนต่อเนื่องของ Authentication
Learning Outcomes
- แยก server-side session, opaque token และ JWT พร้อม trade-off ได้
- ตั้ง cookie attributes และ storage boundary สำหรับ browser ได้
- ป้องกัน session fixation, hijacking และ CSRF แบบหลายชั้นได้
- ออกแบบ timeout, rotation, logout และ revocation ตาม risk ได้
- ตรวจ refresh-token reuse และจัดการ session หลัง sensitive event ได้
Session Secret คือ Authenticator ชั่วคราว
session secret แบบ bearer หมายถึงผู้ที่ถือค่าก็ใช้ได้ จึงต้องมี entropy สูง, สร้างจาก CSPRNG, ส่งผ่าน protected channel, ไม่อยู่ใน URL/log และถูก revoke ได้
| Model | State อยู่ที่ใด | จุดแข็ง | Trade-off |
|---|---|---|---|
| Server-side session | server เก็บ state; client ถือ opaque ID | revoke/change state ง่าย, client ไม่เห็น claim | ต้องมี session store และ availability design |
| Opaque access token | authorization server/introspection | ซ่อน claim และควบคุม lifecycle กลาง | introspection/cache dependency |
| JWT access token | claim/signature อยู่ใน token | resource server validate local ได้ | revocation, key/claim/type confusion และข้อมูลอ่านได้ |
JWT ไม่ได้ปลอดภัยกว่า opaque token โดยธรรมชาติ และ signed JWT ไม่ได้เข้ารหัส payload การเลือกขึ้นกับ trust boundary, latency, revocation, key lifecycle และ operational complexity
Browser Cookie ที่เป็น Secure Default
ตัวอย่าง cookie สำหรับ session reference:
Set-Cookie: __Host-session=<opaque>; Path=/; Secure; HttpOnly; SameSite=Lax
Cache-Control: no-store
Secureส่ง cookie ผ่าน HTTPS เท่านั้นHttpOnlyลดการอ่านจาก JavaScript แต่ XSS ยังสั่ง request ในนามผู้ใช้ได้SameSiteจำกัด cross-site sending ตาม mode และเป็น defense in depth ต่อ CSRF__Host-ต้องไม่มีDomain, มีPath=/และSecureลด scope confusion- cookie value ควรเป็น opaque ID ไม่ใช่ email, account number หรือข้อมูลส่วนบุคคล
กำหนด Domain/Path ให้แคบที่สุด อย่าแชร์ session cookie ให้ทุก subdomain หาก subdomain
มี owner หรือ content trust ต่างกัน
หลีกเลี่ยง Local Storage สำหรับ Session Secret
ค่าใน Local Storage อ่านได้จาก JavaScript ทุก script ใน origin รวม compromised dependency/XSS และไม่มี expiration/revocation semantics ในตัว สำหรับ browser application ที่มีข้อมูลละเอียดสูง ควรพิจารณา server-side session/BFF กับ HttpOnly cookie ตาม architecture
ป้องกัน Session Fixation
session fixation เกิดเมื่อ attacker กำหนดหรือรู้ session ID ก่อน authentication แล้วรอให้ผู้ใช้ login ด้วย ID เดิม ระบบต้อง rotate session identifier เมื่อ privilege เปลี่ยน:
rotate เมื่อ login, step-up, role/tenant switch, recovery หรือ sensitive privilege change และอย่าคัดลอก untrusted pre-login state เช่น redirect URL โดยไม่ validate
Session Hijacking และ Context Signals
การป้องกันหลายชั้นประกอบด้วย:
- TLS และ cookie/storage ที่เหมาะสม
- short-lived access + bounded session lifetime
- rotate หลัง privilege change
- revoke เมื่อ compromise/sensitive event
- XSS/CSRF defenses
- device/session inventory และ anomaly detection
- reauthentication สำหรับ high-risk action
การ bind session กับ IP แบบตายตัวทำให้ผู้ใช้ mobile/NAT หลุดและ attacker ใน network เดียวผ่านได้ IP, device และ location ควรเป็น risk signals ไม่ใช่ identity proof เพียงตัวเดียว
Timeout ต้อง Enforce ฝั่ง Server
timeout หลักมี 2 ชนิด:
- Inactivity timeout — ไม่มี activity นานเกินกำหนด
- Overall/absolute timeout — session อายุเกินเพดานไม่ว่ามี activity หรือไม่
NIST SP 800-63B-4 ให้ guideline สำหรับ AAL2 ว่า overall reauthentication ไม่เกิน 24 ชั่วโมง และ inactivity ไม่เกิน 1 ชั่วโมง แต่ไม่ควรคัดเป็นค่ากลางทุกระบบ ธุรกรรมการเงินอาจต้อง fresh authentication สั้นกว่านั้นตาม action/risk ขณะที่ availability/accessibility ก็ต้องถูกพิจารณา
client-side timer ใช้เตือน UI ได้ แต่ server ต้องเป็นผู้ตัดสิน session validity และ refresh request ไม่ควรยืด absolute lifetime โดยไม่จำกัด
SameSite ไม่ใช่ CSRF Defense ชั้นเดียว
browser แนบ cookie อัตโนมัติ จึงเกิด CSRF ได้เมื่อ site อื่นทำให้ browser ส่ง state-changing request แนวป้องกันตาม architecture:
- anti-CSRF token ที่ผูกกับ session
- ตรวจ
Origin/Sec-Fetch-Siteตาม trusted proxy/browser model SameSite=LaxหรือStrictเมื่อ flow รองรับ- state-changing operation ไม่ใช้ GET
- reauthentication/transaction confirmation สำหรับ action สำคัญ
XSS อาจอ่าน CSRF token หรือสั่ง request จาก origin จริงได้ จึงต้องแก้ XSS แยกต่างหาก
Access Token และ Refresh Token
access token ควรมี scope/audience/lifetime จำกัด ส่วน refresh token มีอำนาจขอ token ใหม่และ ต้องป้องกันมากกว่า โดยเฉพาะ public client
refresh-token rotation ที่ตรวจ reuse ต้องเก็บ token family/state ถ้า R1 ถูกใช้ซ้ำหลังออก R2 ให้ถือว่าอาจมีการขโมยและ revoke family ตาม policy Rotation ที่ไม่ตรวจ reuse เพียงเปลี่ยนค่า แต่ไม่สร้างสัญญาณ compromise
sender-constrained token เช่น mTLS/DPoP ลดการใช้ token ที่ขโมยโดยไม่มี key แต่ยังต้องมี TLS, audience validation, key protection และ authorization ซึ่งจะลงรายละเอียดในบท OAuth
JWT Verification ที่ต้องครบ
เมื่อ resource server รับ JWT ต้องอย่างน้อย:
- pin algorithm ที่ยอมรับ ไม่เชื่อ header ให้เลือกได้อิสระ
- verify signature ด้วย key/source ที่กำหนด
- validate exact issuer และ intended audience
- validate expiry/not-before ตาม policy และ clock handling
- แยก validation rules ของ ID token, access token และ token type อื่น
- จำกัด scope/claim และอย่าใช้ claim ที่ไม่มี authority เป็น authorization truth
- ไม่ตาม
jku/x5uURL อิสระจาก token
JWT payload เป็น base64url ที่อ่านได้ จึงห้ามใส่ secret/PII โดยคิดว่า signature เข้ารหัสข้อมูล
Logout และ Revocation Semantics
คำว่า logout ต้องระบุ scope:
| Event | สิ่งที่อาจต้อง revoke |
|---|---|
| Logout current device | current session + related refresh token |
| Logout all devices | session/token family ทั้ง subject ตาม policy |
| Password change | sessions ที่ assurance พึ่ง password เก่า |
| Authenticator recovery | old sessions/refresh tokens และ privileged grants |
| Account disable | interactive + API/workload delegation ที่เกี่ยวข้อง |
| Suspected theft | targeted device/session แล้วขยายตาม evidence |
access token แบบ self-contained อาจ valid จนหมดอายุแม้ revoke refresh token จึงต้องเลือก short lifetime, introspection/denylist หรือ event-driven invalidation ตาม impact/scale
Concurrent Sessions และ Device Management
หน้าจัดการ session ควรแสดงข้อมูลที่ช่วยตัดสินใจโดยไม่เปิดเผยเกินจำเป็น เช่น device label, เวลาล่าสุด, approximate location และ authentication time พร้อม revoke ราย session/ทั้งหมด
ข้อมูล device/IP ไม่ควรถูกนำเสนอว่าเป็นหลักฐานแน่นอน Notification สำหรับ new login, recovery และ authenticator change ควรมีช่องทาง report ที่นำไปสู่ containment จริง
Logging โดยไม่ทำ Token รั่ว
บันทึก session created/rotated/revoked, token validation failure, refresh reuse, step-up และ admin revocation ด้วย opaque identifiers/correlation แต่ห้าม log:
- raw cookie, access token, refresh token หรือ authorization code
- token signature/payload ทั้งก้อน
- CSRF secret และ recovery code
- sensitive URL query/referrer
token fingerprint สำหรับ correlation ควรออกแบบให้ย้อนใช้เป็น credential ไม่ได้และมี retention จำกัด
Testing Checklist
- session/token สร้างด้วย secure random และไม่อยู่ใน URL/log
- browser cookie ใช้
Secure,HttpOnly,SameSiteและ scope แคบ - session secret ไม่เก็บใน Local Storage โดยไม่มี threat-based decision
- session ID rotate หลัง login, step-up, recovery และ privilege change
- inactivity/absolute timeout enforce ฝั่ง server
- CSRF มี token/origin/SameSite ตาม architecture ไม่พึ่งชั้นเดียว
- refresh rotation ตรวจ reuse และ revoke token family ได้
- JWT pin algorithm และตรวจ issuer/audience/time/token type ครบ
- logout/password change/recovery มี revocation semantics ที่ระบุชัด
- session inventory/revoke และ telemetry ไม่เปิดเผย credential
สรุป
Session secret คือ authenticator ชั่วคราว การป้องกันต้องครอบคลุม generation, transport, storage, rotation, timeout, revocation และ detection ตลอด lifecycle Token ที่ verify ผ่านยังต้อง ถูกตรวจ audience, authorization และ current account/session state ตาม design