כמה מילים על הצפנת סיסמאות ב-DB

עדכון לפוסט: התווספו שיטות נוספות מומלצות, תודה למגיבים בטוקבקים.

ביום ה' האחרון נודע כי אתר "תפוז" נפרץ, ובסיס הנתונים שהכיל את פרטי המשתמשים (כמו אימייל, סיסמאות וכו') נפרץ. פריצה זו מצטרפת לשורת פריצות באתרים שונים, ויש אתרים הרבה יותר גדולים שנפרצים ופרטי המשתמשים מופצים לכל עבר (ומי שעושה מזה הכי הרבה הם הספאמרים כמובן, עוד כמה מיליוני רשימות לדחוף להם ויאגרה מזוייפת, ועוד כל מיני שקרים).

הבעיה העיקרית בד"כ של אותם אתרים, זה שאותם מתכנתים באותה חברה, נמצאים תחת אשליה שאם יש את הקופסאות XYZ שנותנים כל מיני סוגים של "חומות אש", אז הם מוגנים ואפשר לשים פס גדול על כל עניין של אבטחת מידע, המלחת סיסמאות וכו'. בד"כ אני קורא לזה במובן הכי פשוט מתכנתים עצלנים.

אבל זה לא קורה רק אצל אתרים גדולים, זה קורה גם באתרים בינוניים וקטנים, כאלו שלא תשמעו עליהם או כאלו שאולי תשתמשו בהם ולא תדעו שהפרטים שלכם חשופים. זוכרים את "הפורץ הסעודי"? (כן, הוא כבר מת), זה שפרץ לאתרים שונים ופירסם מספרי כרטיסי אשראי, מה שהכניס את חברות האשראי בארץ לסרטים ובמיוחד את לקוחותיהם? אז כן, הבעיה גם שם.

והיא קשורה בד"כ לעצלנות נטו או לתחושה של "הכל בסדר" בגלל הבטחות שניתנות ע"י ספקי אחסון שונים ש"הכל מוגן". כמובן שאין חיה כזו

אז לטובת המפתחים שמכירים חלקית את נושא ההמלחה והצפנה, ולטובת אלו שלא מכירים, הנה שיעור מזורז. אני מדבר בפוסט זה על MySQL אבל זה רלוונטי לכל אפליקציית בסיס נתונים.

באופן עקרוני, תוכנת בסיסי נתונים (אני פשוט אקרא לזה DB בפוסט זה) יודעות לאחסן סיסמאות בצורה מוצפנת, אבל ההצפנה עצמה מאוד קלה לפריצה. כך לדוגמא כשמשתמשים בפונקציית Password ב-MySQL כדי להכניס משתתמשים לטבלת משתמשים ב-MySQL כבר עושים את הטעות הראשונה. כפי ש-MySQL כותבים בעצמם, זה לא מיועד לניהול משתמשים עבור האפליקציה שלך, אלא עבור התחברות והתממשקות ל-MySQL. 

כשרוצים להוסיף משתמשים לאפליקציה שלך, צריכים לבצע מספר שלבים:

השלב הראשון לאחר קבלת פרטי המשתמש, הוא "להמליח" את הסיסמא ומומלץ גם את כתובת האימייל. ב"המלחה" הכוונה להוסיף מספר תווים רנדומלי לפני, או אחרי, או באמצע הקלט שקיבלת, כך שאם הסיסמא שהזנתי היא hello123 אז לאחר ההמלחה היא תהיה hello123#O2j!d%425  (כאשר אחרי ה-3 יהיו ג'יבריש של אותיות רנדומליות שהמערכת תחשב מחדש בכל פעם שמישהו נרשם. אפשר לשפר ולהוסיף ג'יבריש בגדלים שונים [אנחנו פה לאמלל את חיי הפורץ או לא?], ואפשר לשים את הג'יבריש לפני הסיסמא האמיתית [כל עוד היא בגודל קבוע, כדי שתוכלו אחר כך לוודא שהסיסמא תקינה] או באמצע, הכל תלוי בכישורי התכנות של המפתח.

