בידוד של אתר למפתחי אינטרנט

בגרסה 67 של Chrome למחשב יש תכונה חדשה שנקראת בידוד של אתר שמופעלת כברירת מחדל. במאמר הזה נסביר על הנושא של בידוד אתרים, למה הוא נחוץ ולמה מפתחי אתרים צריכים להיות מודעים אליו.

מהו בידוד של אתר?

האינטרנט מיועד לצפייה בסרטונים על חתולים, לניהול ארנקים של מטבעות וירטואליים ועוד – אבל לא היית רוצה של-fluffycats.example תהיה גישה למטבעות הקריפטוגרפיים היקרים שלך! למרבה המזל, בדרך כלל אתרים לא יכולים לגשת לנתונים אחד של השני בתוך הדפדפן בגלל המדיניות מאותו מקור. עם זאת, אתרים זדוניים עלולים לנסות לעקוף את המדיניות הזו כדי לתקוף אתרים אחרים, ומדי פעם מתגלים באגים באבטחה בקוד הדפדפן שאוכף את המדיניות מאותו מקור. הצוות של Chrome שואפים לתקן באגים כאלה בהקדם האפשרי.

בידוד של אתר הוא תכונת אבטחה ב-Chrome שמציעה קו הגנה נוסף, שמפחית את הסיכוי למתקפות כאלה. התהליך מבטיח שדפים מאתרים שונים תמיד יועברו לתהליכים שונים, שכל אחד מהם יפעל בארגז חול שמגביל את הפעולות שמותר לתהליך. בנוסף, התהליך לא יכול לקבל סוגים מסוימים של מידע אישי רגיש מאתרים אחרים. כתוצאה מכך, בשיטת 'בידוד של אתר' קשה הרבה יותר לאתר זדוני להשתמש בהתקפות ערוץ צדדי ספקולטיבי כמו Spectre כדי לגנוב נתונים מאתרים אחרים. אחרי שצוות Chrome מסיים פעולות אכיפה נוספות, בידוד של אתר יעזור גם במקרים שבהם דף של תוקף עלול להפר חלק מהכללים בתהליך שלו.

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

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

חסימת קריאה ממקורות שונים

גם כשכל הדפים באתר שונים מועברים לתהליכים נפרדים, דפים עדיין יכולים לבקש משאבי משנה מסוימים באתרים שונים, כמו תמונות ו-JavaScript. דף אינטרנט זדוני עלול להשתמש ברכיב <img> כדי לטעון קובץ JSON עם מידע אישי רגיש, כמו היתרה בחשבון הבנק שלך:

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

ללא בידוד של אתר, התוכן של קובץ ה-JSON נכנס לזיכרון של תהליך הרינדור, ובשלב הזה הוא רואה שזה לא פורמט תמונה חוקי ולא מעבד תמונה. אבל לאחר מכן התוקף יכול לנצל נקודות חולשה כמו Spectre כדי לקרוא את חלק הזיכרון הזה.

במקום להשתמש ב-<img>, התוקף יכול להשתמש גם ב-<script> כדי לשמור את המידע האישי הרגיש בזיכרון:

<script src="https://your-bank.example/balance.json"></script>

Cross-Origin Read block או CORB, הוא תכונת אבטחה חדשה שמונעת מהתוכן של balance.json להיכנס לזיכרון של זיכרון התהליך של הרינדור, על סמך סוג ה-MIME שלו.

נסביר עכשיו איך עובד CORB. אתר יכול לבקש מהשרת שני סוגי משאבים:

  1. משאבי נתונים כמו מסמכי HTML, קובצי XML או JSON
  2. משאבי מדיה כמו תמונות, JavaScript, CSS או גופנים

אתר יכול לקבל משאבי נתונים מהמקור שלו או ממקורות אחרים עם כותרות CORS מאפשרות כמו Access-Control-Allow-Origin: *. מצד שני, אפשר לכלול משאבי מדיה מכל מקור, גם בלי כותרות CORS מתירניות.

CORB מונע מתהליך הרינדור לקבל משאב נתונים ממקורות שונים (כלומר HTML, XML או JSON) במקרים הבאים:

  • למשאב יש כותרת X-Content-Type-Options: nosniff
  • CORS לא מאפשרת גישה למשאב באופן מפורש

אם למשאב הנתונים ממקורות שונים לא הוגדרה הכותרת X-Content-Type-Options: nosniff, CORB מנסה לסרוק את גוף התגובה כדי לקבוע אם הוא HTML, XML או JSON. זה נדרש כי שרתי אינטרנט מסוימים מוגדרים באופן שגוי ומציגים תמונות בתור text/html, למשל.

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

כדי ליהנות מאבטחה אופטימלית וגם כדי להפיק תועלת מ-CORB, מומלץ:

  • יש לסמן תשובות עם הכותרת הנכונה של Content-Type. (לדוגמה, משאבי HTML צריכים להיות מוצגים כ-text/html, משאבי JSON עם סוג JSON MIME ומשאבי XML עם סוג XML MIME).
  • כדי לבטל את ההסכמה להסרת סריקה, צריך להשתמש בכותרת X-Content-Type-Options: nosniff. בלי הכותרת הזו, Chrome מבצע ניתוח תוכן מהיר כדי לוודא שהסוג נכון. עם זאת, עדיף לא לתת תשובות כדי למנוע חסימה של דברים כמו קובצי JavaScript, ולכן עדיף לכם לעשות את הדברים הנכונים בעצמכם.

