בקשות HTTP מכילות כותרות כמו User-Agent או Content-Type. בנוסף לכותרות שמצורפות על ידי הדפדפנים, אפליקציות ל-Android יכולות להוסיף כותרות נוספות, כמו Cookie או Referrer, באמצעות ה-Intent extra EXTRA_HEADERS
. מטעמי אבטחה, Chrome מסנן חלק מהכותרות הנוספות בהתאם לאופן שבו ה-intent מופעל ולאיפה הוא מופעל.
בקשות ממקורות שונים מחייבות שכבת אבטחה נוספת, כי הלקוח והשרת לא בבעלות אותו צד. במדריך הזה נסביר איך מפעילים בקשות כאלה דרך כרטיסיות בהתאמה אישית ב-Chrome, כלומר כוונות שמופעלות מאפליקציות שפותחות כתובת URL בכרטיסייה בדפדפן. עד גרסה 83 של Chrome, מפתחים יכלו להוסיף כותרות כלשהן כשהפעילו כרטיסייה מותאמת אישית. החל מגרסה 83, Chrome התחיל לסנן את כל הכותרות שמקורן במקור אחר, מלבד כותרות ברשימת האישור, כי כותרות שלא נמצאות ברשימת האישור מהוות סיכון אבטחה. החל מ-Chrome 86, אפשר לצרף כותרות שלא נמצאות ברשימת האישורים לבקשות ממקורות שונים, כשהשרת והלקוח קשורים באמצעות קישור לנכס דיגיטלי. ההתנהגות הזו מסכמת בטבלה הבאה:
גרסת Chrome | מותר להשתמש בכותרות CORS |
---|---|
לפני Chrome 83 | ברשימת ההיתרים, לא ברשימת ההיתרים |
מ-Chrome 83 עד Chrome 85 | approvelisted |
מ-Chrome 86 ואילך | ברשימה המאושרת, לא ברשימה המאושרת כשמגדירים קישור לנכס דיגיטלי |
טבלה 1: סינון של כותרות CORS שלא נמצאות ברשימת ההיתרים.
במאמר הזה נסביר איך להגדיר חיבור מאומת בין השרת ללקוח, ולהשתמש בו כדי לשלוח כותרות HTTP שרשומים ברשימה המאושרת וגם כותרות HTTP שלא רשומים ברשימה המאושרת. אפשר לדלג אל הוספת כותרות נוספות לכוונות שימוש מותאמות אישית בכרטיסיות כדי לקבל את הקוד.
רקע
כותרות של בקשות CORS ברשימת האישור לעומת כותרות של בקשות CORS שלא ברשימת האישור
שיתוף משאבים בין מקורות (CORS) מאפשר לאפליקציית אינטרנט ממקור אחד לבקש משאבים ממקור אחר. רשימת הכותרות שאושרו על ידי CORS מתעדכנת בתקן HTML. דוגמאות לכותרות ברשימת האישור מפורטות בטבלה הבאה:
כותרת | תיאור |
---|---|
accept-language | מפרסם שפות טבעיות שהלקוח מבין |
content-language | מתארת את השפה המיועדת לקהל הנוכחי |
content-type | מציין את סוג המדיה של המשאב |
טבלה 2: דוגמאות לכותרות CORS ברשימת האישור.
הכותרות ברשימת האישור נחשבות בטוחות כי הן לא מכילות מידע רגיש של משתמשים, וסבירות נמוכה שהן יגרמו לשרת לבצע פעולות שעלולות להזיק.
דוגמאות לכותרות שלא נכללות ברשימת הכותרות שאושרו מפורטות בטבלה הבאה:
כותרת | תיאור |
---|---|
bearer-token | אימות לקוח בשרת |
מקור | מציין את מקור הבקשה |
קובץ cookie | מכיל קובצי cookie שהוגדרו על ידי השרת |
טבלה 3: דוגמה לכותרות CORS שלא נמצאות ברשימת האישור.
מומלץ לא לצרף כותרות שלא נמצאות ברשימת האישור לבקשות CORS, כי תקן ה-HTML אוסר על כך, והשרתים מניחים שבקשות ממקורות שונים מכילות רק כותרות שנמצאות ברשימת האישור. שליחת כותרות שלא נמצאות ברשימת ההיתרים מדומיינים שונים תאפשר לאפליקציות זדוניות של צד שלישי ליצור כותרות שמנצלות לרעה קובצי cookie של משתמשים ש-Chrome (או דפדפן אחר) שומר ומצרף לבקשות. קובצי ה-cookie יכולים לאמת עסקאות זדוניות בשרת שלא ניתן היה לבצע אחרת.
צירוף כותרות מרשימה מאושרת של CORS לבקשות של כרטיסיות בהתאמה אישית
כרטיסיות בהתאמה אישית הן דרך מיוחדת להפעלת דפי אינטרנט בכרטיסייה מותאמת אישית בדפדפן. אפשר ליצור כוונות טירגוט בהתאמה אישית של כרטיסיות באמצעות CustomTabsIntent.Builder()
. אפשר גם לצרף כותרות לכוונות האלה באמצעות Bundle
עם הדגל Browser.EXTRA_HEADERS
:
CustomTabsIntent intent = new CustomTabsIntent.Builder(session).build();
Bundle headers = new Bundle();
headers.putString("bearer-token", "Some token");
headers.putString("redirect-url", "Some redirect url");
intent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
intent.launchUrl(Activity.this, Uri.parse("http://www.google.com"));
אנחנו תמיד יכולים לצרף כותרות מרשימת האישורים לבקשות CORS של כרטיסיות בהתאמה אישית. עם זאת, כברירת מחדל, Chrome מסנן כותרות שלא נמצאות ברשימת ההיתרים. יכול להיות שבדפדפנים אחרים ההתנהגות תהיה שונה, אבל באופן כללי, מפתחים צריכים לצפות שהכותרות שלא נמצאות ברשימת ההיתרים ייחסמו.
כדי לכלול כותרות שלא נמצאות ברשימת ההיתרים בטאבים מותאמים אישית, צריך קודם לאמת את החיבור בין מקורות באמצעות קישור גישה דיגיטלי. בקטע הבא מוסבר איך להגדיר את הרכיבים האלה ולהפעיל כוונה של כרטיסיות בהתאמה אישית עם הכותרות הנדרשות.
הוספת כותרות נוספות לכוונות של כרטיסיות בהתאמה אישית
הגדרת קישורים לנכסים דיגיטליים
כדי לאפשר להעביר כותרות שלא נמצאות ברשימת האישור דרך כוונות של כרטיסיות בהתאמה אישית, צריך להגדיר קישור של נכס דיגיטלי בין אפליקציית Android לאפליקציית האינטרנט, שמאמת שהמחבר הוא הבעלים של שתי האפליקציות.
פועלים לפי המדריך הרשמי כדי להגדיר קישור לנכס דיגיטלי. ביחס הקישור, צריך להשתמש ב-'delegate_permission/common.use_as_origin', שמציין ששתי האפליקציות שייכות לאותו מקור אחרי שהקישור מאומת.
יצירת כוונת רכישה מותאמת אישית בכרטיסייה עם כותרות נוספות
יש כמה דרכים ליצור כוונה של כרטיסיות בהתאמה אישית. כדי להשתמש ב-builder שזמין ב-androidX, מוסיפים את הספרייה ליחסי התלות של ה-build:
implementation 'androidx.browser:browser:1.2.0'
יוצרים את הכוונה ומוסיפים כותרות נוספות:
CustomTabsIntent constructExtraHeadersIntent(CustomTabsSession session) {
CustomTabsIntent intent = new CustomTabsIntent.Builder(session).build();
// Example non-cors-approvelisted headers.
Bundle headers = new Bundle();
headers.putString("bearer-token", "Some token");
headers.putString("redirect-url", "Some redirect url");
intent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
return intent;
}
הגדרת חיבור של כרטיסיות בהתאמה אישית כדי לאמת את הקישור לנכס
חיבור של כרטיסיות בהתאמה אישית משמש להגדרת CustomTabsSession
בין האפליקציה לבין הכרטיסייה ב-Chrome. אנחנו זקוקים לסשן כדי לאמת שהאפליקציה ואפליקציית האינטרנט שייכות לאותו מקור.
האימות עובר רק אם קישורי הנכסים הדיגיטליים הוגדרו בצורה נכונה.
מומלץ להתקשר למספר CustomTabsClient.warmup()
. היא מאפשרת לאפליקציית הדפדפן לבצע את האיניציאליזציה מראש ברקע ולהאיץ את תהליך פתיחת כתובת ה-URL.
// Set up a connection that warms up and validates a session.
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(@NonNull ComponentName name,
@NonNull CustomTabsClient client) {
// Create session after service connected.
mSession = client.newSession(callback);
client.warmup(0);
// Validate the session as the same origin to allow cross origin headers.
mSession.validateRelationship(CustomTabsService.RELATION_USE_AS_ORIGIN,
Uri.parse(url), null);
}
@Override
public void onServiceDisconnected(ComponentName componentName) { }
};
הגדרת קריאה חוזרת שמפעילה את הכוונה אחרי האימות
הערך של CustomTabsCallback
הועבר לסשן. אנחנו מגדירים את onRelationshipValidationResult()
כך שיפעיל את CustomTabsIntent
שנוצר קודם, אחרי שהאימות של המקור מסתיים בהצלחה.
// Set up a callback that launches the intent after session validated.
CustomTabsCallback callback = new CustomTabsCallback() {
@Override
public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
boolean result, @Nullable Bundle extras) {
// Launch custom tabs intent after session was validated as the same origin.
CustomTabsIntent intent = constructExtraHeadersIntent(mSession);
intent.launchUrl(MainActivity.this, Uri.parse(url));
}
};
איך מקשרים את החיבור לשירות של הכרטיסיות בהתאמה אישית
קישור השירות מפעיל אותו, ובסופו של דבר תתבצע קריאה ל-onCustomTabsServiceConnected()
של החיבור. אל תשכחו לבטל את הקישור של השירות בצורה מתאימה. בדרך כלל, הקישור והביטול מתבצעים בשיטות של מחזור החיים של הפעילות onStart()
ו-onStop()
.
// Bind the custom tabs service connection.
// Call this in onStart()
CustomTabsClient.bindCustomTabsService(this,
CustomTabsClient.getPackageName(MainActivity.this, null), connection);
// …
// Unbind the custom tabs service.
// Call this in onStop().
unbindService(connection);
קוד אפליקציית הדגמה
כאן אפשר למצוא פרטים נוספים על השירות של כרטיסיות בהתאמה אישית. במאגר GitHub android-browser-helper תוכלו למצוא אפליקציה לדוגמה שעובדת.
סיכום
במדריך הזה הראינו איך מוסיפים כותרות שרירותיות לבקשות CORS של כרטיסיות בהתאמה אישית. אפשר לצרף כותרות מהרשימה המאושרת לכל בקשת CORS של כרטיסיות בהתאמה אישית. בדרך כלל, כותרות שלא נמצאות ברשימת האישור נחשבות לא בטוחות בבקשות CORS, ו-Chrome מסנן אותן כברירת מחדל. רק לקוחות ושרתים מאותו מקור, שמאומתים באמצעות קישור לנכס דיגיטלי, יכולים לצרף אותם.