חסימת טקסט גלוי באפליקציות Android בלי להסתמך על VPN
טקסט גלוי במטען וטקסט גלוי בנתיב הם שני בעיות שונות. תקנו את הראשונה באפליקציה, ואת השנייה במנהרה או במדיניות מכשיר.
שני דברים שונים לגמרי נקראים חסימת טקסט גלוי ב-Android. האחד הוא מדיניות בתוך אפליקציה שמורה למערכת הרשת שלה לסרב ל-HTTP פשוט. השני הוא הסדר ברמת המכשיר שמנסה להגן על הכול בטלפון בבת אחת, בדרך כלל על ידי העברת התעבורה דרך VPN. הם פותרים בעיות שונות, והשני לא יכול לתקן את הראשון: מנהרה מגנה על הקטע בין הטלפון ליציאה, ואילו אפליקציה שמדברת HTTP ממשיכה לדבר HTTP עם כל מה שנמצא מעבר ליציאה הזו.
שתי בעיות תחת שם אחד
הבעיה הראשונה היא המטען: האפליקציה כותבת פרטי גישה או אסימונים בבקשה שאינה מוצפנת מקצה לקצה, כך שכל דילוג שיכול לראות אותה יכול לקרוא אותה. השנייה היא הנתיב: האפליקציה יכולה להיות תקינה לחלוטין, ואתם עדיין רוצים שמפעיל ה-Wi-Fi המקומי לא ילמד דבר על היכן היא מתחברת. VPN מטפל בשנייה ורק בשנייה.
ההבחנה הזו קובעת למי שייך התיקון. המטען שייך למי שמפיץ את האפליקציה, כי רק הקוד הזה קובע איזו כתובת נקראת והאם תעודה מאומתת. הנתיב שייך למכשיר או לרשת, כי השאלה היא לאן החבילות נוסעות ולא מה הן מכילות.
נתיב ראשון: האפליקציה שאתם שולטים בה
אם אתם מפיצים את האפליקציה, התיקון הוא החלטת תעבורה יחד עם מדיניות שאוכפת אותה. כוונו כל נקודת קצה ל-HTTPS, השאירו את אימות שם המארח והתעודה דלוק, ומחקו את הנפילה ל-HTTP פשוט במקום להשאיר אותה במרחק דגל תצורה אחד. הפניה מחדש לא עוזרת כאן: בקשת HTTP שמקבלת 301 כבר שלחה את הכותרות והגוף שלה בגלוי, ולכן אסור שפרטי גישה יעברו אי פעם מתוך הנחה שההפניה תגן עליהם.
ב-Android 7.0 ואילך, Network Security Configuration מבטא את המדיניות. הקובץ שלמטה הוא הגרסה השימושית הקטנה ביותר: הוא מסרב לטקסט גלוי בכל מקום אלא אם חריגה ספציפית לתחום אומרת אחרת.
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
</network-security-config>הפנו למשאב הזה מן המניפסט, ואז קראו את החריגות שכבר קיימות לפני ההפצה. תחום ניפוי באגים ישן או רשומת SDK של ספק תשלומים יכולים לאפשר בשקט שוב טקסט גלוי עבור המארח שניסיתם להגן עליו.
שני פרטי גרסה כדאי לדעת. אפליקציות שמכוונות ל-API 28 ואילך כבר מסרבות לטקסט גלוי כברירת מחדל, ולכן אפליקציה שעדיין מדברת HTTP פשוט או מכוונת לרמת API ישנה יותר או שיש לה חריגה במקום כלשהו. וכאשר קיימת תצורת אבטחת רשת, מאפיין המניפסט הישן יותר android:usesCleartextTraffic כבר לא מכריע בשאלה, ולכן שינוי הדגל הזה נראה לפעמים כאילו אינו עושה דבר.
נתיב שני: האפליקציה שאינכם שולטים בה
עבור אפליקציית צד שלישי מותקנת אינכם יכולים לשכתב את תצורת האבטחה שלה באמצעות הגדרת משתמש. האפשרויות המציאותיות הן בקרות החיבור המאובטח של המוצר עצמו, חלופה מתוחזקת שכבר משתמשת ב-TLS, או מדיניות מכשיר מנוהל שמגבילה מה המכשיר רשאי להריץ. אלו התחייבויות שונות, ורק השלישית מתרחבת לצי מכשירים.
כללי רשת הם תחליפים חלשים מכפי שהם נראים. התרת יציאה 443 בלבד אינה אוכפת TLS מאומת, מפני שפרוטוקולים רבים של טקסט גלוי יכולים לעבור ליציאה הזו, וחסימת יציאה 80 אינה מכסה טקסט גלוי ביציאות אחרות. גם מצב HTTPS בלבד של דפדפן מוגבל לאותו דפדפן, ולא לאפליקציית דואר או צאט נפרדת באותו טלפון.
התקנת תעודת יירוט כדי שהתעבורה תיראה מוצפנת היא שינוי אמון, לא תיקון. היא מחליפה את אימות השרת של האפליקציה בשלכם, ואפליקציה שלא אימתה תעודות מלכתחילה אינה מתוקנת בכך; רק העברתם את מי שיכול לקרוא את הזרם.
איפה VPN עוזר ואיפה לא
מנהרה היא הכלי הנכון כאשר הדרישה היא שהרשת המקומית לא תלמד דבר על החיבור, או שהאפליקציה תגיע לשירות פרטי שאליו אחרת אינה מגיעה. זה הכלי הלא נכון לגרום ללקוח HTTP לדבר HTTPS, מפני שהבקשה יוצאת מן המנהרה בדיוק כפי שנכנסה.
Android מוסיף מגבלה מעשית: הפלטפורמה מריצה שירות VPN אחד בכל רגע. אפליקציית סינון מקומית שתופסת את המקום הזה מתחרה במנהרה המרוחקת שבאמת נחוצה לכם, ומעבר ביניהן הוא פעולה ברמת המכשיר ולא ברמת האפליקציה. אותו מקום יחיד הוא סיבה טובה לשים את מדיניות המטען באפליקציה ולהשאיר את המנהרה לנתיב.
המקרה היחיד שבו השניים חופפים הוא שרת פרטי שאתם גם מפעילים. שם מנהרה יכולה להפוך את הנתיב לפרטי אף שהפרוטוקול עדיין טקסט גלוי, מה שמפחית חשיפה אך אינו משנה מהו המטען. התייחסו לכך כהפחתת סיכון, לא כהצפנת הנתונים.
מה לבדוק לפני שמכריזים שזה חסום
התחילו מברירת המחדל שמפתיעה אנשים: אפליקציה שמכוונת ל-API 28 ואילך אמורה כבר לסרב לטקסט גלוי, לכן בדקו את רמת היעד לפני שאתם מוסיפים תצורה שמשכפלת ברירת מחדל. אחר כך בדקו את מקרה הכישלון בכוונה: בקשה למארח HTTP פשוט חייבת להיכשל ולא להצליח בשקט דרך נפילת חסד.
adb shell dumpsys package com.example.app | grep -E 'targetSdk|versionName'
adb logcat | grep -i cleartextבמקרה ההצלחה, ודאו שקריאת ה-HTTPS מאמתת שרשרת תעודות אמיתית ושמתווך עם תעודה לא מהימנה נדחה ולא נסבל. לחיצת יד שמצליחה מול כל תעודה לא הוכיחה דבר על אבטחת התעבורה.
עבור אפליקציית צד שלישי, רשמו את הגבול שאתם באמת יכולים לאכוף, בין אם מדיניות מכשיר מנוהל, כלל רשת או כלום, ואמרו זאת בקריטריוני הקבלה. מדריך תצורת האבטחה של Android מפרט את ברירות המחדל של הפלטפורמה ואילו רכיבים מכבדים את המדיניות, וזה החלק שקובע אם התצורה שלכם עושה בכלל משהו.