Content Security Policy

Joe Medley
Joe Medley

מודל האבטחה של האינטרנט מבוסס על שורש של מדיניות המקור הזהה. לקוד מ-https://mybank.com צריכה להיות גישה רק לנתונים של https://mybank.com, ואסור ש-https://evil.example.com אף פעם ייתן גישה. כל מקור נשמר בנפרד משאר האינטרנט, וכך מפתחים ארגז חול בטוח שבו הם יכולים לבנות ולשחק. בתיאוריה, זה מעולה. בפועל, תוקפים מצאו דרכים חכמות לעקוף את המערכת.

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

הסקירה הכללית הזו מדגישה אמצעי הגנה שיכולה לצמצם באופן משמעותי את הסיכון ואת ההשפעה של מתקפות XSS בדפדפנים מודרניים: Content Security Policy (CSP).

אמ;לק

  • אפשר להשתמש ברשימות היתרים כדי לומר ללקוח מה מותר ומה אסור.
  • כאן תוכלו לראות אילו הוראות זמינות.
  • ללמוד אילו מילות מפתח הם משתמשים.
  • קוד מוטבע ו-eval() נחשבים מזיקים.
  • לפני שאוכפים את רמות המדיניות, דווחו לשרת על הפרות מדיניות.

רשימות היתרים של מקורות

הבעיה שבה מנצלות התקפות XSS היא היכולת של הדפדפן להבחין בין סקריפט של חלק מהאפליקציה לבין סקריפט שהוחדר בזדון על ידי צד שלישי. לדוגמה, לחצן Google +1 בתחתית הדף הזה נטען ומפעיל קוד מ-https://apis.google.com/js/plusone.js בהקשר של מקור הדף הזה. אנחנו סומכים על הקוד הזה, אבל לא נוכל לצפות שהדפדפן יבין בעצמו שהקוד של apis.google.com הוא מדהים, ואילו קוד מ-apis.evil.example.com כנראה לא כזה. בשמחה, הדפדפן מוריד ומפעיל כל קוד שנדרש לדף, ללא קשר למקור.

במקום לסמוך באופן עיוור על כל שהשרת מספק, ה-CSP מגדירה את כותרת ה-HTTP Content-Security-Policy, וכך אפשר ליצור רשימת היתרים של מקורות תוכן מהימן ומורה לדפדפן להפעיל או לעבד משאבים רק מהמקורות האלה. גם אם תוקף יכול למצוא פתח כדי להחדיר סקריפט, הסקריפט לא יתאים לרשימת ההיתרים ולכן הוא לא יופעל.

מכיוון שאנחנו סומכים על apis.google.com שיספק קוד תקף, ואנחנו סומכים על עצמנו שנעשה זאת, נגדיר מדיניות שמאפשרת הפעלה של סקריפט רק כשהוא מגיע מאחד משני המקורות האלה:

Content-Security-Policy: script-src 'self' https://apis.google.com

פשוט, נכון? כמו שבטח ניחשתם, script-src היא הוראה ששולטת בקבוצת הרשאות הקשורות לסקריפט בדף ספציפי. הגדרנו את 'self' כמקור תקין לסקריפט ואת https://apis.google.com כמקור אחר. הדפדפן מוריד ומפעיל JavaScript באופן תקין מ-apis.google.com ב-HTTPS, וגם ממקור הדף הנוכחי.

שגיאה במסוף: הסקריפט 'http://evil.example.com/evil.js' לא נטען מפני שהוא מפר את ההוראה הבאה של מדיניות אבטחת התוכן: script-src 'self' https://apis.google.com

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

