בונים מחולל סיסמאות שבאמת בטוח לשימוש - ולא רק נראה ככה¶
כמעט כל מתחיל בונה בשלב מסוים מחולל סיסמאות - זה תרגיל קלאסי עם מחרוזות ולולאות. הבעיה היא שרוב המדריכים ברשת משתמשים במודול הלא נכון לגמרי, ובונים משהו שנראה כמו סיסמה אקראית אבל בפועל אפשר לנחש. הפרויקט הזה הוא הזדמנות ללמוד לא רק לכתוב קוד שעובד, אלא קוד שגם באמת עושה את מה שהוא אמור לעשות - וזה הבדל שקיים בדיוק בעולם אבטחת המידע.
למה random הוא בחירה גרועה לסיסמאות¶
מודול ה-random הרגיל בפייתון מיועד לסימולציות, משחקים והגרלות - לא לאבטחה. הוא משתמש באלגוריתם שנקרא Mersenne Twister, שהוא "פסאודו-אקראי": אם תוקף מצליח לראות מספיק פלטים שלו, הוא יכול תיאורטית לחזות את הפלטים הבאים. זה בסדר גמור אם אתם מגרילים מספר בין 1 ל-100 במשחק, אבל אסון אם אתם מייצרים סיסמה שאמורה להגן על חשבון בנק.
לכן פייתון כולל מודול נפרד בדיוק בשביל זה - secrets. הוא נבנה במיוחד למטרות קריפטוגרפיות ומשתמש במקור אקראיות שמערכת ההפעלה מספקת, שהרבה יותר קשה לחזות. הכלל הפשוט: כל פעם שאתם מייצרים משהו שקשור לאבטחה - סיסמה, טוקן, מפתח - משתמשים ב-secrets, לא ב-random. זו נקודה שאם תזכרו רק אותה מהפרויקט הזה, כבר היה שווה.
שלב ראשון - בונים גרסה בסיסית שעובדת¶
הרעיון הבסיסי: בוחרים אורך רצוי, מגדירים מאיזה תווים אפשר לבחור (אותיות קטנות, גדולות, ספרות, סימנים), ובוחרים תווים אקראיים מתוך הקבוצה הזו:
import secrets
import string
def generate_password(length=16):
chars = string.ascii_letters + string.digits + string.punctuation
return "".join(secrets.choice(chars) for _ in range(length))
זו כבר סיסמה בטוחה יחסית, אבל שווה להוסיף לוגיקה שמוודאת שהסיסמה כוללת לפחות תו אחד מכל קטגוריה (אות גדולה, ספרה, סימן) - כי לפעמים בחירה אקראית לחלוטין יכולה "במקרה" לצאת רק אותיות קטנות.
שלב שני - הופכים את זה לכלי שימושי עם argparse¶
מחולל סיסמאות שרץ רק עם ערכים קבועים בקוד הוא נחמד, אבל כלי אמיתי נותן למשתמש לשלוט בפרמטרים דרך שורת הפקודה. מודול argparse הבנוי בפייתון מאפשר בדיוק את זה - להריץ את הסקריפט עם דגלים כמו --length 20 או --no-symbols:
import argparse
parser = argparse.ArgumentParser(description="מחולל סיסמאות")
parser.add_argument("--length", type=int, default=16)
parser.add_argument("--no-symbols", action="store_true")
args = parser.parse_args()
זה שלב שמלמד משהו מעבר ללוגיקה של סיסמאות - איך בונים כלי שאחרים (או אתם בעתיד) יכולים להשתמש בו בלי לפתוח את הקוד ולשנות משתנים ידנית. ככה נראים רוב הכלים שאתם מריצים בטרמינל כל יום.
שלב שלישי - מוסיפים בודק חוזק לסיסמה¶
תוספת מעניינת היא פונקציה שבודקת עד כמה סיסמה קיימת (לא רק כזו שיצרתם) היא חזקה. פה נכנס מושג האנטרופיה - במילים פשוטות, כמה "ניחושים" תוקף יצטרך בממוצע כדי לפצח את הסיסמה. סיסמה עם יותר סוגי תווים ואורך גדול יותר מייצרת מרחב אפשרויות עצום, וזה מה שהופך אותה לקשה לפיצוח בכוח גס (brute force). אפשר לבנות פונקציה פשוטה שבודקת אורך, מגוון תווים, והאם הסיסמה מופיעה במילון מילים נפוצות - ומחזירה ציון או המלצה.
הטעויות הנפוצות בפרויקט הזה¶
הטעות הראשונה היא כמובן שימוש ב-random במקום secrets. השנייה היא הגבלת מרחב התווים בלי סיבה טובה - למשל הסרת סימנים מיוחדים "כדי לפשט", מה שמקטין דרמטית את החוזק של הסיסמה. השלישית היא לשכוח שסיסמה חזקה שנשמרת בטקסט גלוי בקובץ לא שווה הרבה - אם אתם מרחיבים את הפרויקט לכלי ניהול סיסמאות, שמירה בטוחה היא נושא בפני עצמו.
מה מקבלים מהפרויקט הזה¶
מעבר לכלי שימושי שתשתמשו בו בעצמכם, הפרויקט מלמד חשיבה שמבדילה מפתח שכותב קוד שעובד ממפתח שכותב קוד שבטוח - ההבדל בין "זה נראה אקראי" ל"זה באמת אקראי מבחינה קריפטוגרפית" הוא בדיוק סוג הדיוק שחשוב בתעשייה. בקורס תכנות בסיסי החינמי שלנו עוברים על מחרוזות, מודולים בפייתון וגם על החשיבה הזו של "למה" ולא רק "איך" - בדיוק הבסיס שצריך כדי לבנות פרויקטים כאלה נכון. התחילו את קורס התכנות הבסיסי כאן