บทที่ 12 · Part 2 — Identity & Web Security

Browser, File & Content Security

XSS, CSRF, CSP, Trusted Types, clickjacking, open redirect, browser storage และ secure file pipeline สำหรับเอกสาร KYC

ข้อความ merchant ที่ถูกเก็บใน database อาจดูเป็น data ธรรมดา แต่เมื่อ operations portal นำไปใส่ innerHTML ข้อความนั้นกลายเป็น code ที่รันด้วย session ของ operator ส่วนไฟล์ KYC ที่ตรวจเพียง นามสกุล .jpg อาจเป็น document/parser payload ที่โจมตี worker หลังบ้านแทน browser

Learning Outcomes

  • แยก Reflected, Stored และ DOM-based XSS จาก source/sink ได้
  • ใช้ safe rendering, contextual encoding, sanitization, CSP และ Trusted Types ตามหน้าที่ได้
  • ป้องกัน CSRF, clickjacking และ open redirect แบบหลายชั้นได้
  • เลือก browser storage และ security headers ตาม data/credential boundary ได้
  • ออกแบบ upload/download pipeline ที่ตรวจ type, limit, isolate, authorize และ retain อย่างปลอดภัยได้

XSS คือ Data กลายเป็น Active Content

TypeSource → Execution
Reflectedrequest input ถูกใส่ response แล้ว execute ทันที
Storedpayload ถูกเก็บ แล้ว execute เมื่อผู้ใช้อื่นเปิดหน้า
DOM-basedclient-side code อ่าน source แล้วส่งเข้า dangerous DOM sink

ชื่อ type บอกเส้นทาง แต่ defense ต้องดู output context และ sink จริง

Safe และ Dangerous Sinks

// Safe for plain text: browser does not parse value as HTML.
statusElement.textContent = untrustedStatus

// Dangerous: value becomes HTML and may execute active content.
statusElement.innerHTML = untrustedStatus

framework template มัก escape text โดย default แต่ escape ถูก bypass เมื่อใช้ raw HTML API, string-built URL/event handler, third-party widget หรือ DOM API โดยตรง

Output Encoding ต้องตรง Context

Contextแนวทางหลัก
HTML textframework auto-escape / HTML entity encoding
Attributequoted attribute + attribute encoding + allowlist attribute
URLparse/allowlist scheme-host + encode component
JavaScriptหลีกเลี่ยง inline interpolation; serialize data safely
CSSไม่รับ untrusted CSS; ใช้ property/value allowlist

HTML encoding ค่าแล้วนำไปใส่ JavaScript context ยังไม่ปลอดภัย หลีกเลี่ยงการประกอบ code/string และใช้ structured API

Sanitization ใช้เมื่อจำเป็นต้องรับ HTML

บาง feature ต้องรองรับ rich text Sanitization ต่างจาก encoding: sanitizer parse HTML แล้วลบ element/attribute/protocol ที่ไม่อนุญาต

หลักปฏิบัติ:

  • ใช้ maintained sanitizer ที่เหมาะกับ browser/server context
  • กำหนด allowlist เล็กและไม่มี script/event/style/unsafe URL
  • sanitize หลัง canonical parse และก่อน sink
  • เก็บ original แยกจาก sanitized version ตาม privacy/forensic policy
  • re-sanitize เมื่อ policy/library เปลี่ยนถ้าข้อมูลเก่าถูก render
  • test mutation, SVG/MathML และ browser parsing edge cases ตาม library scope

ห้ามแก้ HTML ด้วย regex และห้าม modify sanitized output ด้วย string concatenation ที่ทำให้ unsafe อีกครั้ง

CSP เป็น Defense in Depth

Content Security Policy จำกัดแหล่งและรูปแบบ content/execution CSP ที่ใช้ nonce/hash สามารถลด ผลของ injection บางชนิด แต่ไม่แทน safe rendering

ตัวอย่าง policy เริ่มต้นที่ต้องปรับตาม application:

