پیاده سازی اشکال زدایی CSP و Trusted Types در Chrome DevTools

کاترینا پروکوپنکو
Kateryna Prokopenko
آلفونسو کاستانیو
Alfonso Castaño

این پست وبلاگ درباره اجرای پشتیبانی DevTools برای اشکال زدایی مسائل مربوط به سیاست امنیتی محتوا (CSP) با کمک برگه Issues است که اخیراً معرفی شده است .

کار پیاده سازی در دوره 2 کارآموزی انجام شد: 1. در دوره اول، چارچوب گزارش کلی را ایجاد کردیم و پیام های موضوع را برای 3 مورد نقض CSP طراحی کردیم. 2. در طول مورد دوم، مسائل Trusted Type را در کنار برخی از ویژگی های تخصصی DevTools برای اشکال زدایی Trusted Types اضافه کردیم.

خط مشی امنیت محتوا چیست؟

سیاست امنیت محتوا (CSP) اجازه می دهد تا رفتارهای خاصی را در یک وب سایت برای افزایش امنیت محدود کنید. به عنوان مثال، CSP را می توان برای غیرمجاز کردن اسکریپت های درون خطی یا غیرمجاز کردن eval استفاده کرد، که هر دو سطح حمله را برای حملات Cross-Site Scripting (XSS) کاهش می دهند. برای معرفی دقیق CSP، اینجا را بخوانید.

یک CSP به ویژه جدید، خط مشی Trusted Types (TT) است که یک تجزیه و تحلیل پویا را امکان پذیر می کند که می تواند به طور سیستماتیک از دسته بزرگی از حملات تزریق در وب سایت ها جلوگیری کند. برای دستیابی به این هدف، TT از یک وب‌سایت در کنترل کد جاوا اسکریپت خود پشتیبانی می‌کند تا فقط انواع خاصی از چیزها را به سینک‌های DOM مانند innerHTML اختصاص دهد.

یک وب‌سایت می‌تواند یک خط‌مشی امنیتی محتوا را با گنجاندن یک هدر HTTP خاص فعال کند. به عنوان مثال، هدر content-security-policy: require-trusted-types-for 'script'; trusted-types default خط مشی TT را برای یک صفحه فعال می کند.

هر خط مشی می تواند در یکی از این حالت ها عمل کند:

  • حالت اجباری - که در آن هر نقض خط مشی یک خطا است،
  • حالت فقط گزارش - که پیام خطا را به عنوان یک هشدار گزارش می دهد، اما باعث خرابی در صفحه وب نمی شود.

پیاده سازی مسائل مربوط به خط مشی امنیت محتوا در تب Issues

هدف از این کار بهبود تجربه اشکال زدایی برای مسائل CSP بود. هنگام بررسی مسائل جدید، تیم DevTools تقریباً این روند را دنبال می کند:

  1. تعریف داستان های کاربر مجموعه‌ای از داستان‌های کاربر را در قسمت جلویی DevTools شناسایی کنید که چگونگی نیاز یک توسعه‌دهنده وب برای بررسی مشکل را پوشش می‌دهد.
  2. اجرای Front-end . بر اساس داستان های کاربر، مشخص کنید که کدام بخش از اطلاعات برای بررسی موضوع در قسمت جلویی مورد نیاز است (به عنوان مثال درخواست مرتبط، نام یک کوکی، یک خط در یک اسکریپت یا فایل html و غیره).
  3. تشخیص مسئله . مکان‌هایی را در مرورگر که می‌توان مشکل را در Chrome شناسایی کرد و مکان را برای گزارش یک مشکل از جمله اطلاعات مربوطه از مرحله (2) تنظیم کنید.
  4. مسائل را ذخیره و نمایش دهید . مسائل را در مکانی مناسب ذخیره کنید و پس از باز شدن در دسترس DevTools قرار دهید
  5. طراحی متن مسائل یک متن توضیحی ارائه دهید که به توسعه‌دهنده وب کمک می‌کند تا مشکل را بفهمد، و مهم‌تر از آن مشکل را برطرف کند

مرحله 1: تعریف داستان های کاربر برای مسائل CSP

