ב-Chrome 67 למחשב יש תכונה חדשה שנקראת בידוד של אתר שמופעלת כברירת מחדל. במאמר הזה נסביר מהו בידוד האתר, למה הוא נחוץ ומדוע מפתחי אתרים צריכים להיות מודעים לו.
מהו 'בידוד של אתר'?
האינטרנט מיועד לצפייה בסרטונים על חתולים ולניהול ארנקים של מטבעות וירטואליים, בין היתר –
אבל לא היית רוצה של-fluffycats.example
תהיה גישה לקריפטוקוינים היקרים שלך! למרבה המזל, בדרך כלל לאתרים אין גישה לנתונים של אתרים אחרים בדפדפן, בזכות מדיניות המקור הזהה. עם זאת, אתרים זדוניים עשויים לנסות לעקוף את המדיניות הזו כדי לתקוף אתרים אחרים, ולפעמים מתגלים באגים באבטחה בקוד הדפדפן שאוכף את מדיניות המקור הזהה. צוות Chrome שואף לתקן באגים כאלה בהקדם האפשרי.
בידוד אתרים היא תכונת אבטחה ב-Chrome שמספקת קו הגנה נוסף כדי להקטין את הסיכוי להצלחה של התקפות כאלה. הוא מבטיח שדפים מאתרים שונים תמיד מועברים לתהליכים שונים, שכל אחד מהם פועל בארגז חול שמגביל את הפעולות שהתהליך יכול לבצע. הוא גם מונע מהתהליך לקבל סוגים מסוימים של מידע רגיש מאתרים אחרים. כתוצאה מכך, כשמשתמשים ב-Site Isolation, קשה הרבה יותר לאתר זדוני להשתמש בהתקפות ספקולטיביות בערוץ צדדי כמו Spectre כדי לגנוב נתונים מאתרים אחרים. כשצוות Chrome ישלים אכיפה נוספת, התכונה 'בידוד אתרים' תעזור גם במקרים שבהם דף של תוקף יכול להפר חלק מהכללים בתהליך שלו.
התכונה 'בידוד אתרים' מקשה על אתרים לא מהימנים לגשת למידע מהחשבונות שלכם באתרים אחרים או לגנוב אותו. הוא מספק הגנה נוספת מפני סוגים שונים של באגים באבטחה, כמו התקיפות בערוץ צדדי של Meltdown ו-Spectre שבוצעו לאחרונה.
למידע נוסף על 'בידוד של אתר', אפשר לעיין במאמר שלנו בבלוג בנושא אבטחה של Google.
Cross-Origin Read Blocking (CORB)
גם כשכל הדפים באתרים שונים מועברים לתהליכים נפרדים, הדפים עדיין יכולים לבקש באופן לגיטימי משאבים משנה מסוימים באתרים שונים, כמו תמונות ו-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 Blocking (CORB) היא תכונת אבטחה חדשה שמונעת את כניסת התוכן של balance.json
לזיכרון של תהליך ה-Renderer על סמך סוג ה-MIME שלו.
ננסה להסביר איך CORB עובד. אתר יכול לבקש משרת שני סוגים של משאבים:
- משאבי נתונים, כמו מסמכי HTML, XML או JSON
- משאבי מדיה כמו תמונות, 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 עם סוג MIME של JSON ומשאבי XML עם סוג XML MIME). - כדי לבטל את ההסכמה לניטור, משתמשים בכותרת
X-Content-Type-Options: nosniff
. בלי הכותרת הזו, Chrome מבצע ניתוח תוכן מהיר כדי לנסות לוודא שהסוג נכון, אבל מכיוון שהסוג הוא נכון, מה שאתם צריכים לעשות הוא לאפשר לתגובות להימנע מחסימת קובצי JavaScript, עדיף לבצע בעצמכם את הפעולה הנכונה.
לפרטים נוספים, אפשר לעיין במאמר על CORB למפתחי אתרים או בהסבר המפורט על CORB.
למה מפתחי אתרים צריכים להשתמש בבידוד אתרים?
ברוב המקרים, Site Isolation היא תכונה של הדפדפן שמתרחשת מאחורי הקלעים, ולא מוצגת ישירות למפתחי האתרים. לדוגמה, אין API חדש שחשוף לאינטרנט שצריך ללמוד. באופן כללי, לא אמורים להיות הבדלים בין דפי אינטרנט לבין הפעלה עם 'בידוד של אתר' או בלעדיה.
עם זאת, לכלל הזה יש כמה חריגים. להפעלת Site Isolation יש כמה תופעות לוואי עדינות שעשויות להשפיע על האתר שלכם. אנחנו שומרים על רשימת בעיות מוכרות בנושא בידוד אתרים, ומפרטים בהמשך על הבעיות החשובות ביותר.
הפריסה של הדף המלא כבר לא סינכרונית
כשמשתמשים בבידוד אתר, אי אפשר להבטיח שהפריסה של הדף המלא תהיה סינכרונית, כי יכול להיות שהפריימים של הדף יהיו מפוזרים עכשיו בין כמה תהליכים. הדבר עשוי להשפיע על דפים אם הם מניחים ששינוי בפריסה מופץ באופן מיידי לכל המסגרות בדף.
לדוגמה, נבחן אתר בשם 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 שנטענו יהיה גבוה יותר
כשמסגרת מנווטת או נסגרת, המסמך הישן וגם כל מסמכי המסגרת המשנית שמוטמעים בו מפעילים את הטיפול שלהם ב-unload
. אם הניווט החדש מתרחש באותו תהליך רינדור (למשל, בניווט ממקור זהה), רכיבי ה-handler של unload
במסמך הישן ובפריימים המשניים שלו יכולים לפעול למשך זמן רב באופן שרירותי לפני שמאפשרים לשמור את הניווט החדש.
addEventListener('unload', () => {
doSomethingThatMightTakeALongTime();
});
במקרה כזה, הטיפולים של unload
בכל המסגרות אמינים מאוד.
עם זאת, גם בלי Site Isolation, חלק מהניווטים במסגרת הראשית מתבצעים בין תהליכים, וזה משפיע על ההתנהגות של ה-handler של הסרת נתונים שנטענו. לדוגמה, אם מנווטים מ-old.example
אל new.example
על ידי הקלדה של כתובת ה-URL בסרגל הכתובות, תהליך הניווט אל new.example
מתבצע בתהליך חדש. מנהלי ההוצאה משימוש של old.example
ושל המסגרות המשניות שלו פועלים בתהליך old.example
ברקע, אחרי שהדף new.example
מוצג, ומנהלי ההוצאה משימוש הישנים מסתיימים אם הם לא מסתיימים תוך פרק זמן קצוב. יכול להיות שמטפלי ההוצאה משימוש לא יסתיימו לפני חלוף הזמן הקצוב לתפוגה, ולכן התנהגות ההוצאה משימוש פחות מהימנה.
בעזרת בידוד אתרים, כל הניווטים בין אתרים הופכים לתהליכים שונים, כך שמסמכים מאתרים שונים לא חולקים תהליך אחד עם השני. כתוצאה מכך, המצב שמתואר למעלה רלוונטי למקרים נוספים, ולעיתים קרובות למטפלי פריקה ב-<iframe>
יש את התנהגויות הרקע והזמן הקצוב לתפוגה שמתוארות למעלה.
הבדל נוסף שמקורו בבידוד האתרים הוא הסדר המקביל החדש של Handlers של הסרת נתונים שנטענו: ללא בידוד אתרים, Handlers של הסרת נתונים שנטענו פועלים בסדר קפדני מלמעלה למטה במסגרות. אבל בעזרת בידוד אתר, רכיבי ה-handler של הסרת הנתונים שנטענו פועלים במקביל בתהליכים שונים.
אלה השלכות בסיסיות של הפעלת בידוד של אתר. צוות Chrome פועל לשיפור האמינות של מנהלי פריקה בתרחישים נפוצים, ככל האפשר. אנחנו גם מודעים לבאגים שבהם הגורמים המטפלים בהסרת הנתונים שנטענו בתת-מסגרות עדיין לא יכולים להשתמש בתכונות מסוימות ופועלים לפתור אותם.
מקרה חשוב לטיפול באירועי unload הוא שליחת פינגים בסיום הסשן. בדרך כלל כך עושים את זה:
addEventListener('pagehide', () => {
const image = new Image();
img.src = '/end-of-session';
});
גישה טובה יותר וחזקה יותר לאור השינוי הזה היא להשתמש ב-navigator.sendBeacon
במקום זאת:
addEventListener('pagehide', () => {
navigator.sendBeacon('/end-of-session');
});
אם דרושה לך יותר שליטה בבקשה, אפשר להשתמש באפשרות keepalive
של Fetch API:
addEventListener('pagehide', () => {
fetch('/end-of-session', {keepalive: true});
});
סיכום
בידוד האתרים מקשיח את הגישה של אתרים לא מהימנים למידע מהחשבונות שלכם באתרים אחרים, או את היכולת שלהם לגנוב את המידע הזה, על ידי בידוד כל אתר בתהליך משלו. כחלק מכך, CORB מנסה למנוע ממקורות של מידע רגיש להיכנס לתהליך היצירה של הגרפיקה. ההמלצות שלמעלה יעזרו לכם להפיק את המקסימום מתכונות האבטחה החדשות האלה.
תודה לאלכס מושצ'וק, צ'רלי רייס, ג'ייסון מילר, נאסקו אוסקוב, פיליפ וולטון, שובהי פנייקר ותומס סטיינר שקראו גרסת טיוטה של המאמר הזה וסיפקו משוב.