כדי לספק אימות חלק בהקשר של דומיינים שונים, ארגונים מטמיעים לעיתים קרובות דפי כניסה בתוך תגי iframe. עם זאת, טעינה של הקשרים של אימות בתוך מסגרות של צד שלישי חושפת את המשתמשים לאיומים קריטיים כמו הונאת קליקים (הצגת ממשק משתמש מטעה) ויצירה לא מורשית של פרטי כניסה. כדי להפחית את הסיכונים האלה, הדפדפנים משביתים את WebAuthn ב-iframe בין מקורות כברירת מחדל. כדי להסיר את ההגבלה הזו בצורה בטוחה, צריך להשתמש בפרוטוקולים פעילים של הגנה לעומק.
זיהוי מודלים של איומים
לפני שמפעילים מפתחות גישה (WebAuthn) בתוך מסגרות משנה, חשוב להבין את תרחישי השימוש לרעה שאתם מנסים להתגונן מפניהם:
- מעקב באמצעות הזרקת iframe מוסתר: תוקף מפעיל הנחיה של WebAuthn מהדומיין שלו באמצעות מודעה או ווידג'ט באתר מהימן, וגורם למשתמשים לאשר מפתח גישה בלי לראות את ההקשר. הפעולה הזו מקשרת את הזהות של המשתמש לחשבון שנמצא בשליטת התוקף, כדי לאסוף נתונים.
- שכבת-על חזותית וגניבת קליקים (הונאת ממשק משתמש): דף אב זדוני מעבד את ה-iframe של האימות באופן בלתי נראה באמצעות CSS רגיל, ומציג שכבת-על של רכיב בממשק המשתמש מזויף כדי לגנוב קליק שמפעיל תהליך אימות. אם המשתמש משלים את ההנחיה בטעות, זה עלול לגרום לחטיפת סשן או לפעולות לא מורשות בכפייה.
כדי להתמודד עם האיומים האלה, כדאי לפעול לפי השיטות המומלצות הבאות:
למסמך ברמה העליונה (הפריים העליון):
למסמך המוטמע (iframe):
- הפעלת קובצי Cookie של צד שלישי עם חלוקה למחיצות
- הגנה על נקודת הקצה באמצעות Content Security Policy
- מהימנות, אבל אימות בצד השרת
בשני המסמכים:
הפעלת האצלה באמצעות מדיניות הרשאות
כברירת מחדל, דפדפנים חוסמים גישה ל-WebAuthn ב-iframes חוצי-מקורות. Permissions Policy הוא מנגנון מאוחד של פלטפורמת האינטרנט שמאפשר למסמך ברמה העליונה להעביר באופן מפורש את היכולות המתקדמות האלה למקורות ספציפיים ומהימנים של צד שלישי.
טוקנים של תכונות
ב-WebAuthn נעשה שימוש בשני אסימונים שונים:
-
publickey-credentials-get: מעניק הרשאה לתהליכי כניסה באמצעות מפתח גישה (navigator.credentials.get()). publickey-credentials-create: מעניק הרשאה לתהליכי רישום של מפתחות גישה (navigator.credentials.create()).
דרישות להפעלה
כדי להפעיל את היכולות האלה, צריך לבצע התאמה גם בתגובה של שרת האב וגם בתגי העיצוב בצד הלקוח:
- כותרת תגובת HTTP של מדיניות ההרשאות (השרת של האתר המארח): בדף המארח צריך להצהיר על המקורות המותרים בכותרות תגובת ה-HTTP באמצעות תחביר של שדות מובנים.
Permissions-Policy: publickey-credentials-get=(self "https://embedded-auth.example.com")
מדיניות ההרשאות: תאימות ל-publickey-credentials-get:
מדיניות הרשאות: תאימות ל-publickey-credentials-create:
- המאפיין
allowשל HTML: בתגי העיצוב של HTML, צריך גם להצהיר על הפעלת התכונה ברכיב<iframe>.
<iframe src="https://embedded-auth.example.com?nonce=deadbeef12345678&client=https%3A%2F%2Fembedded-auth.example.com" allow="publickey-credentials-get"></iframe>
תאימות של iframe עם allow="publickey-credentials-get":
Browser Support
תאימות של iframe עם allow="publickey-credentials-create":
Browser Support
הפעלת קובצי Cookie מחולקים של צד שלישי
כדי להבטיח תהליך אימות מהימן, צריך ליצור סשן ולשמור עליו ב-iframe המוטמע חוצה-הדומיינים. ככל שהדפדפנים המודרניים עוברים להגבלות מחמירות על קובצי Cookie של צד שלישי, מנגנוני התמדה רגילים נחסמים לעיתים קרובות כברירת מחדל, ועשוי להיות צורך להפעיל את Storage Access API כדי לקבל גישה.
כדי להתגבר על המכשולים האלה, צריך להגדיר את קובצי ה-Cookie של הסשן עם המאפיינים SameSite:
None, Secure ו-Partitioned. מנגנון הפלטפורמה המאוחד הזה מבטיח מצב מתמשך בתוך ה-iframe, תוך התחשבות באמצעי הבקרה של הפרטיות ברמת הדפדפן.
הגדרה של SameSite: None
SameSite:
None
מציין באופן מפורש קובץ Cookie לגישה חוצת-אתרים, ומאפשר לשלוח אותו עם
בקשות שמוגשות מהקשר של צד שלישי (כמו iframe). המאפיין הזה הוא תנאי מוקדם לפעולה של קובצי Cookie בתרחישים של בקשות חוצות-מקורות, אבל צריך לשלב אותו עם המאפיין Secure כדי שדפדפנים מודרניים יקבלו אותו.
הגדרה של Partitioned
המאפיין Partitioned מאפשר להשתמש בקובץ ה-Cookie ב-CHIPS (קובצי Cookie עם חלוקה עצמאית למחיצות), ומאפשר לאחסן את קובץ ה-Cookie בנפרד לכל אתר ברמה העליונה. כך מוודאים שקובץ ה-Cookie נשאר נגיש בהקשר הספציפי של ה-iframe של הצד השלישי, ומאפשרים מצב סשן מתמשך בלי להפעיל מעקב באתרים שונים. המשתמש יצטרך להיכנס שוב לכל הטמעה באתר אחר.
הגנה על נקודת הקצה באמצעות Content Security Policy
בזמן שמדיניות ההרשאות קובעת אם אפשר להריץ WebAuthn ב-iframe, Content Security Policy (CSP) קובעת למי מותר לארח את ה-iframe.
בנקודת קצה לאימות, חשוב לוודא שרק אתרי שותפים מורשים או הנכסים שלכם יכולים לטעון את מסגרת המשנה של הכניסה, וכך למנוע ניסיונות לא מורשים של הונאת קליקים עוד לפני שהממשק נטען.
שימוש ב-frame-ancestors
frame-ancestors
ההוראה
מגדירה את דפי ההורה התקפים שיכולים להטמיע את האתר שלכם. אם מוסיפים דומיינים להנחיה הזו, אפשר לאשר את הדומיינים שמותר להם להטמיע את מסגרת המשנה של הכניסה.
Content-Security-Policy: frame-ancestors 'self' https://parent-site.example.com;
Content Security Policy: תאימות של frame-ancestors:
הגדרה של X-Frame-Options
הכותרת מדור קודם X-Frame-Options תומכת ביכולת דומה, אבל רק באפשרויות בינאריות (DENY או SAMEORIGIN). כדאי להגדיר גם את CSP frame-ancestors וגם את X-Frame-Options: DENY למקרה שהדפדפן לא תומך ב-CSP. ל-CSP תמיד יש עדיפות במקומות שבהם היא נתמכת.
X-Frame-Options: DENY
תאימות של X-Frame-Options:
מהימנות, אבל אימות בצד השרת
הבדיקות בצד הלקוח בדפדפן מעריכות את הכוונה וההרשאות, אבל השרת הוא הגורם הסופי שקובע את רמת האמון. צריך לאמת את התגובה בשרת של הצד המסתמך (RP) כדי לוודא שההקשר תקף וחתום.
מטען ייעודי (payload) של נתוני לקוחות
נתוני הלקוח של WebAuthn כוללים פרמטרים שנועדו לעזור לכם לאמת את ההקשר של בקשה שמוגשת בתוך iframe:
-
crossOrigin(בוליאני): מציין אם הופעל WebAuthn API בתוך iframe ממקורות שונים. אם הארכיטקטורה שלכם מסתמכת על תגי iframe, השרת חייב לאכוף שהדגל הזה הואtrue. -
topOrigin(מחרוזת): המקור של הקשר הגלישה ברמה העליונה (מה שמוצג בסרגל הכתובות של הדפדפן). השרת חייב לאמת את זה מול רשימה של מקורות הורים מוכרים ומורשים.
רשימת משימות לאימות
כדי לאמת את התשובה של אמצעי האימות בשרת, מבצעים את השלבים הבאים:
- מנתחים ומפענחים את החתימה
collectedClientDataמהתגובה של אמצעי האימות. - מוודאים שהערך של
typeתואם לטקס (webauthn.getאוwebauthn.create). - מאמתים את נוכחות המשתמש והחתימה שלו.
- אם הבקשה נועדה להגיע ממבנה iframe:
- אכיפה של
crossOrigin === true. - הגדרת אכיפה של התאמה בין
topOriginלבין רשימת המקורות המורשים של ההורה.
- אכיפה של
יצירת סשנים בצורה מאובטחת באמצעות postMessage()
כדי ליצור סשן באופן מהימן, ה-iframe צריך להעביר את אסימון האימות בחזרה לדף ההורה באמצעות postMessage(), כדי שההורה יוכל לנהל את מצב הסשן בהקשר של הנתונים שנאספים ישירות ממשתמשים שלו.
תהליך עבודה מאובטח
כדי ליצור סשן מאובטח, פועלים לפי תהליך העבודה הבא:
- מוודאים שכתובת ה-URL של ה-iframe
srcמכילה פרמטרים של שאילתהnonceו-origin:- צריך להזין ערך אקראי במאפיין
nonce.nonceמשמש כאסימון לאימות אבטחה כדי לוודא שאסימון האימות שהתקבל מ-iframe תואם באופן לגיטימי לסשן הספציפי שהופעל על ידי דף האב. - משתמשים בדומיין של מסגרת ההורה בשביל
origin. פרמטרoriginמציין את המוצא של דף ההורה, וכך מאפשר ל-iframe לזהות בצורה מאובטחת את ההקשר המורשה שבו הוא מוטמע.
- צריך להזין ערך אקראי במאפיין
- ה-iframe משלים את האימות של WebAuthn עם השרת שלו.
שרת ה-iframe מנפיק טוקן, כמו JWT, שכולל את
nonceומעביר אותו לדף ההורה.// Extract nonce and origin from the URL params const urlParams = new URLSearchParams(window.location.search); const nonce = urlParams.get('nonce'); const origin = urlParams.get('origin'); if (!nonce || !origin) { alert('Nonce or origin is missing in the URL'); return; } // Create a JWT const response = await post('/createToken', { nonce, origin }); const token = response.token; // Post the JWT to the parent frame window.parent.postMessage({ token }, origin);דף ההורה מקשיב לאירוע
message, מאמת את המקור של השולח ומאמת את האסימון.window.addEventListener("message", (event) => { if (event.origin !== "https://embedded-auth.example.com") return; // Verify the received JWT const result = await post('/verifyIdToken', { token: event.data.token, origin: provider.origin, }); });אם ה-JWT מאומת בהצלחה, דף ההורה שומר את הסשן.
השולח והמקבל חולקים את האחריות לאבטחה:
- השולח (iframe): תמיד צריך לציין מקור יעד מדויק כששולחים הודעות (אף פעם לא להשתמש ב-
"*"). - הנמען (ההורה): תמיד צריך לאמת את
event.originכשמקבלים הודעות כדי למנוע זיוף מקור.
סיכום
השימוש ב-iframe בצורה בטוחה מתבסס על מדיניות ההרשאות להפעלה, על CSP להגבלה, על קובצי Cookie של צד שלישי עם חלוקה למחיצות כדי לשמור על נתוני הסשן, על אימות בצד השרת של הקשר של הלקוח ועל העברת סשן בהתאם להקשר באמצעות postMessage().
כדי לקרוא מידע נוסף על נושאים קשורים, אפשר לעקוב אחרי הבלוג של Google למפתחי Chrome ולעיין במקורות מידע נוספים בתיעוד בנושא זהות מפתח Chrome.