המדיניות חלה על מגוון רחב של משאבים

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

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

  • ההגדרה base-uri מגבילה את כתובות ה-URL שיכולות להופיע ברכיב <base> של הדף.
  • ב-child-src תוכלו לראות את כתובות ה-URL של העובדים ואת התוכן של המסגרות המוטמעות. לדוגמה: child-src https://youtube.com תאפשר להטמיע סרטונים מ-YouTube, אך לא ממקורות אחרים.
  • connect-src מגביל את המקורות שאפשר להתחבר אליהם (דרך XHR , WebSockets ו-EventSource).
  • החלק font-src מציין את המקורות שיכולים להציג גופנים באינטרנט. ניתן להפעיל את גופנים באינטרנט של Google דרך font-src https://themes.googleusercontent.com.
  • ב-form-action מפורטות נקודות קצה (endpoints) תקינות שאפשר לשלוח מתגי <form>.
  • frame-ancestors מציין את המקורות שיכולים להטמיע את הדף הנוכחי. ההוראה הזו חלה על התגים <frame>, <iframe>, <embed> ו-<applet>. אי אפשר להשתמש בהוראה הזו בתגי <meta>, והיא חלה רק על משאבים שאינם HTML.
  • frame-src הוצא משימוש ברמה 2, אבל שוחזר ברמה 3. אם הוא לא נמצא, הוא עדיין יחזור אל child-src כמו קודם.
  • ההגדרה img-src מגדירה את המקורות שמהם אפשר לטעון את התמונות.
  • ההגדרה media-src מגבילה את המקורות שמורשים להעביר וידאו ואודיו.
  • object-src מאפשר שליטה על Flash ויישומי פלאגין אחרים.
  • plugin-types מגביל את סוגי יישומי הפלאגין שדף יכול להפעיל.
  • report-uri מציין את כתובת ה-URL שאליה הדפדפן ישלח דוחות כאשר יש הפרה של מדיניות אבטחת התוכן. אי אפשר להשתמש בהוראה הזו בתגי <meta>.
  • style-src הוא המקביל של script-src לגיליונות סגנונות.
  • הקוד upgrade-insecure-requests מורה לסוכני המשתמשים לשכתב סכימות של כתובות URL, תוך שינוי מ-HTTP ל-HTTPS. ההוראה הזו מיועדת לאתרים עם מספר גדול של כתובות URL ישנות שצריך לשכתב.
  • worker-src היא הוראה של CSP ברמה 3 שמגבילה את כתובות ה-URL שעשויות להיטען כ-worker, כ-Shared worker או כ-service worker. החל מיולי 2017, ההטמעה מוגבלת.

כברירת מחדל, ההוראות פתוחות. אם לא מגדירים מדיניות ספציפית להוראה, למשל font-src, ההוראה תפעל כברירת מחדל כי ציינתם את * כמקור התקף (לדוגמה, אפשר לטעון גופנים מכל מקום, ללא הגבלה).

כדי לשנות את התנהגות ברירת המחדל הזו אפשר לציין הוראת default-src. ההוראה הזו מגדירה את ברירות המחדל לרוב ההנחיות שלא צוינו. באופן כללי, הכלל הזה חל על כל הוראה שמסתיימת ב--src. אם default-src מוגדר לערך https://example.com ולא מציינים הוראת font-src, אפשר לטעון גופנים מ-https://example.com ולא מאף מקום אחר. בדוגמאות הקודמות שלנו ציינתם רק את הערך script-src, כלומר אפשר לטעון תמונות, גופנים וכן הלאה מכל מקור.

בהנחיות הבאות לא נעשה שימוש ב-default-src כחלופה. זכרו שאם לא מגדירים אותן, זה כמו לאפשר כל דבר.

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

אפשר להשתמש בהוראות האלה בכמה שיותר או מעט מהן, בהתאם לאפליקציה הספציפית שלכם. כדי לעשות זאת, צריך לרשום כל אחת מהן בכותרת ה-HTTP ולהפריד ביניהן באמצעות נקודה-פסיק. חשוב לוודא שאתם מציגים את כל המשאבים הנדרשים מסוג מסוים בהוראה בודדת. אם כותבים משהו כמו script-src https://host1.com; script-src https://host2.com, המערכת פשוט תתעלם מההנחיה השנייה. משהו כמו בדוגמה הבאה מציין בצורה נכונה את שני המקורות כחוקיים:

script-src https://host1.com https://host2.com

