CSP چیست و چطور کار میکند؟
سیاست امنیت محتوا (Content Security Policy) یک هدرِ HTTP است که به مرورگر میگوید محتوای صفحه (بهویژه اسکریپتها) از چه منابعی مجاز به بارگذاری و اجراست. هدفِ اصلیاش کاهشِ اثرِ XSS است: حتی اگر مهاجم بتواند اسکریپتی تزریق کند، CSP میتواند جلوی اجرای آن را بگیرد. CSP همچنین با دستورِ frame-ancestors جای X-Frame-Options را برای ضدِکلیکربایی میگیرد و با form-action مقصدِ ارسالِ فرمها را محدود میکند.
نکتهی محوری این است که CSP یک کاهشدهنده است، نه رفع؛ و مدلِ سنتیِ آن که بر فهرست سفیدِ دامنهها استوار بود، در عمل شکست خورده است.
چرا فهرست سفید شکسته است
مبنای تجربیِ این ادعا، مقالهی گوگل با عنوان «CSP مرده است، زنده باد CSP» در کنفرانسِ CCS 2016 است. یافتههای آن مطالعه:
- ۹۴٫۷۲٪ از سیاستهای متمایزِ بررسیشده دارای راهِ دور زدن بودند.
- ۷۵٫۸۱٪ از سیاستهایی که فهرست سفیدِ اسکریپت داشتند، قابل دور زدن بودند.
- ۹۹٫۳۴٪ از میزبانهایی که CSP مستقر کرده بودند، سیاستی داشتند که هیچ محافظتِ واقعی در برابر XSS نمیداد.
- ۱۴ مورد از ۱۵ دامنهای که بیش از همه در فهرست سفیدِ بارگذاریِ اسکریپت قرار میگیرند، دارای اندپوینتهای ناامن بودند.
دلیلِ ساختاری: هر دامنهی مجازی که یک اندپوینتِ JSONP، یک نسخهی AngularJS، یا یک CDN با سرویسِ مسیرِ دلخواه داشته باشد، به یک گجتِ اجرای اسکریپت بدل میشود. نگهداریِ یک فهرست سفید کارِ بیپایانی است که مدافع در آن برنده نمیشود.
سیاست درست: nonce و strict-dynamic
رویکردِ توصیهشدهی OWASP، سیاستِ سختگیرانهی مبتنی بر nonce است:
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';- nonce باید یک مقدارِ تصادفیِ رمزنگارانه و تازه بهازای هر پاسخِ HTTP باشد؛ nonceِ ثابت یا قابلپیشبینی بیارزش است.
- strict-dynamic اعتماد را از یک اسکریپتِ دارای nonce به اسکریپتهایی که آن بهصورت پویا میسازد منتقل میکند؛ همین است که سیاستِ سخت را در برنامههای دارای loader/bundler بدونِ فهرست سفید قابلِ استقرار میکند.
- object-src 'none' دور زدنهای مبتنی بر پلاگین را میبندد.
- base-uri 'none' جلوی تزریقِ
<base>برای ربودنِ آدرسهای نسبیِ اسکریپت را میگیرد.
هشدارِ OWASP: از میدلوری که کورکورانه به هر تگِ اسکریپت nonce میچسباند بپرهیزید، چون اسکریپتِ تزریقشدهی مهاجم هم nonce میگیرد. ابزارِ سنجش، CSP Evaluatorِ گوگل است.
CSP آسیبپذیری را رفع نمیکند
«CSP، XSS را رفع میکند.» نه — CSP یک کاهشدهنده است، نه رفع. اثرِ XSS را کم میکند اما خودِ باگ را از بین نمیبرد. در گزارش تست نفوذ باید XSS را با شدتِ مستقلِ خودش گزارش کرد، حتی اگر CSP وجود داشته باشد. مهمتر: سیاستی که در script-src دارای 'unsafe-inline' باشد، عملاً هیچ محافظتی در برابر XSS نمیدهد؛ همینطور 'unsafe-eval' برای سینکهای مبتنی بر eval. همچنین CSP در برابر DOM XSS ای که فقط از اسکریپتِ هممبدأ استفاده میکند محافظت نمیدهد؛ آنجا Trusted Types با دستورِ require-trusted-types-for 'script' لازم است.
روش استقرار: حالت گزارشفقط
استقرارِ CSP بدونِ شکستنِ سایت نیازمندِ روش است. ابتدا سیاست را در حالتِ Content-Security-Policy-Report-Only منتشر کنید؛ در این حالت مرورگر چیزی را مسدود نمیکند اما نقضها را گزارش میدهد. با report-to/report-uri این گزارشها را جمع کنید، سیاست را بر اساس آنها اصلاح کنید، و تنها پس از اطمینان به حالتِ اجرایی (Content-Security-Policy) سوئیچ کنید. گزارشهای نقض در کنسولِ مرورگر هم دیده میشوند و سریعترین راهِ فهمِ رفتارِ واقعیِ سیاستاند.
ارتباط با OWASP و CWE
CSP یک کنترلِ دفاعی است نه یک آسیبپذیری؛ اما مستقیماً به XSS مربوط میشود که زیر A05:2025 تزریق (CWE-79) قرار دارد. نبودِ هدرهای امنیتی مثل CSP نیز میتواند زیر A02:2025 (پیکربندی نادرست) گزارش شود. برای دفاعِ کاملِ XSS، CSP را با کدگذاریِ خروجیِ متناسب با زمینه و Trusted Types ترکیب کنید — نه بهجای آنها. مطالعهی امنیت برنامهی وب تصویرِ کاملتری میدهد.
پرسشهای متداول
آیا CSP جلوی XSS را میگیرد؟
CSP اثرِ XSS را کاهش میدهد اما آن را رفع نمیکند. یک سیاستِ سختگیرانهی مبتنی بر nonce میتواند اجرای اسکریپتِ تزریقشده را مسدود کند، اما اگر 'unsafe-inline' در سیاست باشد عملاً هیچ محافظتی نمیدهد. XSS باید در کد رفع و مستقلاً گزارش شود.
چرا CSP مبتنی بر فهرست سفید توصیه نمیشود؟
چون در عمل شکسته است. مطالعهی گوگل در CCS 2016 نشان داد ۹۴٫۷۲٪ سیاستها راهِ دور زدن داشتند و ۱۴ مورد از ۱۵ دامنهی پرکاربردِ فهرست سفید، اندپوینتِ ناامن داشتند. رویکردِ درست، nonce + strict-dynamic است.
CSP را چطور بدون خرابکردن سایت مستقر کنم؟
ابتدا با هدرِ Content-Security-Policy-Report-Only؛ در این حالت مرورگر چیزی را مسدود نمیکند اما نقضها را گزارش میدهد. پس از جمعآوری و اصلاح بر اساس گزارشها، به هدرِ اجراییِ Content-Security-Policy سوئیچ کنید.
strict-dynamic چه میکند؟
اعتماد را از یک اسکریپتِ دارای nonce به اسکریپتهایی که آن بهصورت پویا بارگذاری میکند منتقل میکند. همین باعث میشود سیاستِ سختگیرانه در برنامههای دارای loader و bundler، بدونِ نیاز به فهرست سفیدِ دامنه، قابل استقرار باشد.