מודל האבטחה של האינטרנט מבוסס על שורש של מדיניות המקור הזהה. לקוד מ-https://mybank.com
צריכה להיות גישה רק לנתונים של https://mybank.com
, ובטח שאסור לתת גישה ל-https://evil.example.com
.
כל מקור נשמר בנפרד משאר האינטרנט, וכך מפתחים ארגז חול בטוח שבו הם יכולים לבנות ולשחק. בתיאוריה, זה רעיון מצוין. בפועל, תוקפים מצאו דרכים חכמות לעקוף את המערכת.
לדוגמה, תקיפות של פרצת אבטחה XSS (cross-site scripting) עוקפות את מדיניות המקור הזהה על ידי הטעיה של אתר להעברת קוד זדוני יחד עם התוכן המיועד. זוהי בעיה גדולה, כי הדפדפנים סומכים על כל הקוד שמופיע בדף כחלק לגיטימי ממקור האבטחה של הדף. הקובץ XSS Cheat Sheet הוא סקירה כללית ישנה אך מייצגת של השיטות שבהן תוקף עלול להשתמש כדי להפר את האמון הזה על ידי הזרקת קוד זדוני. אם תוקף מצליח להחדיר קוד כלשהו, זה בדרך כלל סימן לכך שהמשחק נגמר: נתוני הסשן של המשתמש נחשפו ומידע שצריך להישמר בסוד הודלף לאנשים הרעים. ברור שאנחנו רוצים למנוע את זה, אם אפשר.
בסקירה הכללית הזו נסביר על אמצעי הגנה שיכול להפחית באופן משמעותי את הסיכון ואת ההשפעה של מתקפות XSS בדפדפנים מודרניים: מדיניות אבטחת תוכן (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, וגם מהמקור של הדף הנוכחי.
כשהמדיניות הזו מוגדרת, הדפדפן פשוט גורם לשגיאה במקום לטעון סקריפט ממקור אחר כלשהו. כשתוקף חכם מצליח להחדיר קוד לאתר, הוא עלול להציג הודעת שגיאה במקום מוקדם מאשר את ההצלחה שציפו לה.
המדיניות חלה על מגוון רחב של משאבים
משאבי סקריפט הם סיכוני האבטחה הבולטים ביותר, אבל 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
מפורטות נקודות קצה תקינות לשליחה מתגי<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
היא הוראה ברמה 3 של CSP שמגבילה את כתובות ה-URL שאפשר לטעון כ-worker, shared worker או service worker. נכון ליולי 2017, יש ל-directive הזו הטמעות מוגבלות.
כברירת מחדל, ההוראות פתוחות לכולם. אם לא תגדירו מדיניות ספציפית להנחיה, נניח 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
. לפעולה הזו יכולות להיות מגוון רחב של השפעות על הדף: אילוץ הדף למקור ייחודי ומניעה של שליחת טופס, בין היתר. זה קצת מעבר להיקף המאמר הזה, אבל אפשר למצוא פרטים מלאים על מאפיינים תקינים של ארגז חול בקטע 'ארגז חול' במפרט 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: הזרקת סקריפט בקוד.
אם תוקף יכול להחדיר תג סקריפט שמכיל ישירות תוכנה זדונית כלשהי (<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 מוטמע מבנה והתנהגות בדיוק בדרך שאסור לעשות זאת. קל יותר לדפדפנים לשמור במטמון מקורות חיצוניים, הם מובנים יותר למפתחים ומתאימים יותר ל-compilation ול-minification. כשתעבירו את הקוד למשאבים חיצוניים, תוכלו לכתוב קוד טוב יותר.
סגנון בקוד מטופל באותו אופן: צריך לאחד את המאפיין style
ואת התגים style
בגיליון סגנונות חיצוני כדי להגן מפני מגוון שיטות חכמות להפליא של זליגת נתונים ש-CSS מאפשר.
אם אתם חייבים להשתמש בסקריפט ובסגנון בתוך שורת הטקסט, תוכלו להפעיל אותם על ידי הוספת 'unsafe-inline'
כמקור מורשה בהנחיה script-src
או style-src
. אפשר גם להשתמש ב-nonce או ב-hash (ראו בהמשך), אבל לא מומלץ לעשות זאת.
איסור סקריפטים מוטמעים הוא היתרון הגדול ביותר של שירותי CSP מבחינת אבטחה, והאיסור על סגנונות מוטמעים מחזק את האפליקציה באופן דומה. צריך להשקיע קצת מאמץ מראש כדי לוודא שהכול עובד כמו שצריך אחרי שמעבירים את כל הקוד מחוץ לשורה, אבל זה שווה את המאמץ.
אם אתם חייבים להשתמש בו
ברמת CSP 2 יש תאימות לאחור לסקריפטים מוטמעים, כי אפשר להוסיף סקריפטים מוטמעים ספציפיים לרשימת ההיתרים באמצעות גיבוב או צפן חד-פעמי (nonce) קריפטוגרפי. יכול להיות שזה יהיה מסורבל, אבל זה שימושי במקרה הצורך.
כדי להשתמש בצפנים חד-פעמיים, צריך לתת לתג הסקריפט מאפיין צופן חד-פעמי (nonce). הערך שלו חייב להתאים לאחד מהערכים ברשימת המקורות המהימנים. לדוגמה:
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// Some inline code I can't remove yet, but need to asap.
</script>
עכשיו מוסיפים את המזהה החד-פעמי להנחיה 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 הנכון לכל אחד מהסקריפטים שנוספו בקוד.
Eval too
גם אם תוקף לא יכול להחדיר סקריפט ישירות, יכול להיות שהוא יוכל לגרום לאפליקציה להמיר טקסט לא פעיל ל-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()
כדי לזרז את יצירת התבניות בזמן הריצה. זוהי שיטה יעילה לשימוש בתוכנה דינמית, אבל היא עלולה להוביל להערכה של טקסט זדוני. יש מסגרות שמספקות תמיכה ב-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 פרסו את הכותרת (כדאי לקרוא את המחקר של Twitter), והסטנדרט מוכן לפריסה באתרים שלכם.
השלב הראשון ביצירת מדיניות לאפליקציה הוא להעריך את המשאבים שאתם בעצם טוענים. אחרי שתסיימו להבין איך הנתונים מקובצים באפליקציה, תוכלו להגדיר מדיניות על סמך הדרישות האלה. נעבור על כמה תרחישים נפוצים לשימוש ונראה איך אנחנו יכולים לתמוך בהם בצורה הטובה ביותר במסגרת ההגנה של 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>
כי היא מבודדת בבטחה בקופסה חולית משאר האתר. כדי לפעול כראוי, הוא דורש הוראה מסוג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 בנושא אבטחת אפליקציות אינטרנט כבר התחילה לעבוד על הגרסה הבאה של המפרט, מדיניות אבטחת תוכן ברמה 3.
אם אתם רוצים להצטרף לדיון על התכונות העתידיות האלה, תוכלו לעיין בארכיונים של רשימת התפוצה public-webappsec@ או להצטרף לדיון בעצמכם.