برنامه نویسی متقابل سایت مبتنی بر DOM (DOM XSS) زمانی اتفاق می افتد که داده ها از یک منبع کنترل شده توسط کاربر (مانند یک نام کاربری یا یک URL تغییر مسیر که از قطعه URL گرفته شده است) به یک sink می رسد که تابعی مانند eval()
یا یک ویژگی است. تنظیم کننده ای مانند .innerHTML
که می تواند کدهای جاوا اسکریپت دلخواه را اجرا کند.
DOM XSS یکی از رایج ترین آسیب پذیری های امنیتی وب است و معمولا تیم های توسعه دهنده به طور تصادفی آن را در برنامه های خود معرفی می کنند. Trusted Types ابزارهایی را برای نوشتن، بررسی امنیتی، و عاری از آسیبپذیریهای DOM XSS با ایمن کردن عملکردهای وب خطرناک API در اختیار شما قرار میدهد. Trusted Types برای مرورگرهایی که هنوز از آنها پشتیبانی نمیکنند، بهعنوان پلیپر در دسترس هستند.
پس زمینه
برای سالهای متمادی DOM XSS یکی از رایجترین و خطرناکترین آسیبپذیریهای امنیتی وب بوده است.
دو نوع اسکریپت بین سایتی وجود دارد. برخی از آسیبپذیریهای XSS به دلیل کد سمت سرور ایجاد میشوند که بهطور ناامن کد HTML تشکیلدهنده وبسایت را ایجاد میکند. برخی دیگر یک دلیل ریشه ای روی کلاینت دارند، جایی که کد جاوا اسکریپت توابع خطرناک را با محتوای کنترل شده توسط کاربر فراخوانی می کند.
برای جلوگیری از XSS سمت سرور ، HTML را با الحاق رشته ها تولید نکنید. به جای آن از کتابخانههای الگوی فرار خودکار متنی، همراه با یک خطمشی امنیتی محتوای غیرمبتنی برای کاهش بیشتر اشکال استفاده کنید.
اکنون مرورگرها همچنین می توانند با استفاده از Trusted Types به جلوگیری از XSS مبتنی بر DOM سمت سرویس گیرنده کمک کنند.
معرفی API
Trusted Types با قفل کردن عملکردهای مخاطره آمیز سینک زیر کار می کنند. ممکن است قبلاً برخی از آنها را بشناسید، زیرا فروشندگان مرورگر و چارچوب های وب شما را از استفاده از این ویژگی ها به دلایل امنیتی دور می کنند.
- دستکاری اسکریپت :
<script src>
و تنظیم محتوای متنی عناصر<script>
. - تولید HTML از یک رشته :
- اجرای محتوای افزونه :
- کامپایل کد جاوا اسکریپت در زمان اجرا :
-
eval
-
setTimeout
-
setInterval
-
new Function()
-
Trusted Type ها از شما می خواهند که داده ها را قبل از ارسال به این توابع سینک پردازش کنید. فقط استفاده از یک رشته ناموفق است، زیرا مرورگر نمی داند که داده ها قابل اعتماد هستند یا خیر:
anElement.innerHTML = location.href;
برای نشان دادن اینکه داده ها به طور ایمن پردازش شده اند، یک شی خاص ایجاد کنید - یک Trusted Type.
anElement.innerHTML = aTrustedHTML;
Trusted Types به طور قابل توجهی سطح حمله DOM XSS برنامه شما را کاهش می دهد. بررسیهای امنیتی را ساده میکند و به شما امکان میدهد بررسیهای امنیتی مبتنی بر نوع را که هنگام کامپایل، لنت کردن یا بستهبندی کدتان در زمان اجرا در مرورگر انجام میشود، اعمال کنید.
نحوه استفاده از Trusted Types
برای گزارش های نقض خط مشی امنیت محتوا آماده شوید
میتوانید یک جمعآوری گزارش، مانند گزارشدهی منبع باز-api-processor یا go-csp-collector را مستقر کنید، یا از یکی از معادلهای تجاری استفاده کنید. همچنین میتوانید با استفاده از ReportingObserver، موارد ثبت سفارشی و نقض اشکالزدایی را در مرورگر اضافه کنید:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
یا با افزودن شنونده رویداد:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
یک هدر CSP فقط گزارش اضافه کنید
هدر HTTP Response زیر را به اسنادی که میخواهید به Trusted Types منتقل کنید اضافه کنید:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
اکنون همه تخلفات به //my-csp-endpoint.example
گزارش می شود، اما وب سایت به کار خود ادامه می دهد. بخش بعدی نحوه عملکرد //my-csp-endpoint.example
را توضیح می دهد.
تخلفات Trusted Types را شناسایی کنید
از این پس، هر بار که Trusted Types تخلفی را تشخیص میدهد، مرورگر گزارشی را به یک report-uri
پیکربندی شده ارسال میکند. به عنوان مثال، هنگامی که برنامه شما یک رشته را به innerHTML
ارسال می کند، مرورگر گزارش زیر را ارسال می کند:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
این می گوید که در https://my.url.example/script.js
در خط 39، innerHTML
با رشته ای که با <img src=x
شروع می شود فراخوانی می شود. این اطلاعات باید به شما کمک کند تا مشخص کنید کدام بخش از کد ممکن است DOM XSS را معرفی کند و نیاز به تغییر داشته باشد.
تخلفات را برطرف کنید
چند گزینه برای رفع نقض Trusted Type وجود دارد. میتوانید کد توهینآمیز را حذف کنید ، از یک کتابخانه استفاده کنید ، یک خطمشی Trusted Type ایجاد کنید یا به عنوان آخرین راهحل، یک خطمشی پیشفرض ایجاد کنید .
کد متخلف را دوباره بنویسید
این امکان وجود دارد که کد نامنطبق دیگر مورد نیاز نباشد، یا بدون توابعی که باعث نقض می شود، بازنویسی شود:
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
از کتابخانه استفاده کنید
برخی از کتابخانه ها قبلاً Trusted Types را تولید می کنند که می توانید آنها را به توابع سینک منتقل کنید. به عنوان مثال، می توانید از DOMPurify برای پاکسازی یک قطعه HTML استفاده کنید و بارهای XSS را حذف کنید.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify از Trusted Types پشتیبانی میکند و HTML پاکشده را که در یک شی TrustedHTML
پیچیده شده است، برمیگرداند تا مرورگر تخلفی ایجاد نکند.
یک خط مشی Trusted Type ایجاد کنید
گاهی اوقات نمیتوانید کدی که باعث نقض میشود را حذف کنید، و هیچ کتابخانهای برای پاکسازی مقدار و ایجاد یک Trusted Type برای شما وجود ندارد. در این موارد، می توانید خودتان یک شی Trusted Type ایجاد کنید.
ابتدا یک خط مشی ایجاد کنید. خطمشیها کارخانههایی برای انواع معتمد هستند که قوانین امنیتی خاصی را در ورودی خود اعمال میکنند:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
این کد یک خط مشی به نام myEscapePolicy
ایجاد می کند که می تواند اشیاء TrustedHTML
را با استفاده از تابع createHTML()
تولید کند. قوانین تعریف شده HTML-escape <
کاراکترها برای جلوگیری از ایجاد عناصر جدید HTML.
از این خط مشی استفاده کنید:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
از یک خط مشی پیش فرض استفاده کنید
گاهی اوقات نمی توانید کد توهین آمیز را تغییر دهید، برای مثال، اگر در حال بارگیری یک کتابخانه شخص ثالث از یک CDN هستید. در این صورت، از یک خط مشی پیش فرض استفاده کنید:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
خطمشی با نام default
در هر جایی که رشتهای در سینک استفاده میشود استفاده میشود که فقط Trusted Type را میپذیرد.
به اجرای خطمشی امنیت محتوا بروید
وقتی برنامه شما دیگر تخلف ایجاد نمی کند، می توانید اجرای Trusted Types را شروع کنید:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
اکنون، مهم نیست که برنامه وب شما چقدر پیچیده است، تنها چیزی که می تواند آسیب پذیری DOM XSS را معرفی کند، کد موجود در یکی از خط مشی های شما است و می توانید با محدود کردن ایجاد خط مشی ، آن را حتی بیشتر قفل کنید.