הטמעת ניפוי באגים ב-CSP ובסוגים מהימנים בכלי הפיתוח ל-Chrome

Kateryna Prokopenko
Kateryna Prokopenko
Alfonso Castaño
Alfonso Castaño

הפוסט הזה עוסק בהטמעת תמיכה של DevTools לניפוי באגים בבעיות שקשורות למדיניות אבטחת תוכן (CSP) בעזרת הכרטיסייה בעיות שהושקה לאחרונה.

עבודת ההטמעה בוצעה במהלך 2 תקופות התמחות: 1. במהלך הפגישה הראשונה, פיתחנו את מסגרת הדיווח הכללית ותכננו את הודעות הבעיה לגבי 3 הפרות של CSP. 2. במהלך הפגישה השנייה הוספנו בעיות שקשורות ל-Trusted Types, לצד כמה תכונות מיוחדות של DevTools לניפוי באגים של Trusted Types.

מהו Content Security Policy?

מדיניות אבטחת תוכן (CSP) מאפשרת להגביל התנהגויות מסוימות באתר כדי לשפר את האבטחה. לדוגמה, אפשר להשתמש ב-CSP כדי לאסור סקריפטים בקוד או לאסור את השימוש ב-eval. שתי הפעולות האלה מפחיתות את שטח ההתקפה של תקיפות Cross-Site Scripting‏ (XSS). כאן אפשר לקרוא מבוא מפורט ל-CSP.

מדיניות CSP חדשה במיוחד היא המדיניות של סוגים מהימנים(TT), שמאפשרת ניתוח דינמי שיכול למנוע באופן שיטתי סוג גדול של התקפות הזרקה באתרים. כדי להשיג זאת, TT תומכת באתר בביצוע אכיפה של קוד ה-JavaScript שלו, כדי לאפשר רק הקצאה של סוגים מסוימים של פריטים למנקזי DOM, כמו innerHTML.

אתר יכול להפעיל מדיניות אבטחת תוכן על ידי הכללת כותרת HTTP מסוימת. לדוגמה, הכותרת content-security-policy: require-trusted-types-for 'script'; trusted-types default מפעילה את מדיניות TT בדף.

כל מדיניות יכולה לפעול באחת מהשיטות הבאות:

  • מצב אכיפה – שבו כל הפרת מדיניות נחשבת לשגיאה,
  • מצב דיווח בלבד – דיווח על הודעת השגיאה כאזהרה, אבל לא גורם לכשל בדף האינטרנט.

הטמעת בעיות שקשורות ל-Content Security Policy בכרטיסייה בעיות

