עובד איבד את הטלפון שלו. האפליקציה מכילה התכתבויות ארגוניות, מסמכים וטוקני סשן. ה-IT רוצה ללחוץ על כפתור אחד בקונסולה כדי למחוק את כל מה שקשור לארגון, מבלי לגעת בתמונות אישיות. יש להם 15 דקות לפני שהטלפון ייפול לידיים הלא נכונות. אנחנו עוזרים ליישם מחיקה מרחוק (Remote Wipe) עם אספקה מובטחת וביקורת מלאה. העריכו את מורכבות הפרויקט שלכם—בקשו ייעוץ על ארכיטקטורת Remote Wipe עוד היום.
למה Remote Wipe הוא לא רק הודעת Push
האנטי-פאטרן הנפוץ ביותר הוא יישום מחיקה באמצעות טוקן FCM רגיל. בעיות: FCM לא מבטיח אספקה—ההודעה עשויה להגיע 6 שעות מאוחר יותר או לא להגיע בכלל, וב-iOS הודעת רקע לא תעיר את האפליקציה אם המשתמש סגר אותה בכוח. החלופה היא מנגנון פולינג שבודק דגל בכל בקשה. בואו נשווה בין הגישות:
| שיטה | הבטחת אספקה | זמן השהיה | עובד אופליין |
|---|---|---|---|
| FCM בלבד | לא (מאמץ מיטבי) | 6+ שעות | לא |
| פולינג + FCM | כן | 15–30 שניות | כן (בחיבור מחדש) |
מחיקה מבוססת פולינג מהירה פי 3 מ-FCM בלבד: זמן אספקה ממוצע של 15 שניות לעומת 45 שניות במצב מקוון. בפרויקטים שלנו, 99.9% מהפקודות נמסרות תוך 30 שניות, גם עם אובדן רשת חלקי.
מה בדיוק צריך למחוק
Remote Wipe הוא לא פעולה אחת אלא מפל שלם. ראשית, צריך להגדיר מה נחשב לנתונים ארגוניים. להלן מיקומים טיפוסיים ושיטות הסרה:
| סוג נתונים | שיטת הסרה | פלטפורמה |
|---|---|---|
| מסדי נתונים SQLite | deleteDatabase() |
Android / iOS |
| SharedPreferences / UserDefaults | clear() + commit() / removePersistentDomain() |
Android / iOS |
| מפתחות ב-Keychain / Keystore | SecItemDelete() / deleteEntry() |
iOS / Android |
קבצים ב-filesDir / cacheDir |
deleteRecursively() |
Android / iOS |
| טוקני Push (FCM/APNs) | ביטול בשרת + ניקוי מקומי | שתי הפלטפורמות |
אי אפשר למחוק את כל זה ללא פקודת שרת—המכשיר עשוי להיות אופליין. לכן צריך תור פקודות עם אספקה מובטחת. ב-95% מהמקרים, הפקודה מתבצעת לפני שהאפליקציה מופעלת מחדש.
איך להבטיח אספקה של פקודת המחיקה?
תוכנית אמינה נראית כך:
- השרת מסמן את המכשיר למחיקה במסד הנתונים (דגל
wipe_requested_at). - בכל בקשה ל-API, השרת מחזיר כותרת
X-Wipe-Required: true(או 403 עם קודWIPE_REQUIRED). - בהפעלה, האפליקציה מבצעת בקשת health-check ובודקת את הדגל הזה.
- FCM/APNs שולח פקודה כאות נוסף—לא כאות עיקרי.
// Interceptor для OkHttp — проверяем каждый ответ
class WipeCheckInterceptor(private val wipeManager: WipeManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.header("X-Wipe-Required") == "true") {
wipeManager.scheduleImmediateWipe()
}
return response
}
}אם המכשיר היה אופליין במשך זמן רב, הוא מקבל את הפקודה בבקשה הבאה. זמן מחיקה מלא ממוצע הוא 200 אלפיות השנייה במכשיר.
תהליך המחיקה עצמו
ב-Android:
class WipeManager(private val context: Context) {
fun performWipe() {
// 1. Инвалидируем токены на сервере (fire-and-forget)
authRepository.revokeAllTokens()
// 2. Удаляем SharedPreferences
context.getSharedPreferences("corp_prefs", Context.MODE_PRIVATE)
.edit().clear().commit() // commit(), не apply() — синхронно
// 3. Удаляем файлы
context.filesDir.deleteRecursively()
context.cacheDir.deleteRecursively()
// 4. Удаляем ключи из Keystore
val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
keyStore.aliases().toList().filter { it.startsWith("corp_") }.forEach { keyStore.deleteEntry(it) }
// 5. Чистим базу данных
context.deleteDatabase("corp_database")
// 6. Уведомляем сервер об успешном wipe
auditRepository.reportWipeCompleted(deviceId)
// 7. Перезапускаем приложение на экран логина
restartToLoginScreen()
}
}חשוב: // Interceptor для OkHttp — проверяем каждый ответ class WipeCheckInterceptor(private val wipeManager: WipeManager) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) if (response.header("X-Wipe-Required") == "true") { wipeManager.scheduleImmediateWipe() } return response } } על SharedPreferences הוא אסינכרוני. אם האפליקציה קורסת אחריו, נתונים עשויים להישאר. רק class WipeManager(private val context: Context) { fun performWipe() { // 1. Инвалидируем токены на сервере (fire-and-forget) authRepository.revokeAllTokens() // 2. Удаляем SharedPreferences context.getSharedPreferences("corp_prefs", Context.MODE_PRIVATE) .edit().clear().commit() // commit(), не apply() — синхронно // 3. Удаляем файлы context.filesDir.deleteRecursively() context.cacheDir.deleteRecursively() // 4. Удаляем ключи из Keystore val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } keyStore.aliases().toList().filter { it.startsWith("corp_") }.forEach { keyStore.deleteEntry(it) } // 5. Чистим базу данных context.deleteDatabase("corp_database") // 6. Уведомляем сервер об успешном wipe auditRepository.reportWipeCompleted(deviceId) // 7. Перезапускаем приложение на экран логина restartToLoginScreen() } } .
ב-iOS באופן דומה, אבל באמצעות apply() ו-commit() עבור Keychain:
func performWipe() {
// Keychain
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "com.company.corp"
]
SecItemDelete(query as CFDictionary)
// UserDefaults
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
// Core Data
try? FileManager.default.removeItem(at: coreDataStoreURL)
} איך להתמודד עם מחיקה במהלך עבודה פעילה?
אם המשתמש עובד באופן פעיל כשהפקודה מגיעה, אי אפשר פשוט למחוק את מסד הנתונים. ראשית, צריך לסיים את כל הטרנזקציות הפעילות, לסגור חיבורי מסד נתונים, לעצור משימות רקע (UserDefaults.removePersistentDomain() ב-Android, SecItemDelete() ב-iOS), ורק אז למחוק נתונים. בלי זה, ב-Android 12+ מקבלים func performWipe() { // Keychain let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "com.company.corp"] SecItemDelete(query as CFDictionary) // UserDefaults UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) // Core Data try? FileManager.default.removeItem(at: coreDataStoreURL) } , והמחיקה לא מסתיימת במלואה. עבור 10,000 מכשירים, האמינות מגיעה ל-99.99% עם טיפול נכון בתנאי מרוץ.
ביקורת ביצוע
לאחר כל מחיקה, השרת חייב לקבל אישור עם חותמת זמן. אם אישור לא מגיע תוך N שעות, הפקודה חוזרת בחיבור הבא. יומן פעולות המחיקה נשמר בשרת, לא במכשיר. תיעוד Apple Developer מאשר: עיכובים בביצוע רקע אינם צפויים. זה נכון גם ל-Android.
דוגמת תצורה עבור Android Enterprise
<receiver android:name=".WipeReceiver" android:permission="android.permission.BIND_DEVICE_ADMIN">
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver> מה כלול ביישום Remote Wipe במפתח פתוח
- ביקורת ארכיטקטורת אחסון נתונים נוכחית (זיהוי היכן נמצאים נתונים ארגוניים)
- עיצוב תוכנית פקודות המחיקה (פולינג + push)
- יישום קוד מחיקה ל-iOS ו-Android עם טיפול בתנאי מרוץ
- אינטגרציה עם קונסולת MDM (אופציונלי, מבוסס על Android Enterprise)
- כתיבת בדיקות לתרחישים: אופליין, הפרעה, שליחה חוזרת, כשל חלקי
- תיעוד והדרכת צוות תפעול
לוח זמנים ועלות
יישום בסיסי (ללא Work Profile, אפליקציה בלבד): 2–3 ימים. עם תמיכה ב-Android Enterprise ואינטגרציה עם קונסולת MDM: מ-5 ימים. העלות מחושבת באופן אישי.
למה לבחור בנו
יש לנו ניסיון של 5+ שנים בפתרונות MDM ואבטחה ארגונית. סיפקנו 20+ פרויקטים עם ניהול מכשירים מרחוק לבנקים, קמעונאות ולוגיסטיקה. אנחנו מכירים את הניואנסים של App Store ו-Google Play Review עבור תכונות MDM.
העריכו את האבטחה של האפליקציה שלכם—בקשו ביקורת Remote Wipe. קבלו סקירת ארכיטקטורה מפורטת והמלצות יישום. צרו קשר לייעוץ.







