הבלוג של ארתיום
בלוג על לינוקס, תוכנה חופשית, מוזיקה, סלסה, ומה לא!
כיצד בונים מערכת פרוצה מרכיבים בטוחים קריפטוגרפית
אתמול שחררתי גרסאות CppCMS חדשות בשני הענפים - היציב וענף הבטא שסגרתי בהן באג שקשור לשימוש לא נכון ברכיבים קריפטוגרפיים.
תחילת הסיפור
הסמסטר אני לוקח קורס יסודות קריפטוגרפיה של פרופ' רן קנטי. במהלך הקורס דננו בנושא הצפנה והדרישות של בטיחות.
נגיד, יש לנו אלגוריתם שיודע להצפין בצורה בטוחה: היריב לא יוכל להשיג שום מידע על התוכן של הודעות בהסתכלות על טקסט מוצפן; ואז שואלים שאלות כמו האם זה מספיק, באיזה תנאים וכו'.
עכשיו, נחזור לבעיה שאני מנסה לפתור ב־CppCMS בעזרת הצפנה.
אני רוצה לשמור מידע (session) בצד הלקוח בעוגייה ואני רוצה להבטיח שני דברים:
- הלקוח לא ידע מה אני שומר אצלו.
הלקוח לא יוכל לשנות את המידע ששמרתי;
הגדרת יותר מדויקת: הוא לא יוכל ליצור session (עוגייה) עם מידע שאני לא יצרתי.
הפתרון:
ניקח תוכן שאני רוצה לשמור אותו, נחשב לו checksum בעזרת פונקציה hash ונצפין אותו יחד עם ה־hash. סביר להניח שאם אין יכולת להפוך את הטקסט לטקסט מוצפן ובחזרה אז לא ניתן גם ליצור משהו שהתוכן וה-hash שלו יהיו תואמים.
האם יש אנשים שחשבו כך והשתמשו בזה לפניי? בהחלט כן - פרוטוקול WEP עושה בדיוק את הדבר הזה! אז, כפי שאתם כבר בוודאי יכולים לנחש, מימשתי פתרון דומה ב־CppCMS כדי להבטיח שהלקוח לא ייצור session לא חוקי, רק שהשתמשתי בהצפנת CBC-AES וב־hash קריפטוגרפי ולא ב־CRC32 (כמו שזה היה ב־WEP).
למען הסר ספק, לא השתמשתי בשיטה הזו בגלל שראיתי שימוש בה ב־wep - אני יודע כי wep פרוץ
אז ההרצאה דנה בנושא הצפנה ובטיחות. במהלכה הוצג שהפתרון הזה לא נכון וכלל לא מבטיח בטיחות במקרה כללי! כיוון שלא הצלחתי במהלך ההרצאה להבין כיצד במקרה פרטי שלי אפשר לתקוף את המנגנון הזה, התחלתי לחפש ומצאתי מאמר מאוד מעניין שעוסק ניתוח בעיית ההצפנה ואימות הזהות ביחד ובודק פתרונות שונים.
מי שמעוניין יכול לקרוא את המאמר, אבל בגדול הוא מציג התקפה אמתית (יחסית פשוטה) כנגד השיטה כזו שעובדת עם הצפנה מבוססת CBC ו־hash שמוצמד לסוף התוכן. המאמר קובע כי כל שיטה שמסתמכת על hash פומבי לבדיקת אמתות לא תעבוד. המאמר ממליץ על אימות התוכן המוצפן בעזרת MAC שעובד עם מפתח נוסף.
מה שחשוב שניתן להוכיח בקלות שאם ה־MAC שלנו בטוח וההצפנה בטוחה אז השיטה הזו תהיה בטוחה גם כן.
המעשה
אחרי הבנתי שיש לי באג קריטי במערכת ניהול ה־session בשני הענפים של CppCMS התחלתי לעבוד על התיקון (שהוא בסה"כ תיקון פשוט) ושחררתי גרסה החדש עם התיקון הנדרש.
בדיעבד אני מאוד שמח שלקחת את הקורס הזה, למרות שהוא מסוג הקורסים המאוד תיאורתיים שבד"כ אני לא אוהב ועוסק בנושאים שיחסית רחוקים הקריפטוגרפיה מעשית.
מסתבר שגם כאן, כמו בבכל דבר, אין תחליף לבסיס תאורתי מוצק.
מסקנות
שימוש באלגוריתמים בטוחים לא מבטיח שום בטיחות אם משתמשים בהם בצורה לא נכונה. לכן, בכל פעם שאתה עוסק במשהו קשור לקריפטוגרפיה, אם יש לך אפשרות תן למומחה להסתכל על מה שעשית.
האם אפשר היה לעשות אחרת?
אני חשבתי על זה - אולי אני בכוח מנסה להמציא גלגל קריפטוגרפי המחדש?
אבל ככל שאני חושב יותר, אני מבין שכנראה בהינתן הידע והניסיון שיש לי, לא יכולתי לעשות משהו יותר טוב.
- לא קיים פתרון סגור (קרי ספריה) לבעיה שאני צריך, בד"כ ההצפנה ואימות הן חלק מפרוטוקולים קיימים כמו TLS או SSH והם ממומשים ספציפית עבורם בצורה של תקשורת כך שהמשתמש מקבל API דמוי socket וכל מה שיש למטה מוסתר.
- רוב הספריות נותנות מימוש לש פרוטוקולים קיימים או אבני בניין כמו AES או MAC שאתה אחראי לחבר אותם ביחד בצורה נכונה, כך למשל, אם אתה לא מאתחל נכון את iv באלגוריתם CBC אתה תיצור חור אבטחה בקלות.
- עשיתי כמיטב יכולתי והשתמשתי ברכיבים מוכנים, לא כתבתי מימושים של AES או של SHA1 מחדש, נעזרתי בקוד קיים בדוק ומתוחזק היטב.
הבעיה שהרכבתי את האלגוריתמים קריפטוגרפיים בצורה לא נכונה; ולמען האמת, אני לא הראשון ולא האחרון שעשה דבר כזה - היו גדולים לפני שעשו טעויות כאלה וגם יהיו אחרי.
בהסתכלות לאחור - נראה עשיתי באמת מה שיכולתי. אם לא הייתי מפתח קוד פתוח פרטי אלא מנהל פרויקט בחברה שמפתחת פרויקט כזה אז, הייתי מזמין מומחה כדי לעשות בדיקה לקוד ולהחלטות שעשינו.
תגובות
יש בעיה בכלל בגישה שלך. דבר ראשון הוכח עד היום שכל ה MD למינהם, אפשר ליצור להם התנגשויות, וליצור משהו שהוא לא באמת נוצר על ידי המצפין המקורי.
דבר שני, אני חושב שמזהה session חד חד ערכי שלא ניתן לזיוף, זה דבר שהוא יותר אוקסימורון. מה שכן, אתה יכול להשתמש באורך מספיק גדול של תווים שיכולת הניחוש תיקח מספיק זמן בשביל שלא ישתלם ככה סתם לעשות לך "הרעלת session" או כל פעולה אחרת כמו זיוף שלו.
דבר שלישי שאפשר לבדוק וזה כבר בתוך ה session, זה האם מבקש ה id הוא גם הבעלים שלו. כלומר זה שיש לך מזהה מסויים, עדיין לא אומר שהוא באמת שייך לך, ואם הוא מכיל מידע מסויים שאתה מחפש נגיד כתובת MAC עם כתובת IP ועוד מספר רב של נתונים, ורק אם הם מתקיימים, אז אתה מניח שזה הבקשה הנכונה. שים לב שעדיין אפשר לזייף, אבל ככל שזה מורכב יותר ככה, ההתקפה תהיה גם מורכבת יותר. אבל אם אתה בודק רק מזהה של session, ההתקפה לפעמים פשוטה יותר ממה שאתה מדמיין. אני פשוט יכול לעשות brute force למזהים ולנסות את כולם עד שאקבל תוצאה רצויה.
מה הוכח?
MD5 כן פרוץ במובן החזק ובמובן החלש אפשר לפרות אותו בחודש של חישובים כבדים, SHA1 לא פרוץ, כמובן גם לא sha256 ומעלה.
דבר שני MD איננה הצפנה אלא hash. ברור שאם אתה יודע למה אתה מחשב hash אז אתה יכול לחשב אותו ללא בעיות.
שיב לב, אני לא משתמש ב־MD תור הצפנה אלא בתור פונקציית hash בלבד.
קרי, אני לא מניח שאם c=hash(m) ואני יודע את c אבל לא את m אז לא אוכל לגלות את m - כי אם m מספיק קצר בעזרת brute-force אתה מגלה אותו מאוד מהר.
זאת הגישה המקובלת לניהול sessions, אבל היא דורשת server-side-storage, זה משהו שלפעמים מאוד יקר לעשות. בגלל זה יש אפשרות לשמור משהו בצד לקוח לנגיד לחתום עליו דיגיטלית כך שהוא לא ישנה את זה.
אגב RoR תומך בדבר כזה - יש לו תמיכה ב-session עם חתימת HMAC. זה לא משהו חדש. כמובן יש לזה מספר בעיות אחרות אבל זה סיפור אחר.
CppCMS מאפשר לך לא רק לחתום אלא גם להצפין את המידע שאני שומר בצד הלקוח. אם אתה רוצה, כמובן אתה יכול לעבוד גם עם session-id אבל אז נדרש איזשהו db בצד השרת.
ראה:
נראה לי שלא הבנת את המטרה של ההצפנה שאני משתמש בה.
הוסף תגובה:
חובה לאפשר JavaScript כדי להגיב.