با Trusted Types از آسیب‌پذیری‌های برنامه‌نویسی متقابل مبتنی بر DOM جلوگیری کنید

کریستوف کوتوویچ
Krzysztof Kotowicz

پشتیبانی مرورگر

  • کروم: 83.
  • لبه: 83.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

منبع

برنامه نویسی متقابل سایت مبتنی بر 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 با قفل کردن عملکردهای مخاطره آمیز سینک زیر کار می کنند. ممکن است قبلاً برخی از آنها را بشناسید، زیرا فروشندگان مرورگر و چارچوب های وب شما را از استفاده از این ویژگی ها به دلایل امنیتی دور می کنند.

Trusted Type ها از شما می خواهند که داده ها را قبل از ارسال به این توابع سینک پردازش کنید. فقط استفاده از یک رشته ناموفق است، زیرا مرورگر نمی داند که داده ها قابل اعتماد هستند یا خیر:

نکن
anElement.innerHTML  = location.href;
با فعال بودن Trusted Types، مرورگر یک TypeError پرتاب می کند و از استفاده از سینک DOM XSS با یک رشته جلوگیری می کند.

برای نشان دادن اینکه داده ها به طور ایمن پردازش شده اند، یک شی خاص ایجاد کنید - یک Trusted Type.

انجام دهید
anElement.innerHTML = aTrustedHTML;
  
با فعال کردن Trusted Types، مرورگر یک شی TrustedHTML را برای سینک‌هایی که انتظار قطعه‌های HTML را دارند، می‌پذیرد. همچنین اشیاء TrustedScript و TrustedScriptURL برای سایر سینک های حساس وجود دارد.

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, '&lt;')
  });
}

این کد یک خط مشی به نام 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;  // '&lt;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 را معرفی کند، کد موجود در یکی از خط مشی های شما است و می توانید با محدود کردن ایجاد خط مشی ، آن را حتی بیشتر قفل کنید.

در ادامه مطلب