Kodokon kodokon.com

ความปลอดภัยฝั่ง HTML: XSS, sandbox, noopener และ CSP

ระบุเวกเตอร์ XSS ที่เฉพาะเจาะจงกับมาร์กอัป และปรับใช้การป้องกันเนทีฟของ HTML: sandbox, noopener และ Content Security Policy

11 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

บริบทของการ escape สร้างความแตกต่างทั้งหมด: ค่าที่ไม่เป็นอันตรายในโหนดข้อความอาจกลายเป็นสิ่งที่ปฏิบัติการได้ในแอตทริบิวต์ แอตทริบิวต์ ที่ไม่ใส่เครื่องหมายคำพูด (unquoted) เป็นกรณีที่แย่ที่สุด: ช่องว่างเพียงตัวเดียวในข้อมูลที่ถูกฉีดเข้ามาก็เพียงพอที่จะสร้างแอตทริบิวต์ใหม่ เช่น handler onmouseover แม้เมื่อใส่เครื่องหมายคำพูดอย่างถูกต้องแล้ว ค่าก็ยังอันตรายใน sink ที่ละเอียดอ่อน: href และ src รับ scheme javascript:, formaction ขโมยการส่งของฟอร์ม และ srcdoc ตีความ entity ของมันเป็น HTML เมื่อถูกถอดรหัสแล้ว

HTML
<!-- userName = x onmouseover=alert(1) -->
<img src="avatar.png" alt=x onmouseover=alert(1)>
<!-- The unquoted attribute lets a handler in -->
ข้อมูลที่ไม่ได้ escape ในแอตทริบิวต์ที่ไม่ใส่เครื่องหมายคำพูดจะกลายเป็นโค้ด

แอตทริบิวต์ sandbox ของ iframe ถอด สิทธิ์ทั้งหมด ออก แล้วคุณจึงมอบมันคืนทีละอย่าง: allow-scripts, allow-forms, allow-popups... หากไม่มี allow-same-origin เอกสารที่ฝังไว้จะได้รับ origin ที่ทึบแสง (opaque origin): ไม่มีคุกกี้ ไม่มีสตอเรจ ไม่มีการเข้าถึง DOM ของ parent - แม้ว่ามันจะถูกเสิร์ฟจากโดเมนของคุณเองก็ตาม

HTML
<iframe
  src="https://widget.example.com"
  sandbox="allow-scripts allow-forms"
  referrerpolicy="no-referrer">
</iframe>
วิดเจ็ตจากบุคคลที่สามที่รันสคริปต์ได้ แต่ถูกจำกัดอยู่ใน origin ที่ทึบแสง

ลิงก์ target=_blank เคยให้การอ้างอิง window.opener แก่หน้าที่ถูกเปิด ชี้กลับมายังแท็บของคุณในอดีต: มันสามารถรีไดเรกต์หน้าของคุณไปยังโคลนฟิชชิงได้ การโจมตีที่รู้จักกันในชื่อ tabnabbing เบราว์เซอร์สมัยใหม่บัดนี้ใช้ noopener โดยปริยายบน target=_blank แล้ว แต่การเขียนมันออกมาชัดเจนยังคงจำเป็นสำหรับ window.open ใน JavaScript และเพื่อบันทึกเจตนา; เพิ่ม noreferrer เพื่อระงับส่วนหัว Referer ด้วย

HTML
<a href="https://external.example"
   target="_blank"
   rel="noopener noreferrer">
  View the external resource
</a>
หน้าที่ถูกเปิดจะได้รับ window.opener === null

Content Security Policy จำกัดแหล่งที่มาของโค้ดที่ปฏิบัติการได้และทำให้ XSS ส่วนใหญ่เป็นกลาง แม้เมื่อการฉีดสำเร็จแล้วก็ตาม เมื่อประกาศผ่านแท็ก <meta http-equiv> มันก็ยังมีจุดบอด: directive frame-ancestors, report-uri และ sandbox จะถูกละเว้นในนั้นและต้องใช้ส่วนหัว HTTP เลือกใช้ nonces ที่สร้างใหม่ทุกครั้งที่ตอบสนอง มากกว่ารายการอนุญาตโดเมน (domain allowlists) ซึ่ง directive strict-dynamic ทำให้ล้าสมัยอยู่แล้ว

HTML
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               script-src 'self' 'nonce-r4nd0m'">
เฉพาะสคริปต์ที่พก nonce ของเซิร์ฟเวอร์เท่านั้นที่รัน

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ทำไมการรวม allow-scripts และ allow-same-origin บน iframe ที่ใช้ origin เดียวกันจึงยกเลิก sandbox?
    • เอกสารที่ฝังไว้ซึ่งรันสคริปต์ได้และไม่ถูกแยกส่วน สามารถเข้าถึง DOM ของ parent และลบแอตทริบิวต์ sandbox ได้ด้วยตัวเอง
    • โทเคนทั้งสองเข้ากันไม่ได้และเบราว์เซอร์จะละเว้นแอตทริบิวต์ทั้งหมด
    • allow-same-origin ปิดใช้งานโทเคนอื่น ๆ ทั้งหมดโดยอัตโนมัติ
  2. rel=noopener ป้องกันการโจมตีแบบใด?
    • การขโมยคุกกี้เซสชันโดยหน้าที่ถูกเปิด
    • การฉีดสคริปต์เข้าไปในหน้าที่ถูกเปิด
    • Tabnabbing: หน้าที่ถูกเปิดใช้ window.opener เพื่อรีไดเรกต์แท็บของคุณไปยังไซต์ฟิชชิง
  3. directive ใดถูกละเว้นเมื่อ CSP ถูกประกาศผ่าน <meta http-equiv>?
    • script-src
    • frame-ancestors
    • default-src
    • img-src