Browser Support
دستورگاننویسی بینسایتی (XSS)، توانایی تزریق دستورگان مخرب به برنامه وب، بیشاز یک دهه است که یکی از بزرگترین آسیبپذیریهای امنیتی وب بوده است.
خطمشی امنیتی محتوا (CSP)
یک لایه امنیتی اضافه است که به کاهش XSS کمک میکند. برای پیکربندی CSP،
سرایند HTTP Content-Security-Policy را به صفحه وب اضافه کنید و مقادیری را تنظیم کنید که
کنترل میکنند نماینده کاربر چه منابعی را میتواند برای آن صفحه بار کند.
این صفحه توضیح میدهد که چگونه از CSP مبتنی بر نانس یا درهمسازی برای کاهش XSS استفاده کنید، بهجای CSPهای مبتنی بر فهرست مجاز میزبان که معمولاً استفاده میشوند و اغلب صفحه را درمعرض XSS قرار میدهند زیرا میتوان آنها را در اکثر پیکربندیها دور زد.
اصطلاح کلیدی: عدد یکبارمصرف عددی تصادفی است که فقط یکبار استفاده میشود و میتوانید از آن برای علامتگذاری
برچسب <script> بهعنوان برچسب مورداعتماد استفاده کنید.
اصطلاح کلیدی: تابع چکیدهساز یک تابع ریاضی است که مقدار ورودی را به مقدار عددی فشردهای بهنام چکیده تبدیل میکند. میتوانید از درهمسازی (مثلاً SHA-256) برای علامتگذاری برچسب <script> درونخطی بهعنوان معتمد استفاده کنید.
«خطمشی امنیتی محتوا» مبتنی بر نانس یا درهمسازی اغلب CSP سختگیرانه نامیده میشود. وقتی برنامهای از CSP سختگیرانه استفاده میکند، مهاجمانی که نقصهای تزریق HTML را پیدا میکنند معمولاً نمیتوانند از آنها برای مجبور کردن مرورگر به اجرای اسکریپتهای مخرب در سند آسیبپذیر استفاده کنند. دلیلش این است که CSP سختگیرانه فقط به اسکریپتهای درهمسازیشده یا اسکریپتهایی با مقدار نانس صحیح تولیدشده در سرور اجازه میدهد، بنابراین مهاجمان نمیتوانند اسکریپت را بدون دانستن نانس صحیح برای پاسخ دادهشده اجرا کنند.
چرا باید از CSP سختگیرانه استفاده کنید؟
اگر سایت شما ازقبل دارای CSP است که شبیه script-src www.googleapis.com است،
احتمالاً دربرابر بینسایتی مؤثر نیست. این نوع CSP را
CSP فهرست مجاز مینامند. این روشها به سفارشیسازی زیادی نیاز دارند و مهاجمان میتوانند آنها را
دور بزنند.
«خطمشیهای امنیتی محتوا» سختگیرانه مبتنی بر هشها یا نانسهای رمزنگاری از این تلهها اجتناب میکنند.
ساختار سختگیرانه «خطمشی امنیتی محتوا»
یک «خطمشی امنیتی محتوای» سختگیرانه پایه از یکی از سرایندهای پاسخ HTTP زیر استفاده میکند:
CSP سختگیرانه مبتنی بر Nonce
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
خطمشی امنیتی محتوای سختگیرانه مبتنی بر درهمسازی
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
ویژگیهای زیر باعث میشود CSP مانند این «سختگیرانه» و درنتیجه ایمن باشد:
- این خطمشی از نانس
'nonce-{RANDOM}'یا درهمسازی'sha256-{HASHED_INLINE_SCRIPT}'استفاده میکند تا نشان دهد توسعهدهنده سایت به کدام برچسبهای<script>برای اجرا در مرورگر کاربر اعتماد دارد. - این ویژگی
'strict-dynamic'را تنظیم میکند تا با اجازه دادن خودکار به اجرای دستورگانهایی که دستورگان معتبری ایجاد میکند، تلاش برای استقرار CSP مبتنی بر هش یا عدد یکبارمصرف را کاهش دهد. این کار همچنین استفاده از اکثر ابزارکها و کتابخانههای جاوا اسکریپت طرف سوم را رفع انسداد میکند. - این خطمشی براساس فهرستهای مجاز نشانی وب نیست، بنابراین از دور زدنهای رایج CSP رنج نمیبرد.
- این خطمشی دستورگانهای بهخط غیرقابلاعتماد مانند کنترلکنندههای رویداد بهخط یا URIهای
javascript:را مسدود میکند. - این خطمشی
object-srcرا محدود میکند تا افزایههای خطرناکی مثل Flash را غیرفعال کند. - این خطمشی
base-uriرا محدود میکند تا از تزریق برچسبهای<base>جلوگیری کند. این کار از تغییر مکان اسکریپتهای بارشده از نشانیهای وب نسبی توسط مهاجمان جلوگیری میکند.
اتخاذ «خطمشی امنیتی محتوا» سختگیرانه
برای اتخاذ CSP سختگیرانه، باید:
- تصمیم بگیرید که آیا برنامهتان باید CSP مبتنی بر هش یا عدد یکبارمصرف تنظیم کند یا نه.
- «خطمشی امنیتی محتوا» را از بخش ساختار خطمشی امنیتی محتوای سختگیرانه کپی کنید و آن را بهعنوان سرایند پاسخ در سراسر برنامهتان تنظیم کنید.
- الگوهای HTML و کد سمت کارخواه را بازسازی کنید تا الگوهایی را که با CSP ناسازگار هستند بردارید.
- CSP خود را مستقر کنید.
میتوانید از ممیزی روالهای مطلوب Lighthouse (نسخه ۷.۳.۰ و بالاتر با پرچم --preset=experimental) درطول این فرایند استفاده کنید تا بررسی کنید آیا سایت شما CSP دارد یا خیر، و آیا بهاندازه کافی سختگیرانه است که دربرابر XSS مؤثر باشد یا خیر.
مرحله ۱: تصمیم بگیرید که آیا به CSP مبتنی بر هش یا عدد یکبارمصرف نیاز دارید یا نه
نحوه عملکرد دو نوع CSP سختگیرانه در اینجا آمده است:
«خطمشی امنیتی محتوا» مبتنی بر شماره تکمنظوره
با CSP مبتنی بر عدد یکبارمصرف، یک عدد تصادفی در زمان اجرا تولید میکنید، آن را در CSP خود قرار میدهید و آن را با هر برچسب اسکریپت در صفحه خود مرتبط میکنید. مهاجم نمیتواند اسکریپت مخربی را در صفحه شما اضافه یا اجرا کند، زیرا برای این کار باید عدد تصادفی صحیح را برای آن اسکریپت حدس بزند. این روش فقط زمانی کار میکند که عدد قابلحدس زدن نباشد و در زمان اجرا برای هر پاسخ بهطور جدید تولید شود.
از CSP مبتنی بر عدد یکبارمصرف برای صفحههای HTML ارائهشده در سرور استفاده کنید. برای این صفحهها، میتوانید برای هر پاسخ عدد تصادفی جدیدی ایجاد کنید.
خطمشی امنیتی محتوا مبتنی بر درهمسازی
برای CSP مبتنی بر درهمسازی، درهمسازی هر برچسب دستورگان درونخطی به CSP اضافه میشود. هر اسکریپت درهمسازی متفاوتی دارد. مهاجم نمیتواند اسکریپت مخربی را در صفحهتان اضافه یا اجرا کند، زیرا برای اجرای آن اسکریپت، درهمسازی آن باید در CSP شما باشد.
از CSP مبتنی بر درهمسازی برای صفحههای HTML که بهصورت ایستا ارائه میشوند یا صفحههایی که باید در حافظه پنهان ذخیره شوند استفاده کنید. برای مثال، میتوانید از CSP مبتنی بر درهمسازی برای برنامههای وب تکصفحهای ساختهشده با چارچوبهایی مانند Angular، React، یا موارد دیگر استفاده کنید که بهصورت ایستا و بدون پردازش سمت سرور ارائه میشوند.
مرحله ۲: تنظیم CSP سختگیرانه و آماده کردن اسکریپتها
هنگام تنظیم CSP، چند گزینه دارید:
- حالت فقط گزارش (
Content-Security-Policy-Report-Only) یا حالت اجرا (Content-Security-Policy). در حالت فقط گزارش، CSP هنوز منابع را مسدود نمیکند، بنابراین هیچچیز در سایت شما خراب نمیشود، اما میتوانید خطاها را ببینید و برای هر چیزی که مسدود شده است گزارش دریافت کنید. بهصورت محلی، وقتی CSP خود را تنظیم میکنید، این موضوع واقعاً اهمیتی ندارد، زیرا هر دو حالت خطاها را در کنسول مرورگر به شما نشان میدهند. اگر چیزی باشد، حالت اجرا میتواند به شما کمک کند منابعی را که پیشنویس CSP شما مسدود میکند پیدا کنید، زیرا مسدود کردن یک منبع میتواند باعث شود صفحه شما خراب بهنظر برسد. حالت فقط گزارشدهی در مراحل بعدی فرایند مفیدتر میشود (مرحله ۵ را ببینید). - سرصفحه یا تگ HTML
<meta>. برای توسعه محلی، برچسب<meta>میتواند برای اصلاح CSP شما و دیدن سریع تأثیر آن بر سایتتان راحتتر باشد. بااینحال:- بعداً، هنگام استقرار CSP در تولید، توصیه میکنیم آن را بهعنوان سرایند HTTP تنظیم کنید.
- اگر میخواهید CSP خود را در حالت فقط گزارش تنظیم کنید، باید آن را بهعنوان سرصفحه تنظیم کنید، زیرا متا تگهای CSP از حالت فقط گزارش پشتیبانی نمیکنند.
سرصفحه پاسخ HTTP Content-Security-Policy زیر را در برنامهتان تنظیم کنید:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
تولید یکبارکد برای CSP
«نانس» عدد تصادفیای است که فقط یکبار در هر بارگیری صفحه استفاده میشود. CSP مبتنی بر نانس فقط درصورتی میتواند XSS را کاهش دهد که مهاجمان نتوانند مقدار نانس را حدس بزنند. یک CSP nonce باید:
- مقدار تصادفی قوی ازنظر رمزنگاری (در حالت ایدهآل با طول ۱۲۸+ بیت)
- برای هر پاسخ بهتازگی تولید شده است
- کدبندیشده با Base64
در اینجا چند نمونه از نحوه افزودن CSP nonce در چارچوبهای سمت سرور آورده شده است:
- Django (python)
- اکسپرس (جاوا اسکریپت):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
افزودن مشخصه nonce به عناصر <script>
با CSP مبتنی بر nonce، هر عنصر <script> باید
مشخصه nonce داشته باشد که با مقدار nonce تصادفی
مشخصشده در سرصفحه CSP مطابقت داشته باشد. همه اسکریپتها میتوانند nonce یکسانی داشته باشند. اولین قدم این است که این مشخصهها را به همه متنها اضافه کنید تا
CSP آنها را مجاز کند.
سرصفحه پاسخ HTTP Content-Security-Policy زیر را در برنامهتان تنظیم کنید:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
برای چند دستورگان درونخطی، نحو بهصورت زیر است:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'.
بار کردن پویای دستورگانهای منبعیابیشده
میتوانید دستورگانهای شخص ثالث را بهطور پویا بااستفاده از دستورگان درونخطی بار کنید.
<script>
var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];
scripts.forEach(function(scriptUrl) {
var s = document.createElement('script');
s.src = scriptUrl;
s.async = false; // to preserve execution order
document.head.appendChild(s);
});
</script>{HASHED_INLINE_SCRIPT} را
جایگزین کنید. برای کاهش تعداد درهمسازیها، میتوانید همه دستورات درونخطی را در یک دستور ادغام کنید. برای دیدن این مورد در عمل، به این
نمونه
و کد آن مراجعه کنید.
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
integrity که با منبع مجاز مطابقت داشته باشد ندارند.
ملاحظات بار کردن اسکریپت
نمونه اسکریپت درونخطی s.async = false را اضافه میکند تا مطمئن شود
foo قبلاز bar اجرا میشود، حتی اگر
bar اول بار شود. در این قطعه کد، s.async = false
تجزیهکننده را هنگام بار شدن اسکریپتها مسدود نمیکند، زیرا اسکریپتها
بهصورت پویا اضافه میشوند. تجزیهکننده فقط درحین اجرای اسکریپتها متوقف میشود،
همانطور که برای اسکریپتهای async انجام میشود. اما با این قطعه کد،
بهخاطر داشته باشید:
-
ممکن است یک یا هر دو اسکریپت قبلاز اتمام بارگیری سند اجرا شوند. اگر میخواهید سند تا زمان اجرای اسکریپتها آماده باشد، قبلاز افزودن اسکریپتها، منتظر رویداد
DOMContentLoadedبمانید. اگر این امر باعث مشکل عملکرد شود، چون بارگیری اسکریپتها بهاندازه کافی زود شروع نمیشود، از برچسبهای پیشبارگیری در اوایل صفحه استفاده کنید. -
defer = trueکاری انجام نمیدهد. اگر به این رفتار نیاز دارید، هروقت لازم بود اسکریپت را بهصورت دستی اجرا کنید.
مرحله ۳: بازسازی کردن الگوهای HTML و کد سمت کارخواه
از گردانندههای رویداد درونخطی (مثل onclick="…"، onerror="…") و شناسه منبع یکسان جاوا اسکریپت (<a href="javascript:…">) میتوان برای اجرای اسکریپتها استفاده کرد. این یعنی
مهاجمی که اشکال XSS را پیدا میکند میتواند این نوع HTML را تزریق کند و جاوا اسکریپت مخرب
اجرا کند. یک CSP مبتنی بر هش یا نانس استفاده از این نوع نشانهگذاری را ممنوع میکند.
اگر سایتتان از هرکدام از این الگوها استفاده میکند، باید آنها را به جایگزینهای ایمنتر
بازسازی کنید.
اگر CSP را در مرحله قبلی فعال کرده باشید، هربار که CSP الگوی ناسازگاری را مسدود کند، میتوانید موارد نقض CSP را در کنسول ببینید.
در بیشتر موارد، راهحل ساده است:
بازسازی کردن کد مدیران رویداد درونخطی
<span id="things">A thing.</span>
<script nonce="${nonce}">
document.getElementById('things').addEventListener('click', doThings);
</script><span onclick="doThings();">A thing.</span>
بازسازی کردن کد javascript: URI
<a id="foo">foo</a>
<script nonce="${nonce}">
document.getElementById('foo').addEventListener('click', linkClicked);
</script><a href="javascript:linkClicked()">foo</a>
eval() را از جاوا اسکریپت خود بردارید
اگر برنامهتان از eval() برای تبدیل سریالسازیهای رشته JSON به اشیاء JS استفاده میکند، باید چنین مواردی را به JSON.parse() بازسازی کنید که سریعتر نیز هست.
اگر نمیتوانید همه موارد استفاده از eval() را بردارید، همچنان میتوانید CSP سختگیرانه مبتنی بر عدد یکباراستفاده تنظیم کنید، اما باید از کلیدواژه CSP 'unsafe-eval' استفاده کنید که باعث میشود خطمشی شما کمی کمتر ایمن باشد.
میتوانید این مثالها و مثالهای بیشتری از این بازسازی کد را در این کدلب CSP سختگیرانه پیدا کنید:
مرحله ۴ (اختیاری): افزودن بازگشتها برای پشتیبانی از نسخههای قدیمی مرورگر
Browser Support
اگر لازم است از نسخههای قدیمیتر مرورگر پشتیبانی کنید:
- استفاده از
strict-dynamicمستلزم افزودنhttps:بهعنوان جایگزین برای نسخههای قبلی Safari است. وقتی این کار را انجام میدهید:- همه مرورگرهایی که از
strict-dynamicپشتیبانی میکنند،https:را نادیده میگیرند، بنابراین این کار قدرت خطمشی را کاهش نمیدهد. - در مرورگرهای قدیمی، دستورگانهای دارای منبع خارجی فقط درصورتی میتوانند بار شوند که از
مبدأ HTTPS باشند. این روش نسبتبه CSP سختگیرانه امنیت کمتری دارد، اما همچنان از برخی دلایل رایج XSS مانند تزریق
javascript:URI جلوگیری میکند.
- همه مرورگرهایی که از
- برای اطمینان از سازگاری با نسخههای بسیار قدیمی مرورگر (بیشاز ۴ سال)، میتوانید
unsafe-inlineرا بهعنوان جایگزین اضافه کنید. همه مرورگرهای اخیرunsafe-inlineرا نادیده میگیرند اگر nonce یا درهمسازی CSP وجود داشته باشد.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
مرحله ۵: استقرار CSP
پساز تأیید اینکه CSP شما هیچ اسکریپت قانونی را در محیط توسعه محلیتان مسدود نمیکند، میتوانید CSP خود را در محیط آمادهسازی و سپس در محیط تولید مستقر کنید:
- (اختیاری) CSP خود را بااستفاده از سرصفحه
Content-Security-Policy-Report-Onlyدر حالت فقط گزارش مستقر کنید. حالت فقط گزارشدهی برای آزمایش تغییرات بالقوه مخرب، مانند CSP جدید در تولید، قبلاز شروع اعمال محدودیتهای CSP مفید است. در حالت فقط گزارش، CSP شما بر رفتار برنامهتان تأثیر نمیگذارد، اما مرورگر همچنان خطاهای کنسول و گزارشهای نقض را هنگام مواجهه با الگوهای ناسازگار با CSP شما تولید میکند، بنابراین میتوانید ببینید چه چیزی برای کاربران نهایی شما خراب شده است. برای اطلاعات بیشتر، به Reporting API مراجعه کنید. - وقتی مطمئن شدید که CSP شما سایتتان را برای کاربران نهایی خراب نمیکند،
CSP خود را بااستفاده از سرایند پاسخ
Content-Security-Policyمستقر کنید. توصیه میکنیم CSP خود را بااستفاده از سرایند HTTP در سمت سرور تنظیم کنید، زیرا این روش ایمنتر از برچسب<meta>است. پساز تکمیل این مرحله، CSP شما شروع به محافظت از برنامهتان دربرابر XSS میکند.
محدودیتها
یک CSP سختگیرانه معمولاً یک لایه امنیتی قوی اضافه میکند که به کاهش XSS کمک میکند. در بیشتر موارد، CSP با رد کردن الگوهای خطرناک مثل URIهای javascript:، سطح حمله را بهطور قابلتوجهی کاهش میدهد. بااینحال، براساس نوع
CSP که استفاده میکنید (nonce، درهمسازی، با یا بدون 'strict-dynamic')،
مواردی وجود دارد که CSP بهخوبی از برنامهتان محافظت نمیکند:
- اگر از nonce برای اسکریپت استفاده کنید، اما تزریق مستقیماً در بدنه یا پارامتر
srcآن عنصر<script>انجام شود. - اگر تزریقهایی در مکانهای اسکریپتهای ایجادشده بهصورت پویا
(
document.createElement('script')) وجود داشته باشد، ازجمله در هر تابع کتابخانهای که گرههایscriptDOM را براساس مقادیر آرگومانهایشان ایجاد میکند. این شامل برخیاز میاناهای برنامهسازی کاربردی رایج مانند.html()jQuery، و همچنین.get()و.post()در jQuery < 3.0 میشود. - اگر تزریق الگو در برنامههای قدیمی AngularJS وجود داشته باشد. مهاجمی که بتواند به الگوی AngularJS تزریق کند میتواند از آن برای اجرای جاوا اسکریپت دلخواه استفاده کند.
- اگر خطمشی حاوی
'unsafe-eval'باشد، تزریق بهeval()،setTimeout()، و چند میانای برنامهسازی کاربردی دیگر که بهندرت استفاده میشوند.
توسعهدهندگان و مهندسان امنیت باید درطول بازبینی کد و ممیزیهای امنیتی توجه ویژهای به این الگوها داشته باشند. میتوانید جزئیات بیشتری درباره این موارد در خطمشی امنیت محتوا: آشفتگی موفق بین تقویت و کاهش پیدا کنید.