למידע נוסף, תוכלו לקרוא את המאמר של CORB למפתחי אתרים או הסבר מפורט על CORB.

למה מפתחי אינטרנט צריכים לעניין את התכונה 'בידוד של אתר'?

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

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

הפריסה של הדף המלא כבר לא סינכרונית

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

לדוגמה, נניח אתר בשם fluffykittens.example שמתקשר עם ווידג'ט חברתי שמתארח ב-social-widget.example:

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

בהתחלה, הרוחב של <iframe> בווידג'ט של הרשתות החברתיות הוא 123 פיקסלים. אבל לאחר מכן, הדף FluffyKittens משנה את הרוחב ל-456 פיקסלים (הפריסה המפעילה) ושולח הודעה לווידג'ט של הרשת החברתית, שמכיל את הקוד הבא:

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

בכל פעם שהווידג'ט החברתי מקבל הודעה דרך ה-API של postMessage, הוא מתעד את הרוחב של רכיב השורש <html> שלו.

איזה ערך רוחב נרשם? לפני ש-Chrome הפעיל את התכונה 'בידוד של אתר', התשובה הייתה 456. הגישה אל document.documentElement.clientWidth מאלצת את הפריסה, שהייתה סינכרונית לפני ש-Chrome הפעיל את התכונה 'בידוד אתרים'. עם זאת, כש'בידוד אתר' מופעל, הפריסה מחדש של הווידג'ט החברתי ממקורות שונים מתבצעת עכשיו באופן אסינכרוני בתהליך נפרד. לכן עכשיו התשובה יכולה להיות גם 123, כלומר הערך הישן של width.

אם דף משנה את הגודל של <iframe> ממקורות שונים ולאחר מכן שולח אליו postMessage, יכול להיות שהמסגרת שמקבלת את ההודעה עדיין לא יודעת מה הגודל החדש שלה בזמן קבלת ההודעה: במקרה של 'בידוד של אתר'. באופן כללי, דפים כאלה עלולים להיקטע אם הם מניחים ששינוי הפריסה מתפשט מיד לכל המסגרות בדף.

בדוגמה הספציפית הזו, פתרון יעיל יותר יגדיר את width במסגרת ההורה ויזהה את השינוי הזה ב-<iframe> על ידי האזנה לאירוע resize.

הזמן הקצוב לתפוגה של handlers שנטענו יכול להיות גבוה יותר בתדירות גבוהה יותר

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

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

במצב הזה, רכיבי ה-handler של unload בכל המסגרות הם מהימנים מאוד.

עם זאת, גם בלי בידוד של אתר, חלק מהניווטים במסגרת הראשית הם חוצי-תהליכים, מה שמשפיע על התנהגות ה-handler של הסרת הנתונים שנטענו. לדוגמה, אם עוברים מ-old.example אל new.example על ידי הקלדת כתובת ה-URL בסרגל הכתובות, הניווט אל new.example מתבצע בתהליך חדש. רכיבי ה-handler של הסרת הנתונים שנטענו מ-old.example ומסגרות המשנה שלו פועלים בתהליך old.example ברקע, אחרי שהדף new.example מוצג, ורכיבי ה-handler הישנים של הסרת הנתונים שנטענו מסתיימים אם הם לא מסתיימים תוך פרק זמן מסוים. יכול להיות שהרכיבי ה-handler של הסרת הנתונים שנטענו לא יסתיימו לפני הזמן הקצוב לתפוגה, ולכן התנהגות ההסרה של הנתונים שנטענו פחות מהימנה.

באמצעות 'בידוד של אתר', כל הניווטים בין אתרים הופכים לתהליכים חוצי-תהליכים, כך שמסמכים מאתרים שונים לא משתפים תהליך אחד עם השני. כתוצאה מכך, המצב שלמעלה חל במקרים נוספים, ולרכיבי ה-handler של הסרת הנתונים שנטענו ב-<iframe> יש בדרך כלל את ההתנהגויות של הרקע והזמן הקצוב לתפוגה כפי שמתואר למעלה.

הבדל נוסף שנובע מבידוד של אתר הוא הסדר המקביל החדש של רכיבי ה-handler של הסרת הנתונים שנטענו: ללא 'בידוד של אתר', רכיבי ה-handler של הסרת הנתונים שנטענו פועלים בסדר מחמיר, מלמעלה למטה, במסגרות שונות. אבל עם בידוד של אתר, רכיבי handler של הסרת נתונים שנטענו פועלים במקביל בתהליכים שונים.

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

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

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

גישה טובה יותר ויציבה יותר לאור השינוי הזה היא להשתמש במקום זאת ב-navigator.sendBeacon:

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

אם דרושה לך שליטה רבה יותר על הבקשה, אפשר להשתמש באפשרות keepalive של אחזור ה-API:

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

סיכום

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

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