למה סיסמה אחת לא מספיקה
משתמש בבורסת קריפטו איבד גישה לחשבון שלו עקב SIM-swap — תוקף הנפיק מחדש את כרטיס ה-SIM ויירט קודי SMS. החשבון התרוקן תוך 15 דקות. נתקלנו בזה בפרויקט אחד: לקוח הפסיד 200,000 דולר כי אימות דו-שלבי היה מקושר ל-SMS. אחרי זה, הטמענו TOTP 2FA בכל אפליקציות הקריפטו שאנחנו מפתחים.
TOTP (סיסמה חד-פעמית מבוססת זמן, RFC 6238) הוא תקן שמיושם על ידי Google Authenticator, Authy, ורוב אפליקציות ה-2FA הארגוניות. עבור אפליקציית קריפטו, זו הדרישה המינימלית: קודי SMS מיורטים דרך SIM-swap, TOTP לא. אנחנו מבטיחים שהיישום שלנו מגן מפני התקפות אלה.
איך TOTP עובד
האלגוריתם: HMAC-SHA1 של הזמן הנוכחי (חלון של 30 שניות) וסוד משותף. הסוד המשותף נוצר במהלך הגדרת ה-2FA ונשמר גם בשרת וגם במכשיר המשתמש (באפליקציית האימות). אין שידור רשת במהלך כל אימות — רק בדיקה של הקוד המחושב.
בצד השרת, הבדיקה משתמשת בספריות פופולריות: pyotp (Python), otplib (Node.js), google-authenticator (Java). בצד הנייד, הקוד מוזן ידנית מאפליקציית האימות — אין צורך בלוגיקת TOTP נוספת. TOTP עובד פי 10 מהר יותר מהודעות push כי הוא לא דורש חיבור לאינטרנט.
למה TOTP בטוח יותר מ-SMS והודעות push?
| פרמטר | TOTP | SMS | הודעת push |
|---|---|---|---|
| תלות ברשת | לא | כן | כן |
| פגיעות לפישינג | נמוכה | בינונית | גבוהה |
| עלות למפתח | חינם | חיוב ספק | חינם |
| נוחות למשתמש | בינונית | גבוהה | גבוהה |
| אבטחה תחת SIM-swap | מוחלטת | אין | תלוי בתצוגה |
TOTP הוא תקן הזהב לאפליקציות פיננסיות. הודעות push יכולות להיות מיורטות על ידי תוכנות זדוניות במכשיר, SMS דרך SIM-swap. TOTP דורש גישה פיזית למכשיר עם אפליקציית האימות. OWASP ממליץ על TOTP להגנה על חשבונות קריטיים.
הטמעת 2FA בתהליך ההתחברות
הזרימה הקלאסית: שם משתמש/סיסמה → בדיקת שרת → אם למשתמש יש 2FA פעיל, השרת מחזיר טוקן ביניים → הנייד מציג מסך הזנת TOTP → השרת בודק את הקוד ומנפיק JWT מלא.
sealed class LoginState {
object Idle : LoginState()
object Loading : LoginState()
data class TwoFactorRequired(val tempToken: String) : LoginState()
data class Success(val authToken: String) : LoginState()
data class Error(val message: String) : LoginState()
}
class LoginViewModel(private val authRepository: AuthRepository) : ViewModel() {
val state = MutableStateFlow<LoginState>(LoginState.Idle)
fun submitTOTP(code: String, tempToken: String) {
viewModelScope.launch {
state.value = LoginState.Loading
authRepository.verifyTOTP(code, tempToken)
.onSuccess { token ->
state.value = LoginState.Success(token)
}
.onFailure { e ->
state.value = LoginState.Error(
if (e is InvalidCodeException) "Неверный код" else "Ошибка сервера"
)
}
}
}
} איך להגדיר 2FA: מדריך שלב-אחר-שלב
- המשתמש יוזם הגדרת 2FA בפרופיל.
- השרת יוצר סוד משותף ו-QR-URI
sealed class LoginState { object Idle : LoginState() object Loading : LoginState() data class TwoFactorRequired(val tempToken: String) : LoginState() data class Success(val authToken: String) : LoginState() data class Error(val message: String) : LoginState() } class LoginViewModel(private val authRepository: AuthRepository) : ViewModel() { val state = MutableStateFlow<LoginState>(LoginState.Idle) fun submitTOTP(code: String, tempToken: String) { viewModelScope.launch { state.value = LoginState.Loading authRepository.verifyTOTP(code, tempToken) .onSuccess { token -> state.value = LoginState.Success(token) } .onFailure { e -> state.value = LoginState.Error( if (e is InvalidCodeException) "Неверный код" else "Ошибка сервера" ) } } } }. - אפליקציית הנייד מציגה את קוד ה-QR (באנדרואיד —
otpauth://totp/..., ב-iOS —zxing-android-embedded) וסוד טקסטואלי להזנה ידנית. - המשתמש סורק את ה-QR עם Google Authenticator או Authy.
- לאימות, המשתמש מזין את הקוד הראשון שנוצר.
- השרת בודק את הקוד ומפעיל 2FA.
- לאחר ההפעלה, נוצרים 8-10 קודי גיבוי חד-פעמיים ומוצגים על המסך עם אפשרות להעתיק או להוריד.
קודי גיבוי: מה הם ולמה הם נחוצים?
ללא קודי גיבוי, 2FA באפליקציית קריפטו מסתכן באובדן גישה בלתי הפיך. אנחנו יוצרים 8-10 קודים חד-פעמיים במהלך הפעלת ה-2FA. השרת מאחסן אותם כהאשים של bcrypt. אפליקציית הנייד מציגה אותם פעם אחת עם אפשרות להעתיק או להוריד כקובץ טקסט.
יישום קודי גיבוי דורש ענף נפרד בזרימת ההתחברות: אם שדה הזנת TOTP קיים, אפשר לעבור ל"השתמש בקוד גיבוי". לאחר השימוש, הקוד מבוטל, והמשתמש מקבל אזהרה על המספר שנותר.
| שיטת אחסון | נגישות | אבטחה |
|---|---|---|
| נייר | גבוהה | נמוכה (אובדן) |
| מנהל סיסמאות | גבוהה | גבוהה |
| צילום מסך במכשיר | בינונית | בינונית |
מידע נוסף על הגנת מסך הזנת הקוד
באנדרואיד, `FLAG_SECURE` משמש לחסימת צילומי מסך. ב-iOS, התוכן מוסתר בעת מעבר בין אפליקציות דרך `applicationWillResignActive`. שדה הזנת הקוד צריך להיות `isSecureTextEntry = true` כדי למנוע השלמה אוטומטית ודליפה דרך מקלדות צד שלישי.מה כלול בעבודה
- שרת: בדיקת TOTP, יצירת QR-URI, אחסון קודי גיבוי (bcrypt) ואחסון סודות (מוצפן), API להגדרה ואיפוס של 2FA.
- נייד: מסך הזנת TOTP, הדרכה עם QR, הגנה מפני צילומי מסך, הצגת קודי גיבוי.
- תיעוד: תיאור זרימה, סכמת אבטחה, הוראות משתמש.
- CI/CD: חתימה אוטומטית והפצה דרך TestFlight / Firebase App Distribution.
- תמיכה: שבועיים לאחר ההשקה, תיקוני באגים.
העלות של פרצה אחת לחברת קריפטו יכולה להגיע ל-100,000 דולר, אז השקעה ב-2FA מוצדקת. החיסכון מיישום TOTP במקום SMS מסתכם בכ-15,000 דולר לשנה לכל 10,000 משתמשים.
לוחות זמנים ועלות
שילוב TOTP 2FA (הגדרה, זרימת התחברות, קודי גיבוי, הגנת מסך) — 1-2 שבועות. העלות מחושבת באופן אישי, בהתאם לארכיטקטורה הנוכחית ולשינויים הנדרשים בשרת. הערכנו 15+ פרויקטים עם נושא קריפטו — צרו קשר, בואו נדון במקרה שלכם.
אנחנו צוות פיתוח נייד עם ניסיון של שנים. הטמענו 2FA לעשרות בורסות קריפטו וארנקים. אנחנו מבטיחים עמידה בהנחיות App Store Review (סעיף 4.2/5.1) ובמדיניות Google Play. צרו קשר לייעוץ וביקורת טכנית של מערכת ההתחברות הנוכחית שלכם. הזמינו יישום TOTP היום — הגנו על נכסי המשתמשים מהתקפות SIM-swap.







