מחולל טוקנים אקראיים
מפיק סודות ממחולל האקראיות הקריפטוגרפי של הדפדפן — hex, base64url או אלפבית משלכם, ומשפטי-סיסמה של EFF — עם אנטרופיה בביטים.
192.0 ביטיםזמן ממוצע למציאה, בשלושה קצבי ניחוש. הפער ביניהם גדול מכל הבדל שהסוד עצמו עושה.
- מול התחברות שמגבילה קצב — 10 ניחושים לשנייה
10^49 שנים - מול hash איטי שדלף (bcrypt, argon2) — 10⁴ לשנייה
10^46 שנים - מול hash מהיר שדלף (SHA-256, MD5) — 10¹² לשנייה
10^38 שנים
שליפה מתוך 64 תווים, ולכן כל אחד נושא 6.00 ביטים.
מה הכלי הזה עושה
הוא מפיק סודות: ערכים שכל הגנתם היא שאיש אינו יכול לנחש אותם. בחרו אלפבית ואורך למפתח API או לסיסמת בסיס נתונים, או עברו למשפט-סיסמה שנשלף ממילון מפורסם עבור משהו שאדם צריך להקליד. כל תוצאה מופקת בדפדפן שלכם ומוצגת עם המספר האחד שמתאר את חוזקה בכנות.
זו עבודה אחרת ממחולל ה-UUID שבאתר הזה. UUID חייב להיות ייחודי, כדי ששתי רשומות לעולם לא יתנגשו; סוד חייב להיות בלתי ניתן לניחוש, וזו דרישה חזקה יותר וכשל אחר. מזהה צפוי הוא בלתי מזיק. סוד צפוי הוא דלת פתוחה.
מאיפה מגיעה האקראיות
הכול כאן נשלף מ-crypto.getRandomValues, מחולל האקראיות המאובטח קריפטוגרפית של הדפדפן, שמזורע ממאגר האנטרופיה של מערכת ההפעלה — אותו מקור שממנו שואבים openssl rand ו-/dev/urandom.
החלופה, Math.random, אינה פונקציית אבטחה ומעולם לא טענה להיות כזו. זהו מחולל פסאודו-אקראי מהיר עם מצב פנימי קטן, ובכל מנוע עדכני אפשר לשחזר את המצב הזה ממספר קטן של פלטים, ומאותו רגע כל ערך עבר ועתיד ידוע. טוקן שנבנה ממנו נראה אקראי בדיוק כמו אמיתי; ההבדל מתגלה רק כשמישהו טורח להסתכל.
שאלה סבירה היא האם דף אינטרנט הוא בכלל המקום הנכון להפיק סוד ייצור. ההפקה עצמה תקינה: זהו ה-CSPRNG של הפלטפורמה, הדף סטטי, ושום דבר אינו משודר. החלקים שכדאי לחשוב עליהם הם אלה שסביבו — סוד שעובר דרך הלוח עשוי להיות קריא ליישומים אחרים, וסוד שמודבק למסוף בדרך כלל נוחת בהיסטוריית המעטפת. אלה בדיוק אותם שיקולים כמו בכל שיטה אחרת.
ההטיה שמסתתרת במימוש המתבקש
הפיכת בייטים אקראיים לתווים נראית כמו שורה אחת: קחו בייט, קחו אותו מודולו גודל האלפבית, פנו לאלפבית באינדקס הזה. זה שגוי בעדינות לכל אלפבית שגודלו אינו מחלק את 256 בשלמות, והפלט אינו נותן לכך שום סימן.
עם 62 תווים אלפאנומריים, 256 אינו מתחלק בשלמות: 62 נכנס ל-256 ארבע פעמים ונשארים 8. שמונת ערכי הבייט שנשארו — 248 עד 255 — מתקפלים חזרה על שמונת התווים הראשונים של האלפבית, ולכן כל אחד מהם מקבל חמישה סיכויים מתוך 256 בעוד השאר מקבלים ארבעה. שמונת התווים הראשונים סבירים בכ-25% יותר מהאחרים.
התיקון הוא דגימת דחייה: השליכו שליפה שנחתה בזנב שנשאר ושלפו שוב, במקום לקפל אותה חזרה פנימה. זה עולה בערך שליפה נוספת אחת מכל שלושים ושתיים לאלפבית של 62 תווים, ולא עולה דבר ל-hex או ל-base64url, שגודליהם חזקות שתיים ולכן אין להם זנב כלל. הכלי הזה דוחה; האפקט אינו נראה באף טוקן בודד, וזו בדיוק הסיבה שכדאי לומר אותו.
אנטרופיה, שהיא המדד הכן היחיד
אנטרופיה בביטים אומרת כמה גדול מרחב הסודות שסבירותם שווה: n ביטים פירושם 2^n אפשרויות. היא חיבורית וקלה להשוואה, והיא אינה אומרת דבר על כמה מהר מישהו יכול לסרוק את המרחב הזה — וזו תכונה, מפני שהחלק ההוא תלוי בדברים שמחולל אינו יכול לדעת.
החשבון פשוט במכוון. כל תו תורם log2(גודל האלפבית) ביטים, וכל מילה תורמת log2(גודל המילון), ולכן:
- תו hex הוא 4 ביטים, ולכן טוקן hex בן 32 תווים הוא בדיוק 128 ביטים — מפתח AES-128 כתוב במלואו.
- תו base64url הוא 6 ביטים, ולכן 22 תווים חוצים את 128 הביטים ו-43 חוצים את 256.
- תו אלפאנומרי הוא כ-5.95 ביטים; החרגת הדומים מורידה את האלפבית ל-58 ואת התו ל-5.86, מה שעולה בערך תו אחד של אורך על כל שנים-עשר.
- מילה מהרשימה הארוכה של EFF היא 12.925 ביטים, מפני שברשימה 7,776 ערכים, שהם 6^5 — חמש הטלות קובייה.
- מילה מהרשימה הקצרה היא 10.34 ביטים, מתוך 1,296 ערכים, שהם 6^4.
יעד מקובל הוא 128 ביטים, ששם כוח גס מפסיק להיות אסטרטגיה ולא רק יקר. זה 32 תווי hex, 22 תווי base64url, או 10 מילים מהרשימה הארוכה של EFF.
למה אין «זמן פיצוח» אחד
לסוד אין זמן פיצוח. לזוג של הסוד ושל מה שמגן עליו יש כזה, והשומר משנה הרבה יותר מהסוד. אותו ערך עצמו שהיה לוקח יותר מכל קיום היקום למצוא דרך טופס התחברות נופל בתוך אחר צהריים אם הוא נשמר כ-SHA-256 בלי מלח ובסיס הנתונים דלף.
ולכן מוצגים שלושה קצבים במקום מספר אחד, וכל אחד נקוב בשמו:
- עשרה ניחושים לשנייה, מול התחברות שמגבילה קצב. זו התקרה הריאלית לתוקף שחייב לעבור דרך השירות שלכם.
- עשרת אלפים ניחושים לשנייה, מול hash סיסמאות שדלף ובנוי להיות איטי — bcrypt בעלות עדכנית, או argon2. האיטיות היא כל התכלית של הפונקציות האלה.
- טריליון ניחושים לשנייה, מול hash שדלף ומעולם לא נועד לסיסמאות. SHA-256 ו-MD5 תוכננו להיות מהירים, ומערך של כרטיסי מסך מהיר מאוד אכן.
הקצבים מעוגלים לחזקות של עשר במכוון. כל דבר מדויק יותר היה מרמז על מדידה של תוקף מסוים אחד, בעוד המידע השימושי הוא אחד-עשר סדרי הגודל שבין השורה הראשונה לאחרונה. אם סוד נוח בכל שלוש השורות, השאלה סגורה בלי צורך במספרים המדויקים.
כל מספר הוא הממוצע, שהוא חצי מהמרחב ולא כולו — חיפוש מוצא את התשובה באמצע הדרך בממוצע. ומעבר למיליון שנה התשובה ניתנת כסדר גודל, מפני ש-«ארבע מאות טריליון שנה» אינו משך שמישהו יכול להשוות. גיל היקום הוא כ-10^10 שנים, וזה עוגן שימושי לשורות שמגיעות רחוק כל כך.
משפטי-סיסמה, ומאיפה החוזק שלהם באמת מגיע
משפט-סיסמה חזק מסיבה אחת בדיוק: כמה מילים נבחרו באקראי מתוך רשימה גדולה. הוא אינו חזק מפני שהוא ארוך, והוא אינו חזק מפני שהוא נראה כמו שפה. שש מילים אקראיות מרשימה של 7,776 מילים הן כ-77 ביטים; שש מילים שאדם חשב עליהן שוות הרבה פחות, מפני שאנשים אינם בוחרים באופן אחיד ותוקף יודע על ביטויים נפוצים בדיוק את מה שאתם יודעים.
שתי הרשימות כאן מגיעות מ-EFF והן רשימות ה-diceware המפורסמות, שהורדו ללא שינוי. הן אינן רשימות שהורכבו כאן, וזה משנה: לרשימת מילים שמישהו המציא יש גודל לא ידוע, חפיפה לא ידועה עם רשימות אחרות, ואין שום דרך לבדוק את טענת האנטרופיה שנטענת עליה.
- ברשימה הארוכה 7,776 מילים, אחת לכל הטלה של חמש קוביות, והיא נותנת 12.925 ביטים למילה.
- ברשימה הקצרה 1,296 מילים, אחת לכל הטלה של ארבע קוביות, והיא נותנת 10.34 ביטים למילה. מילותיה קצרות יותר — אף אחת אינה מעל חמש אותיות — מה שמקל על ההקלדה במחיר של יותר מילים לאותו חוזק.
מילה חוזרת במשפט-סיסמה אינה פגם ואינה מוגרלת מחדש כאן. כל שליפה בלתי תלויה, ולכן כל זוג מילים מסוים סביר בדיוק כמו כל אחר; סירוב לחזרות היה מכווץ את מרחב משפטי-הסיסמה האפשריים ועושה אותם מעט חלשים יותר, לא חזקים יותר.
בשתי רשימות ה-EFF יש קומץ ערכים מקופפים — t-shirt, yo-yo, drop-down, felt-tip. אם המפריד הוא גם מקף, משפט-סיסמה שמכיל אחד מהם אינו ניתן לפירוק חד-משמעי חזרה למילותיו. זו בעיית תצוגה ולא בעיית אבטחה, ובחירת רווח או נקודה כמפריד מונעת אותה לגמרי.
האפשרויות שעולות באנטרופיה, ואחת שלא
החרגת התווים הדומים מחליפה חוזק בקריאוּת, וההחלפה גלויה. הסרת 0, O, I ו-l מורידה את האלפבית האלפאנומרי מ-62 תווים ל-58 — שהם, בדיוק, אלפבית ה-base58 שבו משתמש ביטקוין, ומאותה סיבה. כל תו יורד מ-5.95 ל-5.86 ביטים, ולכן טוקן זקוק לכתו נוסף בערך על כל שנים-עשר כדי להישאר חזק באותה מידה. בדרך כלל זה שווה את זה לכל דבר שאדם יקרא ממסך ויקליד במקום אחר.
האפשרות מוצעת רק במקום שבו יש לה משמעות. Hex ו-base64url הם קידודים, לא קבוצות תווים: האלפבית שלהם קבוע במפרט, ו-hex בלי 0 אינו hex עוד — שום דבר לא יוכל לפענח אותו.
אות גדולה בתחילת כל מילה במשפט-סיסמה אינה מוסיפה דבר, ומספר החוזק כאן במכוון אינו זז כשמדליקים אותה. זו אותה טרנספורמציה שמופעלת בכל פעם, ולכן היא אינה יוצרת ולו אפשרות חדשה אחת: תוקף שיודע שהמשפט מנוקד באות גדולה נמצא בדיוק במקום שממנו התחיל. האפשרות קיימת מפני ששדות סיסמה עדיין דורשים אות גדולה, לא מפני שהיא עוזרת.
בחירת אורך
לכל דבר שמכונה מטפלת בו — מפתחות API, טוקני הפעלה, סודות webhook, סיסמאות בסיס נתונים — אין שום סיבה לחסוך. 32 תווי hex או 22 תווי base64url נותנים 128 ביטים, וללכת רחוק יותר עולה רק בבייטים בקובץ תצורה.
לכל דבר שאדם מקליד, האילוץ שונה ומשפט-סיסמה הוא בדרך כלל הצורה הטובה יותר. שש מילים מהרשימה הארוכה חזקות מסיסמה אקראית בת שנים-עשר תווים וקל בהרבה לקלוע בהן בניסיון הראשון, וזה משנה יותר משנדמה: סוד שאנשים מקלידים לא נכון הוא סוד שאנשים רושמים על נייר.
לקוד מספרי הקריאה הכנה היא ש-ארבע ספרות הן 13 ביטים ואפשר למצות אותן בשניות בכל דבר שאינו מגביל קצב. קוד PIN בטוח אך ורק בזכות מדיניות הנעילה שסביבו, לעולם לא בזכות חוזקו שלו.
שאלות נפוצות
- כמה ארוך צריך להיות מפתח API?
- כוונו ל-128 ביטים של אנטרופיה או יותר, ששם כוח גס מפסיק להיות אסטרטגיה. זה 32 תווי hex, 22 תווי base64url, או 43 תווי base64url אם אתם רוצים 256 ביטים. לערך שרק תוכנה מטפלת בו, האפשרות הארוכה יותר אינה עולה דבר.
- האם זה בטוח יותר מהפקת סוד עם Math.random?
- כן, וההבדל אינו עניין של מידה. Math.random הוא מחולל פסאודו-אקראי עם מצב קטן שמנועים עדכניים מאפשרים לשחזר ממספר קטן של פלטים, ומאותו רגע כל ערך שהוא אי פעם יפיק ידוע. הכלי הזה משתמש ב-crypto.getRandomValues, ה-CSPRNG של הפלטפורמה, שהוא אותו מקור שממנו שואב openssl rand.
- למה אתם לא מציגים «זמן פיצוח» אחד?
- מפני שאין מספר כזה. אותו סוד עצמו בלתי נגיש מאחורי התחברות שמגבילה קצב ונופל מהר מאחורי hash מהיר שדלף — אחד-עשר סדרי גודל זה מזה. מספר יחיד חייב לבחור הנחה אחת ואז להסתיר אותה, וקוראים זוכרים את המספר ולא את ההנחה. שלושה קצבים נקובים שמים את ההנחה במקום שאפשר לראות.
- האם משפטי-סיסמה חלשים ממחרוזות אקראיות?
- לא — החוזק תלוי רק בכמה מילים נשלפות מתוך רשימה בת כמה, ושניהם מוצגים. שש מילים מהרשימה הארוכה של EFF הן כ-77 ביטים, יותר מסיסמה אקראית בת עשרה תווים. מה שמחליש משפט-סיסמה הוא בחירת המילים בעצמכם, ואלה אינן כאלה.
- האם כדאי לי להחריג את התווים הדומים?
- אם אדם יקרא את הסוד ממסך ויקליד אותו במקום אחר, כן: המחיר קטן וטוקן שנקרא לא נכון הוא פנייה לתמיכה. אם רק תוכנה תטפל בו, אין שום סיבה. הסרת 0, O, I ו-l מורידה את האלפבית מ-62 תווים ל-58, שהם בדיוק אלפבית ה-base58, ועולה בערך תו אחד של אורך על כל שנים-עשר.
- האם אות גדולה במשפט-סיסמה עושה אותו חזק יותר?
- לא. זה אותו שינוי שמופעל בכל פעם, ולכן הוא אינו מוסיף ולו אפשרות חדשה אחת למרחב משפטי-הסיסמה. מספר האנטרופיה כאן נשאר במקומו כשמדליקים אותה, וזו ההתנהגות המדויקת. האפשרות קיימת מפני שיש שדות סיסמה שמתעקשים על אות גדולה.
- האם משהו שאני מפיק נשלח לשרת?
- לא. ההפקה רצה כולה בדפדפן שלכם בעזרת מחולל האקראיות של הפלטפורמה, שום דבר אינו מועלה או נרשם, וזה עובד בלי חיבור רשת. הטוקנים אינם נשמרים בשום מקום — טעינה מחדש של הדף מפיקה חדשים והישנים נעלמו.