לדוגמה, אם יש לכם אפליקציה שטוענת את כל המשאבים שלה מרשת להעברת תוכן (למשל, https://cdn.example.net), ואתם יודעים שאתם לא צריכים תוכן ממוסגר או יישומי פלאגין, המדיניות שלכם עשויה להיראות בערך כך:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

פרטי ההטמעה

הכותרות X-WebKit-CSP ו-X-Content-Security-Policy יופיעו במגוון מדריכים באינטרנט. מעכשיו והלאה, צריך להתעלם מהכותרות המקדימות האלה. דפדפנים מודרניים (למעט IE) תומכים בכותרת Content-Security-Policy ללא קידומת. זו הכותרת שבה צריך להשתמש.

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

רשימת המקורות בכל הוראה גמישה. אפשר לציין מקורות לפי סכמה (data:, https:) או טווח של ערכי ספציפיות משם מארח בלבד (example.com, שתואם לכל מקור במארח הזה: כל סכימה, כל יציאה) ל-URI שמוגדר במלואו (https://example.com:443, שתואם רק ל-HTTPS, רק example.com, ורק ליציאה 443). אפשר להשתמש בתווים כלליים לחיפוש, אבל רק כסכימה, כיציאה או במיקום השמאלי ביותר של שם המארח: *://*.example.com:* יתאימו לכל תתי-הדומיין של example.com (אבל לא example.com עצמו), בכל סכימה בכל יציאה.

רשימת המקורות מקבלת גם ארבע מילות מפתח:

  • כמו שאפשר לצפות, 'none' לא תואם לכלום.
  • 'self' תואם למקור הנוכחי, אבל לא לתתי-הדומיין שלו.
  • 'unsafe-inline' מאפשר JavaScript ו-CSS מוטבעים. (נדבר על זה בפירוט בהמשך).
  • 'unsafe-eval' מאפשר מנגנונים של המרת טקסט ל-JavaScript, כמו eval. (נגיע גם לנושא הזה).

למילות המפתח האלה צריך להוסיף מירכאות יחידות. לדוגמה, script-src 'self' (עם מירכאות) מאשר הפעלת JavaScript מהמארח הנוכחי; script-src self (ללא מירכאות) מאפשר JavaScript משרת בשם "self" (ולא מהמארח הנוכחי), וזו כנראה לא הייתה הכוונה שלכם.

הרצה בארגז חול

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

מטא תג

מנגנון המסירה המועדף ל-CSP הוא כותרת HTTP. עם זאת, כדאי להגדיר מדיניות בדף ישירות בתגי העיצוב. עושים זאת באמצעות תג <meta> עם המאפיין http-equiv:

<meta
  http-equiv="Content-Security-Policy"
  content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>

לא ניתן להשתמש באפשרות הזו עבור frame-ancestors, report-uri או sandbox.

קוד מוטבע נחשב מזיק

צריך להיות ברור ש-CSP מבוסס על המקורות שברשימת ההיתרים, כי זו דרך חד-משמעית להורות לדפדפן להתייחס לקבוצות משאבים ספציפיות כקבילות ולדחות את השאר. עם זאת, רשימות היתרים שמבוססות על מקור לא פותרות את האיום הגדול ביותר של התקפות XSS: החדרת סקריפט מוטבע. אם תוקף יכול להחדיר תג סקריפט שמכיל ישירות מטען ייעודי (payload) זדוני (<script>sendMyDataToEvilDotCom();</script>), לדפדפן אין מנגנון שלפיו אפשר להבדיל בינו לבין תג סקריפט לגיטימי המוטבע. CSP פותר את הבעיה הזו על ידי חסימה מלאה של סקריפט מוטבע: זו הדרך היחידה לוודא.

החסימה הזו כוללת לא רק סקריפטים שמוטמעים ישירות בתגי script, אלא גם גורמים מטפלים באירועים מוטבעים וכתובות URL מסוג javascript:. צריך להעביר את התוכן של תגי script לקובץ חיצוני ולהחליף את כתובות ה-URL של javascript: ואת <a ... onclick="[JAVASCRIPT]"> בקריאות המתאימות של addEventListener(). לדוגמה, אפשר לשכתב את הקוד הבא:

<script>
  function doAmazingThings() {
    alert('YOU AM AMAZING!');
  }
</script>
<button onclick="doAmazingThings();">Am I amazing?</button>

משהו כמו:

<!-- amazing.html -->
<script src="amazing.js"></script>
<button id="amazing">Am I amazing?</button>

<div style="clear:both;"></div>
// amazing.js
function doAmazingThings() {
  alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('amazing').addEventListener('click', doAmazingThings);
});

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

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

אם נדרשים סקריפט וסגנון מוטבעים, אפשר להפעיל אותם על ידי הוספת 'unsafe-inline' כמקור מותר בהנחיה script-src או style-src. אפשר גם להשתמש בגיבובים (hash) או בצפנים חד-פעמיים (ראו בהמשך), אבל מומלץ שלא לעשות זאת. חסימת סקריפט מוטבע היא השיפור הגדול ביותר ברמת האבטחה שניתנת ל-CSP, וחסימת סגנון מוטבע גם מקשה את האפליקציה. צריך להשקיע מאמץ מסוים מראש כדי להבטיח שהכול יפעל כמו שצריך אחרי שמעבירים את כל הקוד בצורה תקינה, אבל זה שילוב שכדאי לעשות.

אם אתם חייבים להשתמש בו לגמרי,

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

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

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
  // Some inline code I can't remove yet, but need to asap.
</script>

עכשיו מוסיפים את הצופן החד-פעמי (nonce) להוראה script-src שמצורף למילת המפתח nonce-.

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

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

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

<script>
  alert('Hello, world.');
</script>

המדיניות תכלול את הפרטים הבאים:

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

חשוב לציין כאן כמה דברים. הקידומת sha*- מציינת את האלגוריתם שיוצר את הגיבוב. בדוגמה שלמעלה, נעשה שימוש ב-sha256-. CSP תומך גם ב-sha384- וב-sha512-. כשיוצרים את הגיבוב, אין לכלול את התגים <script>. גם אותיות רישיות ורווחים לבנים, כולל רווחים לבנים בתחילת הטקסט או בסופו.

חיפוש Google בנושא יצירת גיבובי SHA יוביל לפתרונות בכל מספר שפות. ב-Chrome מגרסה 40 ואילך, אפשר לפתוח את כלי הפיתוח ולטעון מחדש את הדף. הכרטיסייה Console (מסוף) תכלול הודעות שגיאה עם הגיבוב sha256 הנכון לכל אחד מהסקריפטים המוטבעים.

גם ההערכה

גם כשתוקף לא יכול להחדיר סקריפט ישירות, יכול להיות שהוא יוכל להטעות את האפליקציה ולגרום לכך להמיר טקסט פנימי אחר ל-JavaScript להפעלה ולבצע אותו בשמו. eval(), new Function() , setTimeout([string], ...) ו-setInterval([string], ...) הם כל הווקטורים שדרכם טקסט שהוחדר עלול לגרום לתוכנה זדונית בלתי צפויה. תגובת ברירת המחדל של CSP לסיכון הזה היא חסימה מלאה של כל הווקטורים האלה.

יש לכך לא מעט השפעות על אופן הפיתוח של האפליקציות:

  • צריך לנתח JSON דרך JSON.parse המובנה במקום להסתמך על eval. פעולות JSON נייטיב זמינות בכל דפדפן מאז IE8, והן בטוחות לחלוטין.
  • כותבים מחדש את כל הקריאות ל-setTimeout או ל-setInterval שמבצעים כרגע באמצעות פונקציות מוטבעות במקום מחרוזות. למשל:
setTimeout("document.querySelector('a').style.display = 'none';", 10);

עדיף לכתוב כך:

setTimeout(function () {
  document.querySelector('a').style.display = 'none';
}, 10);
  • מומלץ להימנע מיצירה של תבניות בזמן ריצה: ספריות רבות ליצירת תבניות משתמשות ב-new Function() באופן חופשי כדי לזרז את יצירת התבניות בזמן הריצה. זו אפליקציה קטנה של תכנות דינמי, אבל היא מהווה סיכון של הערכת טקסט זדוני. חלק מה-frameworks תומכות ב-CSP מחוץ לאריזה, ובהיעדר eval הם מנתחים אפקטיביים. דוגמה טובה לכך היא הוראת ng-csp של AngularJS.

עם זאת, עדיף להשתמש בשפה ליצירת תבניות שכוללת הידור מראש (Handlebars כן). הרכבה מראש של התבניות יכולה להפוך את חוויית המשתמש למהירה עוד יותר מההטמעה המהירה ביותר של סביבת זמן הריצה, והיא גם בטוחה יותר. אם ה-eval והטקסט ל-JavaScript שלה חיוניים לאפליקציה, אפשר להפעיל אותם על ידי הוספה של 'unsafe-eval' כמקור מותר בהנחיה script-src, אבל מאוד לא מומלץ לעשות זאת. חסימת היכולת להריץ מחרוזות מקשה על תוקפים לבצע קוד לא מורשה באתר שלכם.

דיווח

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

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

הדוחות האלה ייראו בערך כך:

{
  "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
  }
}

