פילפול סיסמאות
סטטוס אפיסטמי: די בטוח. אני לא קריפטוגרף, אבל אף אחד לא העלה התנגדויות חזקות וחשבתי על זה הרבה מאז שחשבתי על זה בפעם הראשונה._
כל מי שמדבר על פילפול סיסמאות (password peppering) מפספס שיטה טובה יותר ליישם אותו1.
בין אם אתם חושבים שפילפול סיסמאות הוא טוב או רע, יש ערך הלהכיר את השיטה טובה יותר. אם אתם חושבים שהוא טוב, זו שיטה טובה יותר להשתמש בהם. אם אתם חושבים שהוא רע, זה סטילמן לטעון כנגדו. הנקודה המרכזית של הפוסט הזה היא לא לתמוך בפילפול סיסמאות (למרות שאני כן חושב שהוא טוב), אלא להשוות את השיטות הנפוצות לשיטה חלופית.
מונחים ורקע, בקצרה
דלגו אם אתם מכירים
גיבוב (hash): אחסון סיסמאות מאובטח לא שומר סיסמאות, כך שאם מסד הנתונים דולף, הסיסמאות לא נחשפות. במקום זאת משתמשים בפונקציית גיבוב, שלוקחת כל סיסמה והופכת אותה למספר (גדול מאוד) שנראה אקראי — זה הגיבוב, וזה מה ששומרים במקום הסיסמה. גיבובים הם קשים להפיכה - כלומר אם מקבלים גיבוב, קשה לגלות מה היה הקלט המקורי - ובניגוד להצפנה, לגיבובים אין מפתחות לפענוח. כשהמשתמש מתחבר, הסיסמה מגובבת שוב ומושווית לגיבוב השמור. אם יש התאמה, נותנים למשתמש להיכנס לחשבון שלו.
מלח (salt): מחרוזת אקראית שמוסיפים לסיסמה לפני הגיבוב, ושבדרך כלל נשמרת בטקסט גלוי ליד הגיבוב הממולח (כך שכל מלח מקושר למשתמש). כשהמשתמש מתחבר, המלח שוב מגובב עם הסיסמה שלו ומושווה לגיבוב השמור. מלחים מונעים התקפות טבלת קשת, וגורמים לגיבובים של סיסמאות שמשותפות בין משתמשים להיות ייחודיים, כך שפורץ יצטרך לפצח כל סיסמה בנפרד. עם זאת, אם מסד הנתונים דולף והתוקף מכוון למשתמש ספציפי ומוכן לפצח את הסיסמה שלו בכוח גס, מלח לא יעצור אותו — והמשתמש יצטרך להסתמך על חוזק הסיסמה שלו.
פלפל (pepper): פלפל הוא מלח סודי שנשמר בלי קשר למשתמש (למשל בטבלה נפרדת במסד הנתונים2, או שלא נשמר בכלל). פילפול הוא אופציונלי — הוא לא נדרש למערכת מאובטחת. אם בוחרים להשתמש בו, תמיד צריך לעשות זאת בנוסף למלח, כי פילפול לבד לא מספק מספיק אבטחה. היתרון של פילפול סיסמאות הוא שהוא מקשה על פריצת סיסמאות ממולחות ומגובבות ספציפיות, גם במקרה של דליפה.
גישות נפוצות
יש שתי גישות עיקריות לפילפול, ושתיהן צריכות להיות בנוסף להמלחה.
- מייצרים ושומרים מספר ארוך מאוד אחד (128+ ביטים), ושומרים אותו בסוד. ואז בכל פעם שצריך לגבב סיסמה של משתמש, מוסיפים אותו לסיסמה לפני הגיבוב.
- יתרונות: אם לתוקפים אין את הפלפל, אי אפשר לפרוץ אף אחת מהסיסמאות.
- חסרונות: אם התוקפים מוצאים את הפלפל הוא הופך חסר תועלת, ואם הם יודעים את המלח ואת הסיסמה בטקסט גלוי של משתמש כלשהו (יכול להיות אפילו שלהם), ובאיזו פונקציית גיבוב נעשה שימוש, הם עלולים להיות יכולים להשתמש בזה כדי לפצח את הפלפל בכוח גס. סודיות היא הנחת אבטחה גרועה מאוד.
- מייצרים מחרוזת אקראית לכל סיסמה ומשתמשים בה כפלפל, אבל לא שומרים אותה בכלל — כך שבכל פעם שמשתמש מתחבר בעצם צריך לפצח בכוח גס את כל הפלפלים עד שמוצאים את הנכון. כדי שזה לא ייקח נצח, משתמשים במחרוזת קטנה, נניח תו או שניים.
- יתרונות: זה מאט את התוקפים פי X עבור כל סיסמה שהם מנסים לפצח, כי הם צריכים לנסות כל פלפל אפשרי על כל סיסמה אפשרית כדי לדחות אותה. מצד שני, באימות צריך לבדוק, בממוצע, 50% מהפלפלים כדי לאמת את הסיסמה.
- חסרונות: סביר שהתוקפים ידעו אילו פלפלים אפשריים, כי סביר שמדובר בתו או שניים מתוך קבוצת התווים המותרת, וכי המפרט הזה יהיה איפשהו בקוד שלכם — מה שהופך את הסוד לפגיע. סביר להניח שתשתמשו בפרוטוקול מוכן שמספק הפריימוורק שבו אתם משתמשים (וזה אכן מה שכדאי לעשות, במקום לכתוב מערכת אבטחה משלכם שכנראה תכיל באגים או טעויות שניתן לנצל).
אני חושב ששיטה מספר 1 עדיפה על שיטה מספר 2, אבל השיטה שלי עדיפה על שתיהן:
השיטה שלי היא שילוב של שתיהן:
שימו לב שאני לא אומר שאף אחד לא חשב על זה קודם, רק שלא ראיתי את זה.
מייצרים ושומרים כמה (נניח, ~100) מספרים ארוכים מאוד. כשמשתמש נרשם, בוחרים אחד מהם באקראי ומצרפים אותו לסיסמה שלו (בנוסף למלח) לפני הגיבוב. בכל פעם שהם מתחברים, מנסים כל אחד מהפלפלים השמורים עד שאחד עובד.
אם אלה המשתמשים והסיסמאות שלהם:
| משתמשים: | סיסמה |
| User1 | password |
| User2 | qwerty |
אז כך ייראה מסד הנתונים שלכם:
| פלפלים (הקסדצימלי): |
|---|
| d0f008977df53df5 |
| 61e71e00c6ca3e49 |
| 513c98b24377f966 |
| 7a0d272ec2b0c83a |
| ... |
| משתמשים: | מלח | סיסמה3 |
| User1 | 406d1d689f3b6026 | 274e6e7e99740e80dc0b8eb5e10252d02fe1a2e1afdddd6ccc4eac9d |
| User2 | c009b7d01c61bd19 | 68f2508b725b6350b4a7b4ac96569c3c96f8228d0398ce42a4af70c9 |
עבור User1 נבחר פלפל 3, אז גיבבנו ושמרנו את "406d1d689f3b6026password513c98b24377f966" (מלח+סיסמה+פלפל)4.
עבור User2 נבחר פלפל 1, אז גיבבנו ושמרנו את "c009b7d01c61bd19qwertyd0f008977df53df5".
שימו לב שבשום מקום במסד הנתונים לא שומרים איזה פלפלים משמשים איזה משתמשים.
כשUser1 מנסה להתחבר, אנחנו מקבלים את הסיסמה שהוא נותן ומצרפים אותה למלח שלו. עוברים על הפלפלים שלנו, ובכל פעם מצרפים אותם לסיסמה הממולחת, מגבבים את הכול, ומשווים לגיבוב השמור. אם מוצאים גיבוב תואם — מכניסים את המשתמש. אם עברנו על כל הפלפלים ולא מצאנו גיבוב תואם — לא מכניסים.
יתרונות על פני שיטה מספר 1:
- הפלפלים שלכם כבר לא קלים לפיצוח — גם אם פלפל אחד נפרץ בכוח גס, עדיין נשארים כל האחרים שלא ידועים.
- אם הפלפלים דולפים, לתוקפים עדיין לא ידוע איזה פלפל משמש איזה משתמש, ולכן הם עדיין מואטים בכמות הפלפלים שבחרתם לייצר (תזכורת: תוקפים צריכים לנסות את כל הפלפלים כי הם בעיקר צריכים לדחות סיסמאות; אתם צריכים לבדוק 50% מהפלפלים כי אתם בעיקר צריכים לאמת סיסמאות).
יתרונות על פני שיטה מספר 2:
- הפלפלים כמעט בלתי אפשריים לניחוש, ובכל זאת נותנים את היתרון של הרבה פלפלים שונים.
- זה הופך פריצה של סיסמה חלשה כמו '123' לבלתי אפשרית בלי לדעת מראש איזה פלפל שימש או אילו פלפלים לבדוק.
- האימות לא איטי יותר, כי הצירוף מהיר ואתם בוחרים בכמה פלפלים להשתמש.
- לא מאבדים הרבה אבטחה משמירת הפלפלים במסד הנתונים, כי הפלפלים האפשריים כבר היו די צפויים בשיטה הנפוצה.
- יש לכם שליטה טובה יותר על כמות הפלפלים האפשריים, וקל להוסיף עוד פלפלים בהדרגה עם הזמן.
זה מאובטח יותר משתי השיטות, עם מעט חסרונות ובקושי יותר מורכבות — ובכל זאת לא הצלחתי למצוא מקור שמתאר משהו דומה.
-
חיפשתי מקור שמזכיר שיטה כזו ולא מצאתי כלום. ↩
-
הרבה מקורות מדברים על אחסון הפלפל "מחוץ למסד הנתונים", וגרסה קודמת של המאמר הזה השתמשה גם היא בניסוח הזה (למרות שציינה שזה לא הכרחי). הסיבה היא שכפי שהמאמר מסביר, יישומים קונבנציונליים דורשים הנחת סודיות. למרות שאחסון "מחוץ למסד הנתונים" כן מקטין את הסבירות שהפלפל ידלוף אם מסד הנתונים דולף, להניח שאפשר לשמור על הפלפל בסוד אם לא מצליחים לשמור על הסיסמאות בסוד — זו הנחה גרועה ולא מאובטחת. השיטה שאני מציע עדיין נהנית מסודיות (אז אחסון "מחוץ למסד הנתונים" לא יזיק), אבל לא דורשת אותה. ↩
-
השתמשתי ב-SHA224 לגיבוב, רק כדוגמה. זו לא פונקציית גיבוב טובה לסיסמאות. ↩
-
סביר שזו לא בדיוק הדרך הנכונה לגבב את שלושת הערכים יחד, אבל זה לא משנה את ליבת השיטה. ↩