قبل از اینکه کار پیاده سازی خود را شروع کنیم، یک سند طراحی با داستان های کاربر ایجاد کردیم تا بهتر بفهمیم چه کاری باید انجام دهیم. به عنوان مثال، ما داستان کاربر زیر را یادداشت کردیم:


به‌عنوان یک توسعه‌دهنده، که به‌تازگی متوجه شدم بخشی از وب‌سایت من مسدود شده است، می‌خواهم:- - ...پیدا کنم که آیا CSP دلیلی برای مسدود شدن iframe/تصاویر در وب‌سایت من است - ...بیاموزم که کدام دستورالعمل CSP باعث این مشکل می‌شود. مسدود کردن یک منبع خاص - ... می دانم چگونه CSP وب سایت خود را تغییر دهم تا امکان نمایش منابع مسدود شده فعلی / اجرای js های مسدود شده فعلی را فراهم کنم.


برای بررسی این داستان کاربر، چند صفحه وب نمونه ساده ایجاد کردیم که نقض های CSP مورد علاقه ما را نشان می داد و صفحات نمونه را بررسی کردیم تا خودمان با این فرآیند آشنا شویم. در اینجا برخی از صفحات وب نمونه وجود دارد (نمونه نمایشی را با باز کردن تب Issues باز کنید):

با استفاده از این فرآیند، متوجه شدیم که مکان منبع، مهمترین بخش اطلاعات برای اشکال زدایی مسائل CSP است. ما همچنین یافتن سریع iframe و درخواست مرتبط را در صورت مسدود شدن منبع مفید یافتیم و اینکه یک پیوند مستقیم به عنصر HTML در پانل عناصر DevTools نیز می تواند مفید باشد.

مرحله 2: پیاده سازی front-end

ما این بینش را به اولین پیش نویس اطلاعاتی تبدیل کردیم که می خواستیم از طریق پروتکل Chrome DevTools (CDP) در دسترس DevTools قرار دهیم:

در زیر گزیده ای از third_party/blink/public/devtools_protocol/browser_protocol.pdl آمده است

 type ContentSecurityPolicyIssueDetails extends object
   properties
     # The url not included in allowed sources.
     optional string blockedURL
     # Specific directive that is violated, causing the CSP issue.
     string violatedDirective
     boolean isReportOnly
     ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
     optional AffectedFrame frameAncestor
     optional SourceCodeLocation sourceCodeLocation
     optional DOM.BackendNodeId violatingNodeId

تعریف فوق اساساً یک ساختار داده JSON را رمزگذاری می کند. به زبان ساده ای به نام PDL (زبان داده پروتکل) نوشته شده است. PDL برای دو منظور استفاده می شود. اول، ما از PDL برای تولید تعاریف TypeScript استفاده می کنیم که DevTools front-end بر آنها تکیه دارد. به عنوان مثال، تعریف PDL فوق رابط TypeScript زیر را ایجاد می کند:

export interface ContentSecurityPolicyIssueDetails {
  /**
  * The url not included in allowed sources.
  */
  blockedURL?: string;
  /**
  * Specific directive that is violated, causing the CSP issue.
  */
  violatedDirective: string;
  isReportOnly: boolean;
  contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
  frameAncestor?: AffectedFrame;
  sourceCodeLocation?: SourceCodeLocation;
  violatingNodeId?: DOM.BackendNodeId;
}

ثانیا، و احتمالا مهمتر از آن، ما یک کتابخانه C++ از تعریفی که تولید و ارسال این ساختارهای داده را از C++ Chromium back-end به DevTools front-end انجام می دهد، تولید می کنیم. با استفاده از آن کتابخانه، یک شی ContentSecurityPolicyIssueDetails را می توان با استفاده از قطعه کد C++ زیر ایجاد کرد:

protocol::Audits::ContentSecurityPolicyIssueDetails::create()
  .setViolatedDirective(d->violated_directive)
  .setIsReportOnly(d->is_report_only)
  .setContentSecurityPolicyViolationType(BuildViolationType(
      d->content_security_policy_violation_type)))
  .build();

وقتی مشخص کردیم که کدام اطلاعات را می‌خواهیم در دسترس قرار دهیم، باید بررسی کنیم که این اطلاعات را از کجا می‌توان از Chromium دریافت کرد.