Content-Security-Policy: default-src 'none'; script-src 'nonce-random-per-response' 'strict-dynamic'; style-src 'self'; img-src 'self' data:; connect-src 'self' https://api.example.com; base-uri 'none'; object-src 'none'; frame-ancestors 'none'
  • nonce ต้องสุ่มต่อ response และ attacker คาดเดาไม่ได้
  • ห้ามใส่ nonce ให้ script ที่ attacker ควบคุม body/source ได้
  • unsafe-inline ทำให้ script policy อ่อนลง
  • host allowlist กว้างอาจมี JSONP/user content/bypass
  • เริ่มด้วย report-only เก็บ violation และปรับ ก่อน enforce
  • report endpoint ต้อง rate-limit/redact เพราะ report มี URL/data ได้

CSP Report ไม่ใช่หลักฐานว่าไม่มี XSS

browser coverage, extension, blocked report และ code path ที่ไม่ถูกใช้ทำให้ telemetry ไม่ครบ CSP ต้องทำงานร่วมกับ safe sink, encoding, sanitization และ test

Trusted Types ลด DOM XSS Sink

Trusted Types ช่วยบังคับ dangerous DOM sinks ให้รับ typed value ที่สร้างผ่าน policy แทน string ทั่วไป เหมาะกับ application ขนาดใหญ่ที่ต้องการ migrate DOM code

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-policy

policy factory ต้องใช้ sanitizer/validation จริง ไม่สร้าง policy ที่คืน input ตรง ๆ มิฉะนั้นเพียงย้าย ช่องโหว่ไปจุดกลาง และต้องตรวจ browser support/rollout

CSRF: Browser ส่ง Credential ให้อัตโนมัติ

เมื่อใช้ cookie session state-changing request ต้องมี CSRF control ตาม architecture:

  • synchronizer/double-submit token ที่ออกแบบถูก
  • ตรวจ Origin และ Fetch Metadata เมื่อเหมาะสม
  • SameSite เป็น defense in depth
  • ไม่ใช้ GET ทำ side effect
  • fresh authorization/confirmation สำหรับ action สำคัญ

XSS อาจสั่ง request จาก origin จริงและอ่าน CSRF token ได้ จึงต้องแก้ทั้ง 2 ปัญหา ดู browser/origin model ที่บท Web Security Model

Clickjacking และ Framing

attacker อาจวางหน้าธุรกรรมโปร่งใสทับ UI หลอกให้คลิก ป้องกันด้วย:

Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY

ถ้าต้อง embed ให้ allowlist origin ที่จำเป็นและออกแบบ postMessage/transaction confirmation อย่างปลอดภัย frame-ancestors เป็น control หลักสมัยใหม่ ส่วน X-Frame-Options ช่วย compatibility

Open Redirect และ Navigation

parameter เช่น return_to ที่รับ arbitrary URL ช่วย phishing และอาจรั่ว authorization code/token

แนวทาง:

  • ใช้ relative path หรือ server-side destination ID
  • parse URL แล้ว exact allowlist scheme/host/port/path ตาม use case
  • reject userinfo, alternate scheme, protocol-relative และ ambiguous encoding
  • OAuth redirect URI ต้อง exact match ตาม protocol
  • อย่าแนบ credential/token ไป destination ที่ client เลือก

warning page อย่างเดียวอาจถูกผู้ใช้คลิกผ่านและไม่แก้ token leakage

Browser Storage และ Client Data

Storageความเสี่ยง/แนวใช้
HttpOnly cookieJS อ่านไม่ได้ แต่ browser แนบอัตโนมัติและต้องทำ CSRF control
Local/Session StorageJS/XSS อ่านได้; ไม่มี HttpOnly; ไม่เหมาะ long-lived session secret
IndexedDBเก็บได้มากและ JS อ่านได้; ต้อง classify/expiry/clear
In-memoryลด persistence แต่ยังถูก XSS ใน runtime และหายเมื่อ reload
Cache/Service Workeroffline data/response อาจคงหลัง logout และแชร์ผิด scope