המטרה של העבודה הזו הייתה לשפר את חוויית ניפוי הבאגים בבעיות שקשורות ל-CSP. כשהצוות של DevTools בוחן בעיות חדשות, הוא פועל בערך לפי התהליך הבא:

  1. הגדרת סיפורי משתמשים. מזהים קבוצה של סיפורי משתמשים בממשק הקצה של DevTools שמתארים איך מפתח אינטרנט יצטרך לחקור את הבעיה.
  2. הטמעה בחזית. על סמך סיפורי המשתמשים, מזהים אילו פריטים של מידע נדרשים כדי לחקור את הבעיה בחזית (למשל, בקשה קשורה, שם של קובץ cookie, שורה בסקריפט או בקובץ HTML וכו').
  3. זיהוי בעיות. מאתרים את המקומות בדפדפן שבהם אפשר לזהות את הבעיה ב-Chrome, ומכשירים את המקום כדי לדווח על בעיה, כולל המידע הרלוונטי משלב (2).
  4. שומרים את הבעיות ומציגים אותן. שומרים את הבעיות במקום מתאים ומאפשרים להן להיות זמינות ל-DevTools אחרי שהוא נפתח
  5. עיצוב הטקסט של הבעיות. כותבים טקסט הסבר שיעזור למפתח האתר להבין את הבעיה, וחשוב יותר, לתקן אותה.

שלב 1: הגדרת סיפורי משתמשים לבעיות ב-CSP

לפני שהתחלנו את תהליך ההטמעה, יצרנו מסמך עיצוב עם סיפורי משתמשים כדי להבין טוב יותר מה אנחנו צריכים לעשות. לדוגמה, כתבנו את סיפור המשתמש הבא:


כמפתח, שממש עכשיו הבנתי שחלק מהאתר שלי חסום, אני רוצה:- - ...לבדוק אם CSP הוא הסיבה לחסימת iframes או תמונות באתר שלי - ...לברר איזו הוראה של CSP גורמת לחסימה של משאב מסוים - ...לדעת איך לשנות את ה-CSP של האתר שלי כדי לאפשר הצגה של משאבים חסומים כרגע או ביצוע של js חסום כרגע.


כדי לבדוק את סיפור המשתמש הזה, יצרנו כמה דפי אינטרנט לדוגמה שהכילו את הפרות ה-CSP שבהן התעניינו, ובדקנו את הדפים האלה כדי להכיר את התהליך בעצמנו. ריכזנו כאן כמה דוגמאות לדפי אינטרנט (צריך לפתוח את הדמו עם הכרטיסייה בעיות פתוחה):

בעזרת התהליך הזה, למדנו שמיקום המקור הוא פריט המידע החשוב ביותר לניפוי באגים בבעיות של CSP. גילינו גם שאפשר למצוא במהירות את ה-iframe והבקשה המשויכים במקרה שמשאב חסום, וגם שקישור ישיר לרכיב ה-HTML בחלונית Elements ב-DevTools יכול להיות שימושי.

שלב 2: הטמעה בחזית

הפכנו את התובנה הזו לטיוטה הראשונה של המידע שרצינו להפוך לזמין ב-DevTools דרך פרוטוקול כלי הפיתוח של Chrome‏ (CDP):

בהמשך מופיע קטע מ-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 מסתמכת עליהן. לדוגמה, ההגדרה של 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++ מההגדרה שמטפלת ביצירה ובשליחת מבני הנתונים האלה מקצה העורפי של Chromium ב-C++ לקצה הקדמי של DevTools. באמצעות הספרייה הזו, אפשר ליצור אובייקט 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: זיהוי בעיות

כדי שהמידע יהיה זמין לפרוטוקול Chrome DevTools ‏ (CDP) בפורמט שמתואר בקטע הקודם, היינו צריכים למצוא את המקום שבו המידע היה זמין בפועל בקצה העורפי. למרבה המזל, בקוד ה-CSP כבר הייתה צווארה בקבוק ששימש למצב 'דיווח בלבד', שבו הצלחנו להתחבר: ContentSecurityPolicy::ReportViolation מדווח על בעיות לנקודת קצה (אופציונלית) לדיווח שאפשר להגדיר בכותרת ה-HTTP של CSP. רוב המידע שרצינו לדווח עליו כבר היה זמין, כך שלא נדרשו שינויים גדולים בקצה העורפי כדי שהמכשירים שלנו יפעלו.

שלב 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.

ניפוי באגים בבעיות שקשורות לסוגים מהימנים

עבודה עם TT בהיקף גדול יכולה להיות מאתגרת בלי הכלים המתאימים למפתחים.

הדפסה משופרת של מסוף

כשאנחנו עובדים עם אובייקטים מהימנים, אנחנו רוצים להציג לפחות את אותה כמות מידע כמו לגבי המקבילה הלא מהימנה. לצערנו, בשלב הזה כשמציגים אובייקט מהימן לא מוצג מידע על האובייקט המארז.

הסיבה לכך היא שהערך שמוצג במסוף נלקח מהקריאה של .valueOf() על האובייקט כברירת מחדל. עם זאת, במקרה של Trusted Type, הערך המוחזר לא שימושי במיוחד. במקום זאת, אנחנו רוצים להציג משהו דומה למה שמופיע כשקוראים לפונקציה .toString(). כדי לעשות זאת, אנחנו צריכים לשנות את V8 ואת Blink כדי להוסיף טיפול מיוחד באובייקטים מסוג מהימן.

מטעמי היסטוריה, הטיפול בהתאמה אישית הזה בוצע ב-V8, אבל לגישה הזו יש חסרונות חשובים. יש הרבה אובייקטים שדורשים תצוגה מותאמת אישית, אבל הסוג שלהם זהה ברמת ה-JS. מכיוון ש-V8 הוא JS טהור, הוא לא יכול להבדיל בין מושגים שתואמים ל-Web API, כמו סוג מהימן. לכן, V8 צריך לבקש עזרה מהכלי להטמעה (Blink) כדי להבדיל ביניהם.

לכן, העברת החלק הזה של הקוד ל-Blink או לכל כלי להטמעת קוד נשמעת כבחירה הגיונית. מלבד הבעיה שנחשפה, יש עוד יתרונות רבים:

  • לכל מי שמטמיע את הקוד יכול להיות תיאור משלו
  • קל יותר ליצור את התיאור באמצעות Blink API
  • ל-Blink יש גישה להגדרה המקורית של האובייקט. לכן, אם נשתמש ב-.toString() כדי ליצור את התיאור, לא קיים סיכון ש-.toString() יוגדר מחדש.

הפסקה במקרה של הפרה (במצב 'דוחות בלבד')

נכון לעכשיו, הדרך היחידה לנפות באגים של הפרות של TT היא להגדיר נקודות עצירה בחריגות של JS. מאחר שהפרות של TT שחלות עליהם אכיפה יגרמו להפעלת חריגה, התכונה הזו יכולה להיות שימושית. עם זאת, בתרחישים בעולם האמיתי, צריך יותר שליטה מפורטת על הפרות של TT. באופן ספציפי, אנחנו רוצים להפסיק את הפעילות רק במקרים של הפרות של TT (לא במקרים של חריגים אחרים), להפסיק את הפעילות גם במצב דיווח בלבד ולהבדיל בין הסוגים השונים של הפרות של TT.

כבר יש תמיכה במגוון רחב של נקודות עצירה ב-DevTools, כך שהארכיטקטורה ניתנת להרחבה. כדי להוסיף סוג חדש של נקודת עצירה, צריך לבצע שינויים בקצה העורפי (Blink), ב-CDP ובחזית. כדאי להציג פקודה חדשה של CDP, נקרא לה setBreakOnTTViolation. הפקודה הזו תשמש את הקצה הקדמי כדי להודיע לקצה העורפי אילו סוגים של הפרות של TT הוא צריך לשבור. הקצה העורפי, ובמיוחד InspectorDOMDebuggerAgent, יספק 'בדיקה', onTTViolation(), שתופעל בכל פעם שתתרחש הפרה של TT. לאחר מכן, InspectorDOMDebuggerAgent יבדוק אם צריך להפעיל נקודת עצירה בגלל ההפרה הזו, ואם כן, הוא ישלח הודעה לקצה הקדמי כדי להשהות את הביצוע.

מה כבר בוצע ומה השלב הבא?

מאז שהבעיות המתוארות כאן הופיעו, בכרטיסייה בעיות בוצעו כמה שינויים:

בהמשך, אנחנו מתכננים להשתמש בכרטיסייה בעיות כדי להציג בעיות נוספות. כך נוכל להפחית את העומס על מסוף Google בטווח הארוך, ולהפסיק להציג בו הודעות שגיאה שלא ניתן לקרוא.

מורידים את הערוצים של התצוגה המקדימה.

מומלץ להשתמש ב-Chrome Canary, ב-Dev או ב-Beta כדפדפן הפיתוח שמוגדר כברירת מחדל. ערוצי התצוגה המקדימה האלה מעניקים לכם גישה לתכונות העדכניות ביותר של DevTools, מאפשרים לכם לבדוק ממשקי API מתקדמים לפלטפורמות אינטרנט ולמצוא בעיות באתר לפני שהמשתמשים שלכם יעשו זאת.

יצירת קשר עם צוות כלי הפיתוח ל-Chrome

אתם יכולים להשתמש באפשרויות הבאות כדי לדון בתכונות החדשות, בעדכונים או בכל דבר אחר שקשור ל-DevTools.