مرحله 3: تشخیص مشکل

برای در دسترس قرار دادن اطلاعات به پروتکل ابزار توسعه کروم (CDP) در قالبی که در بخش آخر توضیح داده شد، باید مکانی را پیدا کنیم که اطلاعات واقعاً در آن در دسترس بود. خوشبختانه، کد CSP قبلاً دارای یک بطری بود که برای حالت فقط گزارش استفاده می‌شد، که می‌توانستیم به آن متصل شویم: ContentSecurityPolicy::ReportViolation مشکلات را به یک نقطه پایانی گزارش (اختیاری) گزارش می‌کند که می‌تواند در هدر CSP HTTP پیکربندی شود. بیشتر اطلاعاتی که می‌خواستیم گزارش کنیم از قبل در دسترس بود، بنابراین هیچ تغییر بزرگی در قسمت پشتی برای کارکرد ابزار دقیق ما لازم نبود.

مرحله 4: مشکلات را ذخیره و نمایش دهید

یک پیچیدگی کوچک این واقعیت است که ما همچنین می‌خواستیم مشکلاتی را که قبل از باز شدن DevTools رخ داده است گزارش کنیم، مشابه نحوه مدیریت پیام‌های کنسول. این بدان معنی است که ما مشکلات را فوراً به قسمت جلویی گزارش نمی‌کنیم، بلکه از فضای ذخیره‌سازی استفاده می‌کنیم که فارغ از باز بودن یا نبودن DevTools با مشکلاتی پر شده است. پس از باز شدن DevTools (یا هر کلاینت CDP دیگری ضمیمه شده است)، همه مسائل ثبت شده قبلی را می توان از حافظه پخش کرد.

این کار به پایان رسید، و ما اکنون باید بر روی چگونگی آشکار کردن این موضوع در قسمت جلو تمرکز کنیم.

مرحله 5: طراحی متن مسائل

طراحی متن مسائل فرآیندی است که علاوه بر تیم خودمان، چندین تیم را در بر می گیرد، برای مثال، ما اغلب به بینش تیمی که یک ویژگی را پیاده سازی می کند (در این مورد تیم CSP است) و البته تیم DevRel که طراحی می کند، تکیه می کنیم. چگونه توسعه دهندگان وب قرار است با نوع خاصی از مشکل برخورد کنند. متن شماره معمولاً تا زمانی که به پایان برسد کمی اصلاح می شود.

معمولاً تیم DevTools با یک پیش نویس تقریبی از آنچه تصور می کند شروع می کند:


## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site

## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.

Usage of content from not included sources is restricted to strengthen the security of your entire site.

## Specific information

### VIOLATED DIRECTIVES
`img-src 'self'`

### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg

## Specific information
https://web.dev/strict-csp/

پس از تکرار، به این نتیجه رسیدیم:

ALT_TEXT_HERE

همانطور که می بینید، مشارکت تیم ویژگی و DevRel توضیحات را بسیار واضح تر و دقیق تر می کند!

مسائل مربوط به CSP در صفحه شما را نیز می توان در برگه ای که به طور خاص به نقض CSP اختصاص داده شده است، کشف کرد.

اشکال زدایی مشکلات Trusted Types

کار با TT در مقیاس بزرگ بدون ابزارهای توسعه دهنده مناسب می تواند چالش برانگیز باشد.

بهبود چاپ کنسول

هنگامی که ما با اشیاء مورد اعتماد کار می کنیم، می خواهیم حداقل همان مقدار اطلاعات را که برای همتای غیر قابل اعتماد نمایش داده می شود، نمایش دهیم. متأسفانه، در حال حاضر هنگام نمایش یک شی مورد اعتماد، هیچ اطلاعاتی در مورد شی پیچیده شده نمایش داده نمی شود.

به این دلیل است که مقداری که در کنسول نمایش داده می شود به طور پیش فرض از فراخوانی .valueOf() روی شی گرفته شده است. با این حال، در مورد Trusted Type، مقدار بازگشتی چندان مفید نیست. در عوض، ما می‌خواهیم چیزی شبیه به آنچه هنگام فراخوانی .toString() دریافت می‌کنید داشته باشیم. برای رسیدن به این هدف، باید V8 و Blink را تغییر دهیم تا هندلینگ ویژه ای برای اشیاء نوع قابل اعتماد معرفی کنیم.