עכשיו, לפני שאנחנו מכניסים את הסיסמא עם הג'יבריש, אנחנו נצפין את הסיסמא עם הג'יבריש יחד. יש כל מיני סוגי הצפנות, אני ממליץ ללכת על AES-256 (חובבי PHP – יש כאן דוגמא). בשיטת הצפנה זו, ישנו מפתח שלכם, ואיתו אתם מצפינים. ההצפנה מומלץ שתיעשה דרך האפליקציה שלכם והמפתח שלא ישב בקוד עצמו אלא במקום םאחר מחוץ לתיקיות הקוד עם הרשאות מצומצמות מאוד. מדוע AES-256? כי זו הצפנה חזקה מאוד מצד אחד, ומצד שני היא לא מכבידה על שרת האפליקציות (כל שרת עם מעבד Xeon מסידרה 5XXX או מעבד i5 ומעלה או כל מעבד של AMD מה-3 שנים האחרונות כולל קידוד ופתיחת AES על הסיליקון עצמו)

אחרי שהצפננו על שרת האפליקציה את הסיסמא המומלחת, אז נוכל לכתוב את הפלט לתוך ה-DB. כך גם נעשה בצורה הפוכה כשמשתמש יתחבר למערכת ונוודא שסיסמתו נכונה. 

נקודה חשובה: אם אתם עובדים דרך command line בהתממשקות ל-DB שלכם, תוודא שכשאתם יוצאים, למחוק את קובץ ההיסטוריה. אני מכיר לפחות מקרה אחד שמתכנת הצפין את הכל, אבל שכח שהמפתח שלו היה גם בקובץ היסטוריה ומי שפרץ תפס שהמפתח שם וכל ההצפנה לא ממש עזרה (בגלל זה אני ממליץ לעשות הצפנות רק בקוד שלכם, לא ישירות על ה-DB).