frontend ไม่ควรเก็บ KYC, statement หรือ token เกินที่ UX ต้องใช้ logout/account switch ต้อง clear client cache ที่เกี่ยวข้อง และ Cache-Control ฝั่ง HTTP ต้องตรง data class

Security Headers ที่ควรพิจารณา

Content-Security-Policy: <application-specific policy>
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Cross-Origin-Opener-Policy: same-origin

ค่าไม่ควรถูก copy ทั้งชุดโดยไม่ทดสอบ OAuth, embedded content, file download, customer support และ third-party integration Security header เป็น policy ของ browser ไม่แทน backend control

File Upload เป็น Pipeline ไม่ใช่ If Statement

แต่ละชั้นแก้คนละ threat:

  • authorize uploader และ business object
  • allowlist extension/type ตาม use case
  • ไม่เชื่อ Content-Type; ตรวจ signature/structure ด้วย parser ที่จำกัด
  • generate filename/object ID ไม่ใช้ชื่อ client เป็น storage path
  • จำกัด bytes, dimensions, pages, entries, recursion และ decompressed size
  • quarantine ก่อน scan และไม่ให้ execute/serve active content
  • parser/scan รัน isolated least privilege พร้อม timeout/resource limit
  • เก็บนอก webroot หรือคนละ host/origin
  • authorized retrieval + audit + retention/deletion

ไม่มี antivirus หรือ signature check ตัวเดียวที่รับประกันว่าไฟล์ปลอดภัย

KYC Document Processing

เอกสาร KYC มีทั้ง PII sensitivity และ parser risk:

StageControl
Uploadauthenticated purpose, consent/policy, size/type limits
Quarantineencrypted, restricted, random ID, no public serving
Processingisolated worker, minimal network/role, CPU/memory/time limits
Reviewmasked/minimized display, role/case authorization, watermark/audit ตาม policy
Storagekey/access boundary, retention, backup และ deletion workflow
Exportapproval, recipient/purpose, encryption และ traceability

Retention และการแสดงข้อมูลต้องให้คนตัดสิน

ระยะเวลาเก็บ, การ mask, ผู้ตรวจที่เข้าถึงได้ และการส่งต่อเอกสารเป็น policy/compliance decision ระบบควร enforce และสร้าง evidence แต่ไม่ควรเลือกค่าจากตัวอย่าง

Download อย่างปลอดภัย

  • authorize object ทุก request และใช้ short-lived scoped URL หากเหมาะสม
  • set server-generated filename และ safe Content-Disposition
  • set accurate Content-Type + X-Content-Type-Options: nosniff
  • active/untrusted content ควร serve จาก isolated origin และเป็น attachment
  • ห้าม token ใน query ที่รั่วผ่าน log/referrer โดยไม่มี compensating design
  • revoke/delete ต้องครอบคลุม CDN/cache/derived thumbnail ตาม lifecycle

Testing Checklist

  • inventory source/sink รวม raw HTML, URL, DOM, template และ third-party widget
  • rendering ใช้ safe sink/contextual encoding; raw HTML ผ่าน maintained sanitizer
  • CSP nonce/hash และ Trusted Types policy ไม่คืน input ตรง
  • CSRF มี token/origin/SameSite ตาม credential model
  • framing ถูกจำกัดด้วย frame-ancestors
  • redirect/navigation ใช้ relative หรือ exact allowlist และไม่พา credential ออก
  • browser storage/cache ถูก classify, expire และ clear เมื่อ logout/switch account
  • upload ตรวจ auth, type, size/structure, quarantine, isolate และ scan
  • KYC access/retention/export มี policy owner และ audit
  • download authorize, set type/disposition และ isolate active content

สรุป

Browser security ต้องควบคุมจุดที่ data กลายเป็น code, navigation หรือ credentialed request ส่วน file security ต้องมองทั้ง pipeline ตั้งแต่ upload ถึง delete CSP, scanner และ extension check เป็น defense in depth ไม่มีตัวใดแทน safe rendering, isolation และ authorization ได้

Further Reading