ตั้งแต่ Chrome เวอร์ชัน 120 ตัวเลือก unsanitized
ใหม่จะพร้อมใช้งานใน Async Clipboard API ตัวเลือกนี้ช่วยได้ในสถานการณ์พิเศษเกี่ยวกับ HTML ซึ่งคุณต้องวางเนื้อหาในคลิปบอร์ดให้เหมือนกับตอนที่คัดลอก
กล่าวคือ ไม่ต้องมีขั้นตอนการทำความสะอาดข้อมูลระหว่างกลางกับเบราว์เซอร์ที่ใช้กันทั่วไป และเพื่อเหตุผลที่ดี ดูวิธีใช้ในคู่มือนี้
เมื่อทํางานกับ Async Clipboard API ในกรณีส่วนใหญ่ นักพัฒนาซอฟต์แวร์ไม่จําเป็นต้องกังวลเกี่ยวกับความสมบูรณ์ของเนื้อหาในคลิปบอร์ด และสามารถถือว่าสิ่งที่เขียนลงในคลิปบอร์ด (คัดลอก) นั้นเหมือนกันกับสิ่งที่จะได้รับเมื่ออ่านข้อมูลจากคลิปบอร์ด (วาง)
ข้อความเป็นตัวอย่างที่ชัดเจน ลองวางโค้ดต่อไปนี้ในคอนโซล DevTools แล้วโฟกัสหน้าเว็บใหม่ทันที (setTimeout()
เป็นสิ่งจําเป็นเพื่อให้คุณมีเวลาพอที่จะโฟกัสหน้าเว็บ ซึ่งเป็นข้อกําหนดของ Async clipboard 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 ทางเลือก ดังนี้
- หากคุณควบคุมทั้งการคัดลอกและการวาง เช่น หากคุณคัดลอกจากภายในแอปเพื่อวางภายในแอปด้วย คุณควรใช้รูปแบบที่กำหนดเองของเว็บสำหรับ Async Clipboard API หยุดอ่านตรงนี้และดูบทความที่ลิงก์
- หากคุณควบคุมเฉพาะปลายทางการวางในแอป แต่ไม่ได้ควบคุมปลายทางการคัดลอก ซึ่งอาจเป็นเพราะการดำเนินการคัดลอกเกิดขึ้นในแอปเนทีฟที่ไม่รองรับรูปแบบที่กำหนดเองของเว็บ คุณควรใช้ตัวเลือก
unsanitized
ซึ่งอธิบายไว้ในส่วนที่เหลือของบทความนี้
ซึ่งรวมถึงการนำแท็ก script
ออก แทรกสไตล์ในบรรทัด และตรวจสอบว่า HTML มีรูปแบบที่ถูกต้อง รายการนี้ยังไม่ครอบคลุมทั้งหมด และอาจมีการเพิ่มขั้นตอนอื่นๆ ในอนาคต
คัดลอกและวาง HTML ที่ไม่ผ่านการกรอง
เมื่อคุณ write()
(คัดลอก) HTML ไปยังคลิปบอร์ดด้วย Async Clipboard API เบราว์เซอร์จะตรวจสอบว่า HTML อยู่ในรูปแบบที่ถูกต้องโดยเรียกใช้ผ่านโปรแกรมแยกวิเคราะห์ DOM และจัดรูปแบบสตริง HTML ที่ได้ แต่จะไม่มีการจัดทําให้ปลอดภัยในขั้นตอนนี้ คุณไม่ต้องดำเนินการใดๆ เมื่อแอปพลิเคชันอื่นวางread()
HTML ลงในคลิปบอร์ด และเว็บแอปของคุณเลือกใช้การรับเนื้อหาที่ตรงที่สุดและจำเป็นต้องทำการทำให้ปลอดภัยในโค้ดของคุณเอง คุณส่งออบเจ็กต์ตัวเลือกไปยังเมธอด read()
ที่มีพร็อพเพอร์ตี้ unsanitized
และค่า ['text/html']
ได้ เมื่อแยกออกมา ข้อความจะมีลักษณะดังนี้
navigator.clipboard.read({ unsanitized: ['text/html'] })
ตัวอย่างโค้ดต่อไปนี้เกือบเหมือนกับตัวอย่างที่แสดงก่อนหน้านี้ แต่ครั้งนี้มีตัวเลือก unsanitized
เมื่อลองใช้ในคอนโซล DevTools คุณจะเห็นข้อมูลขาเข้าและขาออกเหมือนกัน
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 ทีม Microsoft Edge เป็นผู้ระบุและติดตั้งใช้งาน API