Async ক্লিপবোর্ড এপিআই-এ অস্বাস্থ্যকর HTML

ক্রোম 120 থেকে, অ্যাসিঙ্ক ক্লিপবোর্ড এপিআই-এ একটি নতুন unsanitized বিকল্প উপলব্ধ। এই বিকল্পটি এইচটিএমএল-এর বিশেষ পরিস্থিতিতে সাহায্য করতে পারে, যেখানে আপনাকে ক্লিপবোর্ডের বিষয়বস্তু অনুলিপি করার সময় যেভাবে পেস্ট করতে হবে। অর্থাৎ, কোনো মধ্যবর্তী স্যানিটাইজেশন পদক্ষেপ ছাড়াই যা ব্রাউজারগুলি সাধারণত-এবং ভাল কারণে-প্রয়োগ করে। এই গাইডে এটি কীভাবে ব্যবহার করবেন তা শিখুন।

Async ক্লিপবোর্ড 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> ট্যাগ ছিনিয়ে নেওয়া (এবং অন্যান্য যেমন <meta> , <head> , এবং <style> ) এবং CSS ইনলাইন করে। . নিম্নলিখিত উদাহরণটি বিবেচনা করুন এবং 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();
  const outputBlob = await clipboardItem.getType('text/html');
  const output = await outputBlob.text();
  console.log(input, output);
}, 3000);

এইচটিএমএল স্যানিটাইজেশন সাধারণত একটি ভাল জিনিস। আপনি বেশিরভাগ ক্ষেত্রেই অস্বাস্থ্যকর এইচটিএমএলকে অনুমতি দিয়ে নিরাপত্তা সংক্রান্ত সমস্যায় নিজেকে প্রকাশ করতে চান না। যদিও এমন পরিস্থিতি রয়েছে যেখানে বিকাশকারী জানেন তারা ঠিক কী করছেন এবং যেখানে ইন-এবং আউটপুট HTML-এর অখণ্ডতা অ্যাপটির সঠিক কার্যকারিতার জন্য অত্যন্ত গুরুত্বপূর্ণ। এই পরিস্থিতিতে, আপনার দুটি পছন্দ আছে:

  1. আপনি যদি অনুলিপি করা এবং আটকানো উভয়ই নিয়ন্ত্রণ করেন, উদাহরণস্বরূপ, আপনি যদি আপনার অ্যাপের মধ্যে থেকে অনুলিপি করেন তাহলে একইভাবে আপনার অ্যাপের মধ্যে পেস্ট করেন, আপনার Async ক্লিপবোর্ড API-এর জন্য ওয়েব কাস্টম ফর্ম্যাটগুলি ব্যবহার করা উচিত। এখানে পড়া বন্ধ করুন এবং লিঙ্ক করা নিবন্ধটি দেখুন।
  2. আপনি যদি শুধুমাত্র আপনার অ্যাপে আটকানো শেষ নিয়ন্ত্রণ করেন, কিন্তু অনুলিপি শেষ না করেন, কারণ কপি অপারেশনটি এমন একটি নেটিভ অ্যাপে ঘটে যা ওয়েব কাস্টম ফরম্যাট সমর্থন করে না, তাহলে আপনার unsanitized বিকল্পটি ব্যবহার করা উচিত, যা বাকি অংশে ব্যাখ্যা করা হয়েছে এই নিবন্ধ.

স্যানিটাইজেশনের মধ্যে script ট্যাগ অপসারণ, স্টাইল ইনলাইন করা এবং এইচটিএমএল সুগঠিত হয়েছে তা নিশ্চিত করার মতো বিষয়গুলি অন্তর্ভুক্ত রয়েছে। এই তালিকাটি অ-বিস্তৃত, এবং ভবিষ্যতে আরও পদক্ষেপ যোগ করা হতে পারে।

স্যানিটাইজড এইচটিএমএল কপি এবং পেস্ট করুন

আপনি যখন Async ক্লিপবোর্ড API দিয়ে ক্লিপবোর্ডে HTML write() , তখন ব্রাউজার নিশ্চিত করে যে এটি একটি DOM পার্সারের মাধ্যমে চালনা করে এবং ফলাফল HTML স্ট্রিংকে সিরিয়ালাইজ করে এটি ভালভাবে গঠিত হয়েছে, কিন্তু এই ধাপে কোনো স্যানিটাইজেশন ঘটছে না। আপনার কিছু করার দরকার নেই। আপনি যখন অন্য অ্যাপ্লিকেশন দ্বারা ক্লিপবোর্ডে স্থাপিত read() , এবং আপনার ওয়েব অ্যাপটি সম্পূর্ণ বিশ্বস্ত বিষয়বস্তু পাওয়ার জন্য নির্বাচন করছে এবং আপনার নিজের কোডে কোনো স্যানিটাইজেশন করতে হবে, তখন আপনি 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 বিকল্পটি কার্যকর দেখতে, গ্লিচের ডেমোটি দেখুন এবং এর উত্স কোডটি দেখুন।

উপসংহার

ভূমিকায় বর্ণিত হিসাবে, বেশিরভাগ বিকাশকারীদের ক্লিপবোর্ড স্যানিটাইজেশন সম্পর্কে চিন্তা করতে হবে না এবং ব্রাউজার দ্বারা তৈরি ডিফল্ট স্যানিটাইজেশন পছন্দগুলির সাথে কাজ করতে পারে। বিরল ক্ষেত্রে যেখানে ডেভেলপারদের যত্ন নেওয়া প্রয়োজন, unsanitized বিকল্পটি বিদ্যমান।

স্বীকৃতি

এই নিবন্ধটি অনুপম স্নিগ্ধা এবং রাচেল অ্যান্ড্রু দ্বারা পর্যালোচনা করা হয়েছে। এপিআই মাইক্রোসফ্ট এজ টিম দ্বারা নির্দিষ্ট এবং প্রয়োগ করা হয়েছিল।