שיטה נוספת היא שיטת ה-Hashing – בשיטה זו אלגוריתם (כמו SHA256) מקבל טקסט כלשהו (כמו סיסמא) והוא פולט מחרוזת שנראית כמו ג'יבריש. הסיסמא עצמה לא נשמרת בשום מקום ולא נכתבת לדיסק הקשיח של השרת. מה שכן נכתב ל-DB הוא הפלט, או כפי שהוא נקרא – ה-Hash. כך כשהמשתמש מנסה להתחבר ומקיש סיסמא, הסיסמא שהוא מקיש גם עוברת את אותו תהליך ואם ה-Hash שנוצר מהסיסמא שהמשתמש זה עתה הקיש שווה ל-Hash שנמצא ב-DB, אז הסיסמא היא נכונה והמשתמש יוכל להיכנס. חשוב לציין – גם כאן ישנם אתרים רבים שמאפשרים לכל מיני פורצים להוריד טבלאות Hash מוכנות, כך שהפורץ יוכל עם GPU חזק וטבלאות – לנסות לפרוץ את ה-DB אחרי שהוא העתיק אותו ולכן חובה להוסיף לאחר הסיסמא ג'יבריש רנדומלי באורך קבוע (נניח 10-20 סימנים שמיוצרים ע"י פונקציית RAND) ולקזז את האורך הזה בקלט הסיסמא שנקלטת מהמשתמש, ואז להריץ פונקציית Hash לפי האלגוריתם שבחרתם. תוכלו לקבל פרטים נוספים ודוגמאות קוד בשפות שונות כאן. שיטה זו היא מעולה לאתרים מסויימים אך ישנם חברות שרוצות דווקא לשמור את הסיסמא עצמה, כך ששיטה זו לא תתאים להן ולכן חשוב לבדוק מה הלקוח רוצה.

אפשרית שלישית נוספת היא לוותר על עניין ההזדהות ה"עצמית" (כלומר שהאתר שלך שיזהה את המשתמש), והישענות על שרתים כמו של גוגל או פייסבוק או טוויטר וחברות אחרות, כך כשהמשתמש ירצה להיכנס, הוא יזין לתוך חלק מיוחד באתר (חלק שלא מגיע מהאתר שלכם אלא מאתר אותה חברה שבחרתם) את שם המשתמש והסיסמא שלו, והאותנטיקציה של אותה חברה תחזיר לכם אישור אם המשתמש והסיסמא נכונים וכן את פרטי המשתמש (שם פרטי, כתובת מייל וכו'). בשיטה הזו אין לכם צורך באחזקת סיסמאות (זולת סיסמאות התחברות ל-DB ולניהול המערכת), אך החסרון העיקרי של שיטה זו הוא שבאתרים רבים אנשים מעוניינים להגיב שלא בשמם/כינוי שלהם אלא להגיב בצורה אנונימית, וכאן אתם צריכים להחליט אם להכריח משתמש להתחבר עם הפייסבוק/טוויטר/גוגל שלו או לתת לו גם אפשרות להירשם דרך המערכת שלכם על מנת להגיב.

חשוב לזכור משהו אחד: רוב הפורצים למערכות אינם מתכנתים עם נסיון עשיר. הם Script kiddies שמשתמשים בסקריפטים מוכנים שהורידו או שהם "שיפצו" פה ושם איזה סקריפט שהורידו. הם ידעו איך להיכנס לשרת ה-DB שלכם ואולי אפילו להיכנס לשרתי אפליקציה שלכם, אבל בחיטוט ולימוד הקוד הם לא מי יודע מה, מה עוד שהם רוצים להתנתק כמה שיותר מהר כדי לא להיתפס. פה בדיוק החלק שאתם יכולים לאמלל אותם בכך שאתם יכולים להצפין קוד פרודקשן, או להצפין / לג'ברש את הקוד שקורא למפתח עצמו ושלל טריקים נוספים שבא לכם, כך שגם אם פורץ יעתיק את ה-DB, הוא לא יוכל לעשות איתו כלום (חוץ מאשר אולי לראות דברים שהוא לא היה אמור לראות מבחינת תוכן).

כל עניין הצפנת סיסמאות, שמות משתמשים וכתובות מייל אינו מורכב כל כך ליישום. הכל תלוי באיזה DB ובאיזו שפה אתם מתכנתים, לפיכך מומלץ להקדיש לכך כמה שעות פעם אחתוליישם הצפנה רצינית בקוד ובמפתחות כדי שאם לכם יפרצו, לא תצטרכו לעמוד מול כל העולם ואחותו עם פדיחה רצינית וצורך לענות לעיתונאים איך אתם כל כך חסרי אחריות. במקרה של תפוז, לדוגמא, המזל הגדול של תפוז זה שרוב המשתמשים הם עם שמות משתמשים בעברית והפורץ לא ידע בכלל מה זה UTF-8, אז אי אפשר להשתמש ברשימות שהופצו כדי להיכנס למערכת של תפוז (אבל אפשר להספים את המשתמשים…)

הפריצה והגניבה של כרטיסי האשראי: אנליזה שלי

מאז שכתבתי את הפוסט, קראתי עוד קצת כתבות, של עידו קינן ואחרים, ונראה כי המדיה "קנתה" את הסיפור של בעלי חברת בניית האתרים: אנחנו היינו מוגנים, חשבון ששיתפנו ולא היה באחריותנו לא היה מוגן ודרכו נפרץ האתר עם פרטי הכרטיסים.

אין חולק על כך שפורץ סעודי עשה את העבודה. אין חולק על כך שהכרטיסים נגנבו. אינני מכיר את אותם בוני אתרים, הם לא לקוחות של העסק שלי ולי אין מושג ירוק מה מותקן בשרת זולת הידיעה (דרך Netcraft) שהאתר רץ על Linux.

עם כל זאת, אני מאמין לאותם אנשים כמו שאני מאמין שעל כתפי הימנית יושבת כרגע פייה….

נתחיל בבסיס (המאמר הולך להיות עמוס במונחים טכנולוגיים אך אנסה לפשט כמה שאפשר): בעקרון מערכת Linux רגילה שפותחים משתמש חדש (שהוא אינו עם הרשאות root ואינו בעל הרשאות sudo – הרשאות המאפשרות בעצם לשנות את כל המערכת), אינו יכול לשנות מאומה זולת התיקיה שלו. אם לדוגמא יש לו הרשאה של גישה לבסיס נתונים, הוא יכול ליצור בסיס נתונים, לתת הרשאות בתוכו, ליצור משתמשים נוספים ובסיסי נתונים נוספים (לפי כל מיני מגבלות שמנהל השרת קובע). המשתמש יכול להתפרע עם ההרשאות – אבל רק עד הרמה של המשתמש שלו, כלומר אין לו אפשרות "לטפס" עם ההרשאות "כלפי מעלה" לכיוון הרשאות סיסטם.

כל זה כמובן מדובר על מערכת Linux שמותקנת ומעודכנת עם הטלאים האחרונים (yum update או apt-get update תלוי בהפצה) ועם הגדרות הקשחה נוספות. אותם בוני אתרים טענו שיש להם תקן PCI (אם הבנתי נכון) כך שאם באמת היה להם תקן PCI, מי שמימש אותו היה צריך להקשיח את המערכת.

נחזור אחורה בזמן לאותם רגעים שהפורץ הסעודי נכנס במערכת, וכאן הפואנטה העיקרית: אם המערכת היתה באמת מוקשחת ומוגנת, הפורץ היה מקסימום יכול לשבש/למחוק את אותם אתרים שמתארחים על אותו חשבון שדרכו הוא פורץ. לא צריך להיות גאון, מספיק שהאפליקציה המותקנת על אותו חשבון רעוע אינה מעודכנת, וניתן בדקות ספורות להיכנס ולעשות מה שרוצים אבל רק באותו חשבון, כלומר לא ניתן לשבש חשבונות אחרים, ולא ניתן לדעת מה קורה בחשבונות אחרים. אם כמובן החשבונות האחרים מותקנים עם אותה גירסת אפליקציה, אז אותו פורץ יכול גם לדפוק את החשבונות, אבל עדיין לא יהיו לו ההרשאות לראות את כל הקבצים ובסיסי הנתונים של כל המשתמשים.

נחזור לטענה של בוני האתרים: מהאתר שאינו שלהם ושהם אירחו – אותו פורץ סעודי נכנס לאתר שלהם וגנב משם את הפרטים.

כלומר כדי להיכנס לקבצים של חשבון אחר (בכל זאת, הפורץ צריך לדעת מה שם ה-DB, סיסמא וכו'), צריך לעשות אסקלציה של הרשאות.

ואיך עושים אסקלציה? משתמשים בפירצות ידועות במערכת ההפעלה, פרצות שנסגרו אבל עדיין לא עודכנו בשרת הנוכחי, כלומר כל מה שהפורץ היה צריך לעשות זה להריץ סקריפט או אפליקציה מול אותו שרת, ולראות איזה חור יהיה עדיין פתוח. כנראה שהוא מצא, משם המרחק לקבל הרשאות יותר גבוהות (ואפילו root) מאוד קצר. אחרי שהוא הצליח, הוא כנראה עשה משהו כמו mysqldump לקובץ, שם את הקובץ במקום זמין לו ושאב אותו ב-Download רגיל.

במילים אחרות: אם השרת היה באמת מוקשח עם עדכונים אחרונים, כל זה לא היה קורה.

חבל שהמדיה רצה לקנות את הסיפור שסיפרו לה במקום להתייעץ עם מישהו שבאמת מבין באבטחה..

האם ניתן לעשות משהו כדי למנוע? כן, לא מעט:

  • עדכונים, כל הזמן לעדכן את השרתים, גם גרסאות ישנות של לינוקס עדיין מוציאים להם עדכונים
  • אם יש לכם קוד שמטפל בכרטיסי אשראי, מומלץ להצפין את הקוד ולשמור קוד מקור במחשב אחר לחלוטין. ניתן להצפין קוד PHP עם ION Cube כך שגם אם מישהו פורץ, הוא לא יוכל להסתכל בקוד
  • להצפין את ה-DB. אפשר להשתמש בדרכים כמו שמופיעים כאן לדוגמא
  • שילוב של 2 הסעיפים: ההצפנה מבוצעת בקוד והתוצר המוצפן מוכנס ל-DB כאשר הקוד עצמו מוגן עם ION Cube

ויש עוד דרכים ושיטות אחרות שמומחי אבטחה יכולים להמליץ עליהן.

לסיכום: אני לא מאמין לאותו תירוץ שאותם בוני אתרים סיפרו לעיתונות ואני חושב שהסברתי בפוסט זה מדוע זה לא יתכן. אם מישהו "בונה" על תקן PCI שזה יתן לו אבטחה מקסימלית, הוא מוזמן להתעורר מהאשליה הזו. שום פתרון אבטחה אינו מועיל אם שוכחים לעדכן את השרתים בעדכוני אבטחה.