HTML ที่ไม่ผ่านการตรวจสอบใน API คลิปบอร์ดแบบไม่พร้อมกัน

ใน Chrome 120 มีตัวเลือก unsanitized ใหม่พร้อมให้ใช้งานในคลิปบอร์ดแบบไม่พร้อมกัน API ตัวเลือกนี้สามารถให้ความช่วยเหลือในกรณีพิเศษเกี่ยวกับ HTML ซึ่งคุณจำเป็นต้อง วางเนื้อหาของคลิปบอร์ดที่เหมือนกับตอนที่คัดลอก กล่าวคือ ไม่ต้องมีขั้นตอนการทำความสะอาดข้อมูลชั่วคราวระหว่างที่เบราว์เซอร์มักดำเนินการ และ ด้วยเหตุผลที่ดี ให้สมัคร เรียนรู้วิธีใช้งานได้ในคู่มือนี้

เมื่อใช้ Async Clipboard API ในกรณีส่วนใหญ่ นักพัฒนาซอฟต์แวร์ไม่จำเป็นต้องกังวลเกี่ยวกับความสมบูรณ์ของ เนื้อหาในคลิปบอร์ด และอาจเดาได้ว่าเนื้อหาที่เขียนลงใน คลิปบอร์ด (สำเนา) จะได้รับเนื้อหาเดียวกันเมื่ออ่านข้อมูลจากคลิปบอร์ด คลิปบอร์ด (วาง)

ซึ่งแน่นอนว่าต้องเป็นข้อความ ลองวางโค้ดต่อไปนี้ในเครื่องมือสำหรับนักพัฒนาเว็บ คอนโซลแล้วโฟกัสที่หน้าเว็บอีกครั้งทันที (จำเป็นต้องป้อน setTimeout() ทำให้คุณมีเวลาพอที่จะโฟกัสที่หน้าเว็บ ซึ่งเป็นข้อกำหนดของโหมดอะซิงโครนัส API คลิปบอร์ด) คุณจะเห็นว่าอินพุตตรงกับเอาต์พุตทุกประการ

setTimeout(async () => {
  const input = 'Hello';
  await navigator.clipboard.writeText(input);
  const output = await navigator.clipboard.readText();
  console.log(input, output, input === output);
  // Logs "Hello Hello true".
}, 3000);

ด้วยรูปภาพ จะแตกต่างไปเล็กน้อย เพื่อป้องกันการเรียก การโจมตีแบบคอมเพรสชันบอมบ์, เบราว์เซอร์ เข้ารหัสรูปภาพ เช่น PNG อีกครั้ง แต่อินพุตและเอาต์พุตรูปภาพเป็นภาพ คือพิกเซลต่อพิกเซล

setTimeout(async () => {
  const dataURL =
    'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
  const input = await fetch(dataURL).then((response) => response.blob());
  await navigator.clipboard.write([
    new ClipboardItem({
      [input.type]: input,
    }),
  ]);
  const [clipboardItem] = await navigator.clipboard.read();
  const output = await clipboardItem.getType(input.type);
  console.log(input.size, output.size, input.type === output.type);
  // Logs "68 161 true".
}, 3000);

แต่จะเกิดอะไรขึ้นกับข้อความ HTML ตามที่คุณอาจเดาได้อยู่แล้ว สำหรับ HTML องค์ประกอบ สถานการณ์ไม่เหมือนกัน ในเบราว์เซอร์นี้ เบราว์เซอร์ล้างโค้ด HTML เพื่อป้องกันการละเมิด ไม่ให้อะไรเกิดขึ้น เช่น ตัดแท็ก <script> ออกจาก HTML (และอื่นๆ เช่น <meta>, <head> และ <style>) และ CSS แบบอินไลน์ ลองดูตัวอย่างต่อไปนี้และลองใช้ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ คุณจะ คุณจะสังเกตเห็นว่าเอาต์พุตต่างจากอินพุตอย่างมาก