اگرچه بنا به دلایل تاریخی این مدیریت سفارشی در V8 انجام شد، اما چنین رویکردی دارای معایب مهمی است. اشیاء زیادی وجود دارند که نیاز به نمایش سفارشی دارند اما نوع آنها در سطح JS یکسان است. از آنجایی که V8 یک JS خالص است، نمی تواند مفاهیمی را که با یک Web API مانند Trusted Type مطابقت دارند، تشخیص دهد. به همین دلیل، V8 باید از embedder خود (Blink) برای تشخیص آنها کمک بخواهد.

از این رو، انتقال آن قسمت از کد به Blink یا هر جاسازی یک انتخاب منطقی به نظر می رسد. به غیر از موضوع آشکار، مزایای بسیار دیگری نیز وجود دارد:

  • هر embedder می تواند تولید توضیحات خاص خود را داشته باشد
  • تولید توضیحات از طریق Blink API بسیار ساده تر است
  • Blink به تعریف اصلی شی دسترسی دارد. بنابراین اگر از .toString() برای تولید توضیحات استفاده کنیم، هیچ خطری وجود ندارد که .toString() ممکن است دوباره تعریف شود.

نقض نقض (در حالت فقط گزارش)

در حال حاضر، تنها راه رفع اشکال نقض TT، تعیین نقاط شکست در استثناهای JS است. از آنجایی که نقض TT اجباری باعث ایجاد یک استثنا می شود، این ویژگی می تواند به نوعی مفید باشد. با این حال، در سناریوهای دنیای واقعی شما نیاز به کنترل دقیق تری بر نقض TT دارید. به طور خاص، ما می‌خواهیم فقط در مورد نقض‌های TT (نه استثنائات دیگر) شکسته شود، همچنین در حالت فقط گزارش شکسته شود و بین انواع مختلف نقض TT تمایز قائل شویم.

DevTools در حال حاضر از طیف گسترده ای از نقاط شکست پشتیبانی می کند، بنابراین معماری کاملاً قابل توسعه است. افزودن یک نوع نقطه شکست جدید به تغییرات در backend (Blink)، CDP و frontend نیاز دارد. ما باید یک دستور CDP جدید معرفی کنیم، اجازه دهید آن را setBreakOnTTViolation بنامیم. این دستور توسط frontend استفاده می شود تا به backend بگوید که چه نوع نقض TT باید شکسته شود. پشتیبان، به ویژه InspectorDOMDebuggerAgent ، یک "probe"، onTTViolation() ارائه می دهد که هر بار که نقض TT رخ می دهد فراخوانی می شود. سپس، InspectorDOMDebuggerAgent بررسی می‌کند که آیا این نقض باید یک نقطه شکست را ایجاد کند یا خیر، و اگر اینطور باشد، پیامی به جلویی ارسال می‌کند تا اجرا را متوقف کند.

چه کاری انجام شده است و در ادامه چیست؟

از آنجایی که مسائلی که در اینجا توضیح داده شد، معرفی شدند، تب Issues دستخوش تغییرات زیادی شده است:

با حرکت رو به جلو، قصد داریم از برگه Issues برای آشکارسازی مشکلات بیشتر استفاده کنیم، که این امکان را فراهم می‌کند تا در درازمدت، جریان پیام خطای ناخوانا را تخلیه کنید.

کانال های پیش نمایش را دانلود کنید

استفاده از Chrome Canary ، Dev یا Beta را به عنوان مرورگر توسعه پیش‌فرض خود در نظر بگیرید. این کانال‌های پیش‌نمایش به شما امکان دسترسی به جدیدترین ویژگی‌های DevTools را می‌دهند، به شما اجازه می‌دهند APIهای پلتفرم وب پیشرفته را آزمایش کنید و به شما کمک می‌کنند تا قبل از کاربران، مشکلات سایت خود را پیدا کنید!

با تیم Chrome DevTools در تماس باشید

از گزینه‌های زیر برای بحث در مورد ویژگی‌های جدید، به‌روزرسانی‌ها یا هر چیز دیگری مربوط به DevTools استفاده کنید.