המידע כולל קטע מידע טוב שיעזור לכם לאתר את הגורם הספציפי להפרה, כולל הדף שבו התרחשה ההפרה (document-uri), הגורם המפנה של הדף (שימו לב שבניגוד לשדה הכותרת של HTTP, המפתח לא שגוי), המשאב שהפר את המדיניות של הדף (blocked-uri), ההוראה הספציפית שהוא הפר (violated-directive) והמדיניות המלאה של הדף (original-policy).

דוח בלבד

אם אתם רק מתחילים להשתמש ב-CSP, כדאי להעריך את המצב הנוכחי של האפליקציה לפני ההשקה של מדיניות דרקונית למשתמשים. כצעד בדרך לפריסה מלאה, תוכלו לבקש מהדפדפן לעקוב אחרי מדיניות ולדווח על הפרות אבל בלי לאכוף את ההגבלות. במקום לשלוח כותרת Content-Security-Policy, צריך לשלוח כותרת Content-Security-Policy-Report-Only.

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

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

שימוש בעולם האמיתי

אפשר להשתמש ב-CSP 1 ב-Chrome, ב-Safari וב-Firefox, אבל התמיכה ב-IE 10 מוגבלת מאוד. אפשר לצפות בפרטים ספציפיים בכתובת caniuse.com. רמת CSP 2 זמינה ב-Chrome החל מגרסה 40. אתרים ענקיים כמו Twitter ו-Facebook פרסו את הכותרת (המקרה לדוגמה של טוויטר שווה קריאה), והתקן כבר מוכן ואפשר להתחיל לפרוס באתרים שלכם.

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

