כאשר ה- CSS in JS שלכם יכול להזיק לאבטחה (כן)

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

הפוסט הזה נכתב בעקבות הרצאה שהעברתי ב React Next 2024. ניתן לראות את ההרצאה פה:

הפוסט הוא תקציר או בסיס להרצאה ומכיל קישורים וקצת מידע נוסף 🙂

כשמתכננים אפליקצית צד לקוח, חשוב לקחת בחשבון כיצד בחירה בטכניקת ה-CSS משפיע על אבטחת היישום שלנו. זה אולי נשמע מוזר, אבל גישות עיצוב יכולות לפעמים לגרום צרות צרורות בכל ענייני האבטחה. כן, זה משהו שצ׳אט GPT לא יגיד לך כשתבקש ממנו לכתוב קוד 😇

בפוסט הזה, אסביר את הקשר בין מדיניות CSP לבחירת טכניקת CSS ולמה ההתנגשות מתרחשת, במיוחד עם רכיבי CSS-in-JS ביישומי SSR (רינדור בצד השרת כמו next.js) ו-CSR (רינדור בצד הלקוח כמו Create React App או Vite הקלאסי).

מה זה CSP?

Content Security Policy (CSP) הוא פיצ׳ר בדפפדפן שנועד לעצור התקפות כמו Cross-Site Scripting (XSS), clickjacking, והתקפות הזרקת קוד אחרות שנובעות מהפעלת תוכן זדוני בהקשר האינטרנט המהימן. בכמה דרכים וישנם מדיניות שונה בהתאם למשאבים: JS, תמונות וכו'.
הנה דוגמה לכותרת CSP:


כאשר דפדפן מקבל Header שנראה, הוא לא מאפשר הורדת JS ממקורות חיצוניים חוץ מאשר הדומיין המקורי ובדוגמה הזו: aps.google.com.

הנה דוגמה מהעולם האמיתי לכותרת CSP ב-GitHub:

כתבתי על CSP בהרחבה בפוסט הזה.

מה זה CSP Style?

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

style-src: self;

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

מה זה CSS-in-JS?

בפיתוח צד-לקוח ישנן טכניקות CSS-in-JS – הידועה ביותר היא Styled Components והטרנדית יותר היא ספריית Emotion.
Styled Components לוקחת את הוראות ה-CSS מקבצי TSX\JSX\TS\JS ומקמפלת את ה-CSS בתוך תגית סגנון (inline style) בזמן אמת בצד הלקוח. ממש בתוך תגית style.

מה הבעיה? שזה בחיים לא יתרנדר עם CSP נורמלי כי זה לא מכיל הנחיה של unsafe-inline ב-style.
השימוש ב-unsafe-inline כפי ששמו מרמז, לא בטוח. תוקף זדוני יכול לשתול קריאה ל-CSS בתוך היישום. גם בוא ונגיד שכל pentester יבוא ויתן הערה על זה.

כיצד להשתמש ב-CSP ב-SSR באמצעות Nonce

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

כיצד למנוע התקפות ב-CSR באמצעות Zero-Runtime CSS-in-JS

אם אתם משתמשים ביישום סטטי ללא שרת מעורב, Nonce אינו רלוונטי. לחלופין, תוכלו להשתמש בגישת Zero-Runtime CSS-in-JS. גישה זו משתמשת ב-CSS-in-JS, אך במקום להכניס את ה-CSS ב-inline, היא מקמפלת את ה-CSS בשלב הבנייה ומייצאת אותו לקבצי CSS סטטיים.
שימוש בקבצי CSS סטטיים מאפשר לנו להשתמש ב:

style-src: self;

ומה שהופך אותו לאידיאלי.

אבל איך אפשר להשתמש ב-CSS-in-JS בלי תגיות סגנון inline? עם ספריות Zero-Runtime CSS. הספריות הללו מאפשרות לנו לכתוב CSS-in-JS, בדומה ל-Emotion או Styled Components, תוך כדי יצירת קבצי CSS סטטיים בתהליך הבנייה.

בואו ניקח לדוגמה את Linaria. Linaria היא ספריית CSS-in-JS שעובדת כמו Emotion ו-Styled Components אך יוצרת קובץ CSS חיצוני. זו ספריית Zero-Runtime CSS-in-JS. היא מתוחזקת היטב ומגובה על ידי Callstack. ישנן חלופות ל-Linaria, אך היא החלופה הפופולרית ביותר ליצירת CSS דינמי.

לסיכום

אם אתם משתמשים בספריות CSS-in-JS, עליכם לבחור את הספרייה בהתאם ליישום שלכם (ISR או CSR) אם אתם רוצים ליישם מדיניות CSP שאינה "unsafe-inline". ייתכן שתשקלו גם להימנע משימוש בספריות CSS-in-JS שהן לא zero runtime. יש מצב שתוכלו לחיות עם unsafe-inline – אבל זו החובה שלכם כמתכנתים לדעת ולהכיר את הבעיות האלו.

תודה גדולה ל: ינאי אדרי, שגיא ליבה, חן ראובן על שקראו והעירו על הפוסט הזה בגרסתו באנגלית

פוסטים נוספים שכדאי לקרוא

תמונת תצוגה של מנעול על מחשב
פתרונות ומאמרים על פיתוח אינטרנט

הגנה מפני XSS עם Trusted Types

תכונה ב-CSP שמאפשרת מניעה כמעט הרמטית להתקפות XSS שכל מפתח ווב צריך להכיר וכדאי שיכיר.

רספברי פיי

הרצת גו על רספברי פיי

עולם הרספברי פיי והמייקרים ניתן לתפעול בכל שפה – לא רק פייתון או C – כאן אני מסביר על גו

גלילה לראש העמוד