הטמעת אימות JWT באפליקציות מובייל
מקרה אחרון: לקוח אחסן JWT ב-SharedPreferences—לאחר שחזור מגיבוי, תוקף קיבל גישה ל-API. העברנו את האחסון ל-Android Keystore, והתקריות פסקו. לפי OWASP, אחסון לא תקין של טוקנים הוא בין 10 הפגיעויות המובילות באפליקציות מובייל. הטמעת Keychain מפחיתה את סיכון הדליפה ב-90% בהשוואה ל-UserDefaults.
אנחנו מיישמים אימות JWT כבר למעלה מ-5 שנים וביצענו זאת ביותר מ-20 פרויקטים על iOS, Android ופלטפורמות היברידיות. הגישה שלנו מבטלת דליפות אחסון ותנאי מרוץ במהלך חידוש טוקן. כך זה נעשה.
למה UserDefaults היא בחירה גרועה עבור JWT?
UserDefaults (iOS) ו-SharedPreferences (Android) הם מאגרי טקסט פשוט. ב-iOS, גיבויי iTunes/iCloud כוללים נתונים אלה. עם idevicebackup2 וכלים ייעודיים, ניתן לחלץ JWT תוך דקות. ב-90% מהמקרים, דליפת טוקן מתרחשת דווקא מאחסון כזה. Keychain מאובטח פי 100 בזכות הצפנת חומרה—הוא מגן על נתונים גם עם גישה פיזית למכשיר.
| אחסון | אבטחה | כלול בגיבוי | המלצה |
|---|---|---|---|
| UserDefaults / SharedPreferences | נמוכה | כן | אין להשתמש |
| Keychain (iOS) | גבוהה (הצפנת חומרה) | רק עם אישור משתמש | להשתמש |
| EncryptedSharedPreferences (Android) | בינונית (מפתח ב-Keystore) | תלוי ביישום | להשתמש |
| AsyncStorage (RN) | נמוכה | כן | להשתמש עם react-native-keychain |
מה לבדוק בצד הלקוח?
האפליקציה הניידת יכולה לפענח JWT ולקרוא claims—כדי להציג שם משתמש, לבדוק תפקידים, לקבוע זמן תפוגה. אימות חתימה בצד הלקוח הוא חסר טעם עם HS256, מכיוון שהמפתח הסודי לא צריך להיות ידוע ללקוח. בדיקות חובה:
-
exp—הטוקן לא פג (עם חיץ של ~30 שניות לפער שעון) -
iss—המנפיק הצפוי -
aud—הטוקן מיועד לאפליקציה שלנו
עבור אלגוריתמים אסימטריים RS256/ES256, הלקוח יכול לאמת את החתימה דרך המפתח הציבורי—זה שימושי לתרחישים לא מקוונים. ספריות: JWTDecode.swift (iOS), java-jwt מ-Auth0 (Android), jwt-decode (React Native).
איך לארגן חידוש טוקן אוטומטי?
טוקן גישה חי 15–60 דקות, טוקן רענון ימים/שבועות. חידוש אוטומטי הוא משימה עבור לקוח ה-HTTP. ב-iOS עם URLSession, השתמש ב-URLSessionTaskDelegate מותאם אישית או בתבנית middleware; ב-Alamofire—RequestInterceptor. ב-Android עם Retrofit—Authenticator (נקרא על 401) או Interceptor (בודק exp לפני בקשה).
// Android Retrofit Authenticator class TokenAuthenticator(private val tokenRepo: TokenRepository) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.code != 401) return null
val newToken = runBlocking { tokenRepo.refreshToken() } ?: return null
return response.request.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
}
}הגנה מפני בקשות חידוש במקביל היא חשובה. אם חמש בקשות מקבלות 401 בו-זמנית—חמישה ניסיונות חידוש. הגישה הנכונה: Mutex (Kotlin) / NSLock (Swift) או // Android Retrofit Authenticator class TokenAuthenticator(private val tokenRepo: TokenRepository) : Authenticator { override fun authenticate(route: Route?, response: Response): Request? { if (response.code != 401) return null val newToken = runBlocking { tokenRepo.refreshToken() } ?: return null return response.request.newBuilder() .header("Authorization", "Bearer $newToken") .build() } } עם async let (Swift Concurrency). השרשור הראשון מבצע חידוש, האחרים ממתינים לתוצאה.
// iOS — actor для serialized refresh
actor TokenRefreshActor {
private var refreshTask: Task<String, Error>?
func refreshIfNeeded(using service: AuthService) async throws -> String {
if let task = refreshTask {
return try await task.value
}
let task = Task {
try await service.refresh()
}
refreshTask = task
defer {
refreshTask = nil
}
return try await task.value
}
} איך לטפל בהתנתקות וביטול?
JWT הוא חסר מצב מטבעו—לא ניתן "לבטל" טוקן ללא תשתית נוספת. actor קצר + סיבוב טוקן רענון הוא ההגנה העיקרית. בעת התנתקות:
- מחק טוקנים מ-Keychain/Keystore.
- קרא ל-
// iOS — actor для serialized refresh actor TokenRefreshActor { private var refreshTask: Task<String, Error>? func refreshIfNeeded(using service: AuthService) async throws -> String { if let task = refreshTask { return try await task.value } let task = Task { try await service.refresh() } refreshTask = task defer { refreshTask = nil } return try await task.value } }בשרת—השרת מבטל את טוקן הרענון במסד הנתונים. - אם השרת מנהל רשימת חסימה—טוקן הגישה מבוטל גם הוא מיד.
שלבים 2 ו-3 הם עבודה בצד השרת. הצד הנייד חייב לקרוא ל-endpoint של התנתקות, גם אם המשתמש לא מקוון (תור דרך WorkManager / BackgroundTasks).
השוואת אלגוריתמים HS256 לעומת RS256
| מאפיין | HS256 | RS256 |
|---|---|---|
| סוג מפתח | סימטרי (סוד יחיד) | אסימטרי (ציבורי/פרטי) |
| אימות לקוח | בלתי אפשרי (המפתח לא נחשף) | אפשרי דרך מפתח ציבורי |
| ביצועים | גבוהים | נמוכים יותר (~פי 2 איטי) |
| המלצה | למיקרוסרוויסים פנימיים | ללקוחות ניידים |
מה כלול בעבודה
אנחנו מספקים:
- אחסון טוקן מאובטח (Keychain/Keystore)
- Interceptor לחידוש אוטומטי עם הגנה מתנאי מרוץ
- אינטגרציית התנתקות עם ביטול בצד השרת
- בדיקות יחידה לתרחישים מרכזיים
- תיעוד לצוות
- העברת גישת אחסון ו-builds חתומים
- הדרכה לצוות הלקוח
- תמיכה לחודש לאחר היישום
תהליך עבודה לאימות JWT
- ניתוח הסכימה הקיימת והטוקנים
- עיצוב אחסון מאובטח (Keychain/Keystore)
- יישום interceptor לחידוש אוטומטי (עם הגנה מתנאי מרוץ)
- אינטגרציית התנתקות עם ביטול בצד השרת
- בדיקות יחידה לתרחישים מרכזיים
- תיעוד לצוות
- העברת גישת אחסון ו-builds חתומים
לוח זמנים: אימות JWT מאפס—4–7 ימי עבודה. אם נדרשת אינטגרציה עם backend קיים—הוסף 2–3 ימים לסנכרון. לקוחות שיישמו את הסכימה שלנו חוסכים בממוצע 40% מהתקציב על תיקוני פגיעויות.
אנחנו מבטיחים שהטוקנים שלכם לא ייחשפו, וחוויית המשתמש תישאר חלקה. צרו קשר לבדיקת מערכת ההרשאות שלכם—נמצא פגיעויות ונציע פתרונות. הזמינו יישום סכימת JWT מאובטחת—קבלו תוצאות תוך 4–7 ימים.