תרחיש לדוגמה מס' 1: ווידג'טים של רשתות חברתיות

  • לחצן 1+ של Google כולל סקריפט מ-https://apis.google.com ומטמיע <iframe> מ-https://plusone.google.com. כדי להטמיע את הלחצן, צריכה להיות מדיניות שכוללת את שני המקורות האלה. מדיניות מינימלית היא script-src https://apis.google.com; child-src https://plusone.google.com. כמו כן, צריך לוודא שקטע ה-JavaScript ש-Google מספקת נשלף לקובץ JavaScript חיצוני. אם הייתה לכם מדיניות שמבוססת על רמה 1 באמצעות frame-src ברמה 2, הייתם צריכים לשנות אותה ל-child-src. האפשרות הזו לא נחוצה יותר ברמה 3 של CSP.

  • לחצן הלייק של Facebook כולל מספר אפשרויות הטמעה. מומלץ להמשיך להשתמש בגרסה של <iframe>, כי היא נמצאת בארגז חול (sandboxing) בבטחה בשאר האתר. כדי לפעול באופן תקין, נדרשת הוראת child-src https://facebook.com. שימו לב שכברירת מחדל, הקוד <iframe> ש-Facebook מספק טוען כתובת URL יחסית, //facebook.com. שנו את ההגדרה כדי לציין HTTPS באופן מפורש: https://facebook.com. אין סיבה להשתמש ב-HTTP אם אין חובה לעשות זאת.

  • הלחצן הציוץ ב-Twitter תלוי בגישה לסקריפט ולמסגרת, ששניהם מתארחים ב-https://platform.twitter.com. (ב-Twitter גם מספקת כתובת URL יחסית כברירת מחדל. צריך לערוך את הקוד כך שיציין HTTPS כשמעתיקים/מדביקים אותו באופן מקומי). הכול יהיה מוכן במסגרת script-src https://platform.twitter.com; child-src https://platform.twitter.com, כל עוד מעבירים את קטע הקוד של JavaScript ש-Twitter מספק לקובץ JavaScript חיצוני.

  • לפלטפורמות אחרות יש דרישות דומות, ואפשר להתייחס אליהן באופן דומה. מומלץ פשוט להגדיר default-src של 'none' ולעקוב אחרי המסוף כדי לקבוע אילו משאבים צריך להפעיל כדי שהווידג'טים יפעלו.

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

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

תרחיש לדוגמה מס' 2: ללא 'ביטול נעילה עם טביעת אצבע'

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

נניח שהבנק טוען את כל התמונות, הסגנון והסקריפטים מ-CDN ב-https://cdn.mybank.net ומתחבר באמצעות XHR אל https://api.mybank.com/ כדי למשוך למטה קטעי נתונים שונים. נעשה שימוש במסגרות, אבל רק לדפים מקומיים באתר (ללא מקורות של צד שלישי). אין Flash באתר, אין גופנים, בלי תוספות. כותרת ה-CSP המגבילה ביותר שאנחנו יכולים לשלוח היא:

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

תרחיש לדוגמה מס' 3: SSL בלבד

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

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

למרות שה-https: מצוין ב-default-src, הנחיות הסקריפט והסגנון לא יורשים את המקור הזה באופן אוטומטי. כל הוראה מחליפה לחלוטין את ברירת המחדל של סוג המשאב הספציפי הזה.

העתיד

Content Security Policy רמה 2 היא המלצה לגבי מועמדים. קבוצת העבודה בנושא אבטחת אפליקציות אינטרנט של W3C כבר התחילה לעבוד על האיטרציה הבאה של המפרט, Content Security Policy Level 3.

אם אתם מעוניינים בדיון על התכונות החדשות האלה, תוכלו לעיין בארכיונים של רשימת התפוצה public-webappsec@ או להצטרף בעצמכם.

משוב