למה קוד חד־פעמי תקף עדיין נכשל בכניסה ל־VPN
קוד שנדחה הוא בדרך כלל בעיה בסדר הבקשה, ברישום או בתזמון, ולא קוד שגוי. בדקו את אלה לפי הסדר לפני שמבצעים רישום מחדש.
אתם פותחים את לקוח ה־VPN, מקלידים את הסיסמה, ושש הספרות מהמאמת חוזרות כלא תקפות — פעמיים, ואז בפעם השלישית. שום דבר לא השתנה בחשבון מאתמול, ולכן האינסטינקט הוא למחוק את הרשומה במאמת ולסרוק אותה שוב — בדרך כלל התיקון הלא נכון. קוד חד־פעמי אינו סיסמה שנייה: השער חייב לבקש אותו בשלב מסוים, מרשומה רשומה מסוימת, בתוך חלון מסוים, ורוב הדחיות מתרחשות לפני שהספרות מושוות. בררו באיזה שלב אתם נמצאים לפני שנוגעים ברישום.
סדר הבקשות אומר לכם באיזה זרימה אתם
שלוש צורות מכסות כמעט כל כניסה ל־VPN שמערבת קוד. בצורה הדו־שלבית הלקוח שולח את הסיסמה, השרת מוציא אתגר, והלקוח שואל שוב. בצורה המשולבת השער רוצה סיסמה וקוד בשליחה אחת — לפעמים מחוברים, לפעמים אחרי מפריד שרק המפעיל מתעד. בצורת הדפדפן ללקוח אין שדה כלל: הקוד נכנס לדף אינטרנט, או שאישור דחיפה מחליף אותו, ולקוח שולחן העבודה ממתין לתוצאה.
צורה שגויה נראית כמו קוד רע. הדביקו קוד טרי בשדה שמיועד לסיסמה בלבד והשרת יקרא אותו כחלק מהסיסמה. בצד הלקוח, OpenVPN 2.4 ואילך הופכים את ההבדל לגלוי: שרת שמשיב עם AUTH_FAILED,CRV1: מצפה לתגובת אתגר, ולכן הפרופיל זקוק ל־static-challenge או לקובץ סיסמה בצורת SCRV1:. פרופיל ישן יותר שנושא רק auth-user-pass מאמת את הסיסמה ואז נתקע, מה שנראה ביומן כמו כשל OTP למרות שלא נשלח קוד.
כדאי להזכיר את WireGuard כיוצא מן הכלל: הוא מאמת זוגות מפתחות ולא אנשים, ולכן אין שדה לקוד. אם ספק מפרסם כזה, משהו מחוץ למנהרה מקבל אותו.
מה השרת עושה עם שש הספרות
קודים מבוססי זמן פועלים לפי RFC 6238, שעוטף את המבנה הישן מבוסס המונה מ־RFC 4226: HMAC מעל גורם נע, נחתך כברירת מחדל לשש ספרות. ב־TOTP הגורם הנע הוא זמן ה־Unix הנוכחי חלקי צעד, 30 שניות אלא אם המפעיל שינה אותו. המאמת אף פעם לא משווה את הספרות שלכם לערך שמור; הוא מחשב מחדש את הקוד הצפוי מהזרע המשותף ובדרך כלל מקבל גם את הצעדים השכנים, כך שהלוך־חזור איטי לא מכשיל את הכניסה.
שתי השלכות מוחמצות. אזור הזמן של הטלפון אינו רלוונטי, כי הזרע לא רואה שעון מקומי — רק הסטייה מ־UTC אמיתי חשובה, ומכשיר שמקדים בשתי דקות נכשל בכל קוד בזמן שהוא מציג ספירה לאחור נקייה. וקוד הוא חד־פעמי מלכתחילה: המאמת זוכר את ערך המונה האחרון שקיבל עבור אותו זרע, ולכן אותן ספרות שנשלחות שוב נדחות אפילו באותו חלון. לקוח שמנסה שוב אחרי פסק זמן עלול לשרוף את הקוד שזה עתה שלח.
אסימונים מבוססי מונה מזיזים את המונה בכל לחיצה, ולכן הם יוצאים מסנכרון אחרי שנוצרו קודים שמעולם לא נשלחו; התיקון המקובל הוא חלון סנכרון מחדש שמקבל כמה ערכים קדימה. אם המפעיל רשם HOTP והאפליקציה שלכם מציגה TOTP, כל ערך שגוי באופן שנראה כמו בעיית שעון. כתובת ה־URI של ההקצאה שמאחורי קוד ה־QR מפרטת את הסוג, הספרות, התקופה וה־hash.
כשלים שנראים כמו קוד שגוי
הרשומה הרשומה היא האשמה הנפוצה ביותר. אפליקציות אימות מתייגות רשומות לפי מנפיק וחשבון, ולכן מי שמחזיק שתי זהויות על אותו VPN עלול להגיע לשתי רשומות שהתוויות שלהן נבדלות בתו אחד. קוד תקף עבור הזרע שיצר אותו ולא לשום דבר אחר. השוו את התווית בטלפון לשם המשתמש שהשרת רושם, לא למה שאתם זוכרים שהקלדתם.
שנית, מערכת פרמטרים שאינה תואמת. זרע שנרשם עם שמונה ספרות, תקופה של 60 שניות או SHA-256 מפיק קודים שמאמת שמצפה לשש ספרות, 30 שניות ו־SHA-1 דוחה, ובצד הלקוח אי אפשר להבחין בכך מקוד שגוי. זה מופיע אחרי העברות שבהן הרשומה נוספה מחדש מתצלום של קוד ה־QR, או שבהן רשומת האסימון נבנתה מחדש עם ברירות מחדל.
שלישית, זה לא קשור לקוד. שערים מחזירים בדרך כלל כשל אימות גנרי אחד עבור סיסמה שגויה, קוד שפג תוקפו וחשבון מושבת, בכוונה, כדי שאיש לא יוכל לבדוק איזה גורם נכשל. קראו את יומן השער או RADIUS במקום את הבאנר של הלקוח: אתגר שמעולם לא הוצא, דחיית גישה בלי מאפיין OTP ודחיית מדיניות נראים אותו דבר על המסך ומובילים לצעדים הבאים שונים.
רצף קצר שמצמצם את זה
התחילו בבדיקה הזולה ביותר ועצרו בשלב הראשון שאינו מחזיק.
- המתינו לקוד טרי, שלחו אותו פעם אחת, וציינו אם האתגר מופיע לפני או אחרי קבלת הסיסמה.
- התאימו את התווית במאמת לשם המשתמש שהשרת שומר.
- ודאו שהפרופיל בכלל יכול להשיב לאתגר, כמו במקרה
CRV1לעיל. - השוו את שעון המכשיר למקור זמן מוכר; סטייה של יותר מכמה עשרות שניות שוברת את TOTP בפני עצמה.
- שאלו את המפעיל אם האסימון הוא TOTP או HOTP, ואילו ספרות, תקופה ו־hash נרשמו.
שני כללים מונעים מזה להחמיר את המצב. אל תנסו שוב פעמים רבות: שערים רבים סופרים כשלים לפי חשבון או נועלים אותו או מתחילים לעכב תגובות, ולכן פרץ הופך תצורה שגויה קטנה לכרטיס תמיכה. ואל תרשמו מחדש את האסימון כבדיקה אבחנתית, כי זה מבטל את הזרע הישן; אם התקלה האמיתית הייתה אתגר שהלקוח מעולם לא שלח, כעת יש לכם שני שינויים להסביר במקום אחד.
הרחיקו את הזרע ואת קודי השחזור מהמחזור
שש הספרות והסוד שמאחוריהן אינם ניתנים להחלפה. קוד QR להקצאה או זרע Base32 מייצר כל קוד עתידי של החשבון, ולכן הוא שייך לאותה קטגוריה של הסיסמה: לא בצילום מסך, לא בהערה בכרטיס, ולא בצ'אט עם מי שפנה אליכם ראשון. קודי שחזור הם מנגנון נפרד — חד־פעמיים, לעתים קרובות מדלגים לגמרי על בקשת הקוד, ובזבוז אחד מהם לבדיקת החשבון מסיר מסלול חלופי שאולי תזדקקו לו בהמשך.
קוד גם לא הופך דף כניסה לאמין: בקשת OTP בדומיין דומה אוספת קוד תקף ומשמיעה אותו שוב בתוך החלון, והתעודה היא עדיין מה שאומר לכם היכן אתם מקלידים.
אם הרצף מגיע לפרופיל שלא יכול להשיב לאתגר, תקנו את הפרופיל ונסו שוב עם קוד טרי אחד. אם הוא מגיע לפרמטרי רישום לא תואמים, בקשו מהמפעיל לקרוא את רשומת האסימון לפני שמישהו סורק משהו. אם הוא מגיע לנעילה, המתינו שתיגמר במקום להחליף לקוחות — מצב החשבון הוא החסם, לא האפליקציה.