XSS ผ่าน innerHTML และวิธีป้องกัน
ทำความเข้าใจการโจมตีแบบสคริปต์ข้ามไซต์และทางเลือกที่ปลอดภัยแทน innerHTML
XSS ผ่าน innerHTML และวิธีป้องกัน เป็นบทเรียน HTML Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน HTML Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส HTML Academy มีบทเรียนทั้งหมด 4 บทเรียน
ช่องโหว่หลัก
การกำหนด el.innerHTML = userInput จะวิเคราะห์สตริงนั้นเป็น HTML หาก userInput มีแท็ก <script> หรือแอตทริบิวต์ตัวจัดการเหตุการณ์ (onclick, onerror) เบราว์เซอร์จะตีความแท็กเหล่านั้นและเรียกใช้โค้ดที่ผู้โจมตีควบคุมบนหน้าเว็บ
เหตุใดจึงพบได้บ่อย
โค้ดใดก็ตามที่นำข้อมูลผู้ใช้ไปแทรกใน HTML — ไม่ว่าจะเป็นแม่แบบฝั่งเซิร์ฟเวอร์หรือการแสดงผลฝั่งไคลเอนต์ — ล้วนเสี่ยงต่อ XSS หากไม่ได้ทำการหลีกข้อมูล แอปแบบหน้าเดียวที่สร้าง innerHTML จากการตอบกลับของส่วนต่อประสานโปรแกรมประยุกต์มีความเสี่ยงเป็นพิเศษเมื่อการตอบกลับเหล่านั้นมีเนื้อหาจากผู้ใช้
ตัวอย่างที่เห็นภาพ
ชื่อผู้ใช้ "Bob<img src=x onerror=alert(1)>" ที่กำหนดผ่าน div.innerHTML = `Welcome, ${user}` จะเรียกใช้ alert(1) เมื่อ img โหลดไม่สำเร็จ ข้อมูลเดียวกันในรูปข้อความธรรมดา (ผ่าน textContent) ไม่ก่ออันตราย — เบราว์เซอร์จะเห็นเพียงอักขระ ไม่ใช่แท็ก
// VULNERABLE
div.innerHTML = `Hi, ${user.name}`;
// SAFE
div.textContent = `Hi, ${user.name}`;ใช้ textContent สำหรับข้อความธรรมดา
textContent กำหนดเฉพาะข้อความ — อักขระพิเศษของ HTML จะแสดงเป็นอักขระตามตัวอักษรและไม่กลายเป็นแท็ก นี่เป็นเครื่องมือที่เหมาะสมใน 90% ของกรณี เปลี่ยนไปใช้ innerHTML เฉพาะเมื่อจำเป็นต้องแสดงมาร์กอัปจริง ๆ ไม่ใช่เพียงข้อความ
ใช้เมธอด DOM เพื่อสร้างโครงสร้าง
หากต้องการสร้างองค์ประกอบพร้อมข้อมูลผู้ใช้ ให้สร้างด้วย createElement และ textContent: const li = document.createElement("li"); li.textContent = name; ul.appendChild(li); ผลลัพธ์มีโครงสร้างเหมือนกับ innerHTML ทุกประการ แต่ปลอดภัยจาก XSS ตั้งแต่ขั้นตอนการสร้าง
เมื่อจำเป็นต้องใช้ innerHTML
สำหรับ HTML ที่เซิร์ฟเวอร์สร้างขึ้นและเชื่อถือได้ (ผลลัพธ์จากแม่แบบของคุณเอง หรือข้อความรูปแบบสมบูรณ์ที่ผ่านการทำความสะอาดจากเครื่องมือแก้ไขที่เชื่อถือได้) สามารถใช้ innerHTML ได้ แต่ห้ามนำข้อมูลจากแหล่งที่ไม่น่าเชื่อถือมาใช้โดยตรงโดยไม่ทำความสะอาดก่อน
ทำความสะอาดข้อความรูปแบบสมบูรณ์ด้วย DOMPurify
หากผู้ใช้ส่งข้อความรูปแบบสมบูรณ์ (เช่น ในเครื่องมือแก้ไขความคิดเห็นหรือการแสดงผลมาร์กดาวน์) ให้ทำความสะอาดก่อนกำหนดค่าให้ innerHTML: el.innerHTML = DOMPurify.sanitize(richHtml) DOMPurify จะลบแท็กและแอตทริบิวต์ที่เป็นอันตราย แต่ยังคงรูปแบบที่ปลอดภัย เช่น <b>, <em> และ <a>
insertAdjacentHTML มีความเสี่ยงแบบเดียวกัน
el.insertAdjacentHTML("beforeend", html) จะวิเคราะห์สตริง HTML — มีพื้นผิวการโจมตี XSS แบบเดียวกับ innerHTML ใช้กฎเดียวกัน: ห้ามป้อนข้อมูลผู้ใช้โดยตรง ให้ทำความสะอาดหรือใช้เมธอด DOM เช่นเดียวกันนี้กับ document.write ด้วย แม้ปัจจุบันไม่ควรมีใครใช้แล้ว
ค่าเริ่มต้นของเฟรมเวิร์ก
React, Vue, Svelte และ Angular จะทำการหลีกข้อความที่นำมาแทรกโดยค่าเริ่มต้น — {name} จึงปลอดภัย เฟรมเวิร์กเหล่านี้มีช่องทางข้ามการป้องกัน (dangerouslySetInnerHTML ใน React และ v-html ใน Vue) ซึ่งมีความเสี่ยงต่อ XSS แบบเดียวกัน ควรใช้เท่าที่จำเป็นและทำความสะอาดข้อมูลก่อน
การแทรกผ่านแอตทริบิวต์
แม้แต่ค่าของแอตทริบิวต์ก็อาจเป็นช่องทางโจมตีได้: <a href={url}> เมื่อ url="javascript:alert(1)" จะเรียกใช้โค้ดเมื่อถูกคลิก ตรวจสอบรูปแบบโครงแบบของ URL ทีละแบบ (อนุญาตเฉพาะ http:, https:, mailto: และ tel:) ก่อนนำข้อมูลที่ผู้ใช้ป้อนมาใส่ใน href, src หรือแอตทริบิวต์อื่นที่มี URL
นโยบาย Trusted Types
เบราว์เซอร์รุ่นใหม่รองรับ Trusted Types: กำหนดค่า CSP ด้วย require-trusted-types-for 'script' แล้ว innerHTML จะปฏิเสธสตริงดิบ — มีเพียงค่าที่หุ้มด้วยนโยบายเท่านั้นที่ผ่านได้ วิธีนี้ทำให้ XSS เป็นไปไม่ได้ด้วยกลไกของ API แทนที่จะอาศัยวินัยของผู้พัฒนา
การป้องกันหลายชั้น
ไม่มีชั้นใดชั้นหนึ่งเพียงอย่างเดียวที่เพียงพอ ควรใช้หลายวิธีร่วมกัน ได้แก่ ตรวจสอบข้อมูลเข้าที่เซิร์ฟเวอร์ หลบอักขระพิเศษของผลลัพธ์ขณะแสดงผล ใช้ CSP เพื่อบล็อกสคริปต์ที่ถูกแทรก ใช้ประเภทข้อมูลที่เชื่อถือได้เพื่อปฏิเสธสตริงดิบ และตรวจสอบความปลอดภัยสำหรับการใช้ innerHTML ทุกจุด การป้องกันหลายชั้นยังคงรับมือได้แม้ชั้นใดชั้นหนึ่งจะมีข้อผิดพลาด
ทดสอบความรู้
เหตุใด el.textContent = userInput จึงปลอดภัยจาก XSS ขณะที่ el.innerHTML = userInput เป็นอันตราย
สรุป
การใช้ innerHTML กับข้อมูลเข้าจากผู้ใช้เป็นช่องทาง XSS แบบดั้งเดิม ควรใช้ textContent สำหรับข้อความ และใช้ createElement ร่วมกับ textContent สำหรับโครงสร้าง เมื่อจำเป็นต้องใช้ HTML ที่มีรูปแบบหลากหลาย ให้ใช้เครื่องมือทำความสะอาด HTML ตรวจสอบรูปแบบโครงร่างของที่อยู่เว็บสำหรับค่าแอตทริบิวต์ ใช้ CSP และประเภทข้อมูลที่เชื่อถือได้ร่วมกันเพื่อทำให้ข้อผิดพลาดที่หลุดรอดการตรวจสอบโค้ดหมดผล เฟรมเวิร์กสมัยใหม่จะหลบอักขระพิเศษให้โดยค่าเริ่มต้น ควรใช้ช่องทางหลีกเลี่ยงที่ไม่ปลอดภัยของเฟรมเวิร์กให้น้อยและตรวจสอบอย่างรอบคอบ
คำถามที่พบบ่อย
บทเรียน “XSS ผ่าน innerHTML และวิธีป้องกัน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “XSS ผ่าน innerHTML และวิธีป้องกัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส HTML Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส HTML Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “XSS ผ่าน innerHTML และวิธีป้องกัน”
ทำความเข้าใจการโจมตีแบบสคริปต์ข้ามไซต์และทางเลือกที่ปลอดภัยแทน innerHTML คุณปฏิบัติ HTML Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน HTML Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน HTML Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “XSS ผ่าน innerHTML และวิธีป้องกัน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน HTML Academy นี้ได้ไหม
ได้ บทเรียน HTML Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- Content Security Policy meta http-equiv
- XSS ผ่าน innerHTML และวิธีป้องกัน
- การทำแซนด์บ็อกซ์ iframe และนโยบายสิทธิ์
- HTTPS และความสมบูรณ์ของทรัพยากรย่อย