setTimeout(async () => {
  const input = `<html>  
  <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
    <meta name="ProgId" content="Excel.Sheet" />  
    <meta name="Generator" content="Microsoft Excel 15" />  
    <style>  
      body {  
        font-family: HK Grotesk;  
        background-color: var(--color-bg);  
      }  
    </style>  
  </head>  
  <body>  
    <div>hello</div>  
  </body>  
</html>`;
  const inputBlob = new Blob([input], { type: 'text/html' });
  await navigator.clipboard.write([
    new ClipboardItem({
      'text/html': inputBlob,
    }),
  ]);
  const [clipboardItem] = await navigator.clipboard.read();
  const outputBlob = await clipboardItem.getType('text/html');
  const output = await outputBlob.text();
  console.log(input, output);
}, 3000);

โดยทั่วไปแล้ว การตรวจสอบความถูกต้องของ HTML เป็นสิ่งที่ดี คุณไม่ต้องการเปิดเผยตัว ปัญหาด้านความปลอดภัยโดยอนุญาตให้มี HTML ที่ไม่ผ่านการตรวจสอบได้ในกรณีส่วนใหญ่ มี แต่เป็นสถานการณ์จำลองที่นักพัฒนาซอฟต์แวร์รู้ว่ากำลังทำอะไรอยู่และ โดยที่ความสมบูรณ์ของ HTML ทั้งในและเอาต์พุตเป็นสิ่งสำคัญสำหรับ ฟังก์ชันการทำงานของแอป ภายใต้สถานการณ์นี้ คุณมี 2 ทางเลือกดังนี้

  1. ตัวอย่างเช่น ถ้าคุณควบคุมทั้งจุดสิ้นสุดในการคัดลอกและวาง จากภายในแอปเพื่อวางในแอป คุณควรใช้ รูปแบบที่กำหนดเองบนเว็บสำหรับ Async Clipboard API หยุดอ่านที่นี่และตรวจสอบบทความที่ลิงก์
  2. หากคุณควบคุมเฉพาะจุดสิ้นสุดการวางในแอป แต่ควบคุมจุดสิ้นสุดการคัดลอก อาจเป็นเพราะการดำเนินการคัดลอกเกิดขึ้นในแอปที่มาพร้อมเครื่องซึ่งไม่รองรับ รูปแบบที่กำหนดเองแบบเว็บ คุณควรใช้ตัวเลือก unsanitized ซึ่งเป็น ซึ่งอธิบายไว้ในส่วนที่เหลือของบทความนี้

การปรับปรุงเนื้อหารวมถึงสิ่งต่อไปนี้ เช่น การนำแท็ก script ออก รูปแบบอินไลน์ และ ให้แน่ใจว่า HTML อยู่ในรูปแบบที่ถูกต้อง รายการนี้เป็นแบบไม่ครอบคลุมและอื่นๆ ในอนาคต

คัดลอกและวาง HTML ที่ไม่ผ่านการตรวจสอบ

เมื่อคุณwrite() (คัดลอก) HTML ไปยังคลิปบอร์ดด้วย Async Clipboard API เบราว์เซอร์จะตรวจสอบว่าเบราว์เซอร์มีรูปแบบที่ถูกต้องโดยการเรียกใช้ผ่านโปรแกรมแยกวิเคราะห์ DOM และการจัดลำดับสตริง HTML ที่ได้มานั้น แต่ไม่มีการทำรหัสผ่านขึ้นที่ ขั้นตอนนี้ คุณไม่จำเป็นต้องดำเนินการใดๆ เมื่อคุณวาง HTML read() ใน คลิปบอร์ดตามแอปพลิเคชันอื่น และเว็บแอปของคุณเลือกใช้ เนื้อหาที่มีความแม่นยำครบถ้วนและจำเป็นต้องดำเนินการทำความสะอาดข้อมูลในโค้ดของคุณเอง คุณสามารถส่งออบเจ็กต์ตัวเลือกไปยังเมธอด read() พร้อมด้วยพร็อพเพอร์ตี้ได้ unsanitized และค่า ['text/html'] โดยแยกออกมาให้มีลักษณะดังต่อไปนี้ navigator.clipboard.read({ unsanitized: ['text/html'] }) ตัวอย่างโค้ดต่อไปนี้ ด้านล่างนั้นแทบจะเหมือนกันกับที่แสดงไว้ก่อนหน้านี้ แต่คราวนี้ด้วยunsanitized ตัวเลือก เมื่อลองใช้ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ คุณจะเห็นว่าอินพุตและ เอาต์พุตจะเหมือนกัน

setTimeout(async () => {
  const input = `<html>  
  <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
    <meta name="ProgId" content="Excel.Sheet" />  
    <meta name="Generator" content="Microsoft Excel 15" />  
    <style>  
      body {  
        font-family: HK Grotesk;  
        background-color: var(--color-bg);  
      }  
    </style>  
  </head>  
  <body>  
    <div>hello</div>  
  </body>  
</html>`;
  const inputBlob = new Blob([input], { type: 'text/html' });
  await navigator.clipboard.write([
    new ClipboardItem({
      'text/html': inputBlob,
    }),
  ]);
  const [clipboardItem] = await navigator.clipboard.read({
    unsanitized: ['text/html'],
  });
  const outputBlob = await clipboardItem.getType('text/html');
  const output = await outputBlob.text();
  console.log(input, output);
}, 3000);

การรองรับเบราว์เซอร์และการตรวจหาฟีเจอร์

เนื่องจากยังไม่มีวิธีโดยตรงในการตรวจสอบว่า ฟีเจอร์นี้ได้รับการสนับสนุนหรือไม่ ดังนั้น จะขึ้นอยู่กับการสังเกตพฤติกรรม ดังนั้นตัวอย่างต่อไปนี้ อาศัยการตรวจจับข้อเท็จจริงที่ว่าแท็ก <style> อยู่รอดหรือไม่ บ่งชี้ถึงการรองรับ หรืออยู่ระหว่างในบรรทัด ซึ่งบ่งชี้ว่าไม่สนับสนุน โปรดทราบว่า เพื่อให้สิ่งนี้ทำงาน หน้าเว็บต้องได้รับคลิปบอร์ดอยู่แล้ว สิทธิ์

const supportsUnsanitized = async () => {
  const input = `<style>p{color:red}</style><p>a`;
  const inputBlob = new Blob([input], { type: 'text/html' });
  await navigator.clipboard.write([
    new ClipboardItem({
      'text/html': inputBlob,
    }),
  ]);
  const [clipboardItem] = await navigator.clipboard.read({
    unsanitized: ['text/html],
  });
  const outputBlob = await clipboardItem.getType('text/html');
  const output = await outputBlob.text();
  return /<style>/.test(output);
};

สาธิต

หากต้องการดูตัวเลือก unsanitized ในการใช้งานจริง โปรดดู การสาธิตเกี่ยวกับ Glitch และดู ซอร์สโค้ด

บทสรุป

ตามที่ได้กล่าวไว้ในบทนำ นักพัฒนาแอปส่วนใหญ่ไม่จำเป็นต้องกังวลเกี่ยวกับ การทำความสะอาดคลิปบอร์ดและสามารถทำงานกับตัวเลือกการล้างข้อมูลเริ่มต้นได้ สร้างโดยเบราว์เซอร์ ในกรณีที่พบไม่บ่อยนักที่นักพัฒนาแอปต้องระมัดระวัง มีตัวเลือก unsanitized รายการ

กิตติกรรมประกาศ

บทความนี้ได้รับการตรวจสอบโดย Anupam Snigdha และ Rachel Andrew ระบุ API และ ที่ทีม Microsoft Edge นำมาใช้