স্যানিটাইজার API এর সাথে নিরাপদ DOM ম্যানিপুলেশন

জ্যাক জে
Jack J

অ্যাপ্লিকেশনগুলো প্রতিনিয়ত অবিশ্বস্ত স্ট্রিং নিয়ে কাজ করে, কিন্তু একটি HTML ডকুমেন্টের অংশ হিসেবে সেই কন্টেন্টকে নিরাপদে রেন্ডার করা বেশ জটিল হতে পারে। পর্যাপ্ত সতর্কতা অবলম্বন না করলে, আপনি অসাবধানতাবশত ক্রস-সাইট স্ক্রিপ্টিং (XSS)-এর সুযোগ তৈরি করে ফেলতে পারেন, যা ক্ষতিকর আক্রমণকারীরা কাজে লাগাতে পারে।

সেই ঝুঁকি প্রশমিত করতে, নতুন স্যানিটাইজার এপিআই প্রস্তাবনাটির লক্ষ্য হলো একটি পৃষ্ঠায় যথেচ্ছ স্ট্রিং নিরাপদে সন্নিবেশ করার জন্য একটি শক্তিশালী প্রসেসর তৈরি করা।

// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerror=alert(0)>`, new Sanitizer())

ব্যবহারকারীর ইনপুট এস্কেপ করুন

ব্যবহারকারীর ইনপুট, কোয়েরি স্ট্রিং, কুকির বিষয়বস্তু এবং আরও অনেক কিছু DOM-এ প্রবেশ করানোর সময়, স্ট্রিংগুলোকে অবশ্যই সঠিকভাবে এস্কেপ করতে হবে। .innerHTML ব্যবহার করে DOM ম্যানিপুলেশনের ক্ষেত্রে বিশেষ মনোযোগ দেওয়া উচিত, যেখানে এস্কেপ না করা স্ট্রিংগুলো XSS-এর একটি সাধারণ উৎস।

const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input

যদি আপনি ইনপুট স্ট্রিং-এ HTML বিশেষ অক্ষরগুলিকে এস্কেপ করেন অথবা .textContent ব্যবহার করে এটিকে প্রসারিত করেন, তাহলে alert(0) কার্যকর হয় না। তবে, যেহেতু ব্যবহারকারীর যোগ করা <em> সরাসরি একটি স্ট্রিং হিসাবে প্রসারিত হয়, তাই HTML-এ টেক্সট ডেকোরেশন বজায় রাখার জন্য এই পদ্ধতিটি ব্যবহার করা যায় না।

এখানে সবচেয়ে ভালো কাজ হলো পালানো নয়, বরং জীবাণুমুক্ত করা

ব্যবহারকারীর ইনপুট পরিমার্জন করুন

এস্কেপিং বলতে বিশেষ HTML অক্ষরগুলোকে HTML এনটিটি দ্বারা প্রতিস্থাপন করাকে বোঝায়।

স্যানিটাইজিং বলতে এইচটিএমএল স্ট্রিং থেকে অর্থগতভাবে ক্ষতিকর অংশ (যেমন স্ক্রিপ্ট এক্সিকিউশন) অপসারণ করাকে বোঝায়।

উদাহরণ

পূর্ববর্তী উদাহরণে, <img onerror> এরর হ্যান্ডলারকে এক্সিকিউট করে, কিন্তু যদি onerror হ্যান্ডলারটি সরিয়ে দেওয়া হতো, তাহলে <em> অক্ষত রেখেই DOM-এ এটিকে নিরাপদে এক্সপ্যান্ড করা সম্ভব হতো।

// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`

সঠিকভাবে পরিচ্ছন্ন করার জন্য, ইনপুট স্ট্রিংটিকে HTML হিসেবে পার্স করা, ক্ষতিকর বলে বিবেচিত ট্যাগ ও অ্যাট্রিবিউটগুলো বাদ দেওয়া এবং নিরীহগুলো রাখা প্রয়োজন।

প্রস্তাবিত স্যানিটাইজার এপিআই স্পেসিফিকেশনের লক্ষ্য হলো ব্রাউজারগুলির জন্য এই ধরনের প্রসেসিংকে একটি স্ট্যান্ডার্ড এপিআই হিসেবে প্রদান করা।

স্যানিটাইজার এপিআই

স্যানিটাইজার এপিআই নিম্নলিখিত উপায়ে ব্যবহৃত হয়:

const $div = document.querySelector('div')
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.setHTML(user_input, { sanitizer: new Sanitizer() }) // <div><em>hello world</em><img src=""></div>

তবে, { sanitizer: new Sanitizer() } হলো ডিফল্ট আর্গুমেন্ট।

$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>

উল্লেখ্য যে, setHTML() মেথডটি Element এর উপর সংজ্ঞায়িত। Element এর একটি মেথড হওয়ায়, পার্স করার প্রেক্ষাপটটি স্ব-ব্যাখ্যামূলক (এই ক্ষেত্রে <div> ), পার্সিংটি অভ্যন্তরীণভাবে একবার করা হয় এবং ফলাফলটি সরাসরি DOM-এ সম্প্রসারিত হয়।

স্যানিটাইজেশনের ফলাফল স্ট্রিং হিসেবে পেতে, আপনি setHTML() ফলাফলের উপর .innerHTML ব্যবহার করতে পারেন।

const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img src="">

কনফিগারেশন দিয়ে কাস্টমাইজ করুন

স্যানিটাইজার এপিআই ডিফল্টরূপে এমন স্ট্রিংগুলি সরিয়ে ফেলার জন্য কনফিগার করা থাকে যা স্ক্রিপ্ট এক্সিকিউশন ট্রিগার করতে পারে। তবে, আপনি একটি কনফিগারেশন অবজেক্টের মাধ্যমে স্যানিটাইজেশন প্রক্রিয়ায় আপনার নিজস্ব কাস্টমাইজেশনও যোগ করতে পারেন।

const config = {
  allowElements: [],
  blockElements: [],
  dropElements: [],
  allowAttributes: {},
  dropAttributes: {},
  allowCustomElements: true,
  allowComments: true
};
// sanitized result is customized by configuration
new Sanitizer(config)

নিম্নলিখিত বিকল্পগুলি নির্দিষ্ট করে যে স্যানিটাইজেশনের ফলাফল নির্দিষ্ট উপাদানটির সাথে কীভাবে আচরণ করবে।

allowElements : সেইসব উপাদানের নাম যা স্যানিটাইজার ধরে রাখবে।

blockElements : সেইসব এলিমেন্টের নাম, যেগুলোকে স্যানিটাইজার তাদের চাইল্ড এলিমেন্টগুলো রেখে অপসারণ করবে।

dropElements : স্যানিটাইজার যেসব এলিমেন্ট ও তাদের চাইল্ড এলিমেন্টগুলো মুছে ফেলবে, সেগুলোর নাম।

const str = `hello <b><i>world</i></b>`

$div.setHTML(str)
// <div>hello <b><i>world</i></b></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: [ "b" ]}) })
// <div>hello <b>world</b></div>

$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ "b" ]}) })
// <div>hello <i>world</i></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: []}) })
// <div>hello world</div>

নিম্নলিখিত অপশনগুলির সাহায্যে আপনি স্যানিটাইজারটি নির্দিষ্ট অ্যাট্রিবিউটগুলিকে অনুমতি দেবে নাকি দেবে না, তা-ও নিয়ন্ত্রণ করতে পারেন:

  • allowAttributes
  • dropAttributes

allowAttributes এবং dropAttributes প্রপার্টিগুলো অ্যাট্রিবিউট ম্যাচ লিস্ট আশা করে — এমন অবজেক্ট, যার কী (key) হলো অ্যাট্রিবিউটের নাম এবং ভ্যালু (value) হলো টার্গেট এলিমেন্টের তালিকা অথবা * ওয়াইল্ডকার্ড।

const str = `<span id=foo class=bar style="color: red">hello</span>`

$div.setHTML(str)
// <div><span id="foo" class="bar" style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["span"]}}) })
// <div><span style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["p"]}}) })
// <div><span>hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["*"]}}) })
// <div><span style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({dropAttributes: {"id": ["span"]}}) })
// <div><span class="bar" style="color: red">hello</span></div>

$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// <div>hello</div>

allowCustomElements হলো কাস্টম এলিমেন্ট অনুমোদন বা প্রত্যাখ্যান করার অপশন। যদি সেগুলোকে অনুমোদন দেওয়া হয়, তাহলেও এলিমেন্ট এবং অ্যাট্রিবিউটের অন্যান্য কনফিগারেশনগুলো অপরিবর্তিত থাকে।

const str = `<custom-elem>hello</custom-elem>`

$div.setHTML(str)
// <div></div>

const sanitizer = new Sanitizer({
  allowCustomElements: true,
  allowElements: ["div", "custom-elem"]
})
$div.setHTML(str, { sanitizer })
// <div><custom-elem>hello</custom-elem></div>

এপিআই পৃষ্ঠ

DomPurify-এর সাথে তুলনা

DOMPurify একটি সুপরিচিত লাইব্রেরি যা স্যানিটাইজেশন কার্যকারিতা প্রদান করে। Sanitizer API এবং DOMPurify-এর মধ্যে প্রধান পার্থক্য হলো, DOMPurify স্যানিটাইজেশনের ফলাফল একটি স্ট্রিং হিসেবে রিটার্ন করে, যা আপনাকে .innerHTML ব্যবহার করে একটি DOM এলিমেন্টের মধ্যে লিখতে হয়।

const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sanitized
// `<em>hello world</em><img src="">`

যখন ব্রাউজারে স্যানিটাইজার এপিআই প্রয়োগ করা হয় না, তখন DOMPurify একটি বিকল্প ব্যবস্থা হিসেবে কাজ করতে পারে।

DOMPurify ইমপ্লিমেন্টেশনের কয়েকটি অসুবিধা রয়েছে। যদি কোনো স্ট্রিং রিটার্ন করা হয়, তাহলে ইনপুট স্ট্রিংটি DOMPurify এবং .innerHTML দ্বারা দুইবার পার্স করা হয়। এই দ্বৈত পার্সিং প্রসেসিং সময় নষ্ট করে, কিন্তু এর ফলে এমন কিছু আকর্ষণীয় দুর্বলতাও তৈরি হতে পারে, যেখানে দ্বিতীয় পার্সিংয়ের ফলাফল প্রথমটির থেকে ভিন্ন হয়।

HTML পার্স করার জন্যও কনটেক্সট বা প্রেক্ষাপটের প্রয়োজন হয়। উদাহরণস্বরূপ, <td> <table> মধ্যে মানানসই, কিন্তু <div> মধ্যে নয়। যেহেতু DOMPurify.sanitize() ফাংশনটি আর্গুমেন্ট হিসেবে শুধুমাত্র একটি স্ট্রিং গ্রহণ করে, তাই পার্সিং কনটেক্সটটি অনুমান করে নিতে হয়েছিল।

Sanitizer API- টি DOMPurify পদ্ধতির একটি উন্নত সংস্করণ এবং এটি দ্বৈত পার্সিং-এর প্রয়োজনীয়তা দূর করতে ও পার্সিং-এর প্রেক্ষাপটকে স্পষ্ট করার জন্য ডিজাইন করা হয়েছে।

এপিআই স্ট্যাটাস এবং ব্রাউজার সাপোর্ট

স্যানিটাইজার এপিআই মান নির্ধারণ প্রক্রিয়ার অধীনে আলোচনাধীন রয়েছে এবং ক্রোম এটি বাস্তবায়নের প্রক্রিয়ায় আছে।

ধাপ অবস্থা
১. ব্যাখ্যা তৈরি করুন সম্পূর্ণ
২. স্পেসিফিকেশন খসড়া তৈরি করুন সম্পূর্ণ
৩. মতামত সংগ্রহ করুন এবং নকশা পরিমার্জন করুন সম্পূর্ণ
৪. ক্রোম অরিজিন ট্রায়াল সম্পূর্ণ
৫. চালু করা M105-এ পাঠানোর অভিপ্রায়

মোজিলা এই প্রস্তাবটিকে প্রোটোটাইপ করার যোগ্য বলে মনে করে এবং এটি সক্রিয়ভাবে বাস্তবায়ন করছে।

WebKit: WebKit মেইলিং লিস্টে উত্তরটি দেখুন।

স্যানিটাইজার এপিআই কীভাবে সক্রিয় করবেন

Browser Support

  • ক্রোম: ১৪৬।
  • প্রান্ত: ১৪৬।
  • ফায়ারফক্স: ১৪৮।
  • সাফারি সমর্থিত নয়।

ক্রোম স্যানিটাইজার এপিআই (Sanitizer API) প্রয়োগ করার প্রক্রিয়ায় রয়েছে। ক্রোম ৯৩ বা তার পরবর্তী সংস্করণে, আপনি about://flags/#enable-experimental-web-platform-features ফ্ল্যাগটি সক্রিয় করে এর কার্যকারিতা পরীক্ষা করে দেখতে পারেন। ক্রোম ক্যানারি (Canary) এবং ডেভ চ্যানেলের (Dev channel) আগের সংস্করণগুলিতে, আপনি --enable-blink-features=SanitizerAPI ব্যবহার করে এটি সক্রিয় করতে পারেন। ফ্ল্যাগ সহ ক্রোম কীভাবে চালাবেন, তার নির্দেশাবলী দেখে নিন।

ফায়ারফক্স

ফায়ারফক্স স্যানিটাইজার এপিআই-কে একটি পরীক্ষামূলক বৈশিষ্ট্য হিসেবেও প্রয়োগ করে। এটি সক্রিয় করতে, about:configdom.security.sanitizer.enabled ফ্ল্যাগটিকে true তে সেট করুন।

বৈশিষ্ট্য সনাক্তকরণ

if (window.Sanitizer) {
  // Sanitizer API is enabled
}

প্রতিক্রিয়া

আপনি যদি এই এপিআইটি ব্যবহার করে দেখেন এবং আপনার কোনো মতামত থাকে, তবে আমরা তা জানতে আগ্রহী। স্যানিটাইজার এপিআই গিটহাব ইস্যুগুলোতে আপনার ভাবনাগুলো শেয়ার করুন এবং স্পেক লেখক ও এই এপিআইটিতে আগ্রহী ব্যক্তিদের সাথে আলোচনা করুন।

ক্রোমের বাস্তবায়নে কোনো বাগ বা অপ্রত্যাশিত আচরণ খুঁজে পেলে, তা জানানোর জন্য একটি বাগ রিপোর্ট করুনBlink>SecurityFeature>SanitizerAPI কম্পোনেন্টগুলো নির্বাচন করুন এবং বাস্তবায়নকারীদের সমস্যাটি খুঁজে বের করতে সাহায্য করার জন্য বিস্তারিত তথ্য শেয়ার করুন।

ডেমো

স্যানিটাইজার এপিআই-এর কার্যকারিতা সরাসরি দেখতে মাইক ওয়েস্টের তৈরি স্যানিটাইজার এপিআই প্লেগ্রাউন্ডটি দেখুন:

তথ্যসূত্র