• בלוג
  • חמש טעויות אבטחה שכל מתכנת חייב להכיר

חמש טעויות אבטחה שכל מתכנת חייב להכיר

12/11/2017

מאגר בשם Common Weakness Enumeration (או בשמו המקוצר CWE) מונה כ-700 טעויות אבטחה שאנו עלולים בטעות לשתול בקוד שלנו. חשוב להכיר כמה שיותר חולשות מהמאגר. הנה ה-6 שתוכלו להתחיל מהן.

1. הזרקות קוד

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

הזרקת קוד מפורסמת מאוד היא CWE-89 או בשמה הציבורי SQL Injection. בהזרקה זו קלט ממשתמש הופך לחלק משאילתת SQL ואם לא נבדק כראוי עלול לייצר שאילתות שהמתכנת לא התכוון להריץ.

הזרקה נוספת היא CWE-78 והיא מדבר על הזרקת קוד Shell. קחו לדוגמא את הקוד הבא ב PHP:

$userName = $_POST["user"];
$command = 'ls -l /home/' . $userName;
system($command);

הקוד אמור להפעיל פקודת ls על תיקיה ששמה הועבר כקלט מהגולש. אבל מה קורה כשהגולש מעביר כקלט את הטקסט ;rm -rf / ? סימן נקודה פסיק מוסיף פקודה נוספת לביצוע ובמקרה הזה זו פקודת מחיקה.

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

2. שינוי תיקייה

חולשת CWE-22 (או בשמה הלועזי Path Traversal) מתארת מצב בו אנו מתכננים להפעיל פקודה בתיקייה אחת אבל קלט זדוני ממשתמש גורם להפעלת הפקודה בתיקייה אחרת. קחו לדוגמא את הקוד הבא בשפת Java:

String path = getInputPath();
if (path.startsWith("/safe_dir/"))
{
    File f = new File(path);
    f.delete()
}

הקוד מקבל מהגולש שם של תיקייה כדי למחוק ממנה קובץ ומנסה לוודא שהתיקייה ממוקמת בתוך תת העץ safe_dir. עכשיו נסו לנחש מה יקרה אם הקלט יכלול את סימן מעבר התיקיות .. למשל /safe_dir/../../../etc/passwd.

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

3. התיחסות לקלט לא מאומת

חולשה מספר CWE-345 היא ככל הנראה המורכבת ביותר ברשימה זו. מדובר על קלט תקני לחלוטין אבל שנשלח מחוץ להקשר או בלי כוונה.

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

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

שימו לב שיש לכם דרך לוודא שכל הודעה שנכנסת למערכת שלכם הגיעה בכוונה מהאדם הנכון.

4. מרוץ תהליכים

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

קחו דוגמא קלאסית של קוד perl שבור:

$transfer_amount = GetTransferAmount();
$balance = GetBalanceFromDatabase();

if ($transfer_amount < 0) {
    FatalError("Bad Transfer Amount");
}

$newbalance = $balance - $transfer_amount;
if (($balance - $transfer_amount) < 0) {
    FatalError("Insufficient Funds");
}

SendNewBalanceToDatabase($newbalance);
NotifyUser("Transfer of $transfer_amount succeeded.");
NotifyUser("New balance: $newbalance");

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

אם הקוד שלכם רגיש להפעלה במקביל, וודאו שאתם מגינים עליו באמצעות נעילה.

5. שמירת סיסמאות בקבצי הגדרה

חולשה CWE-260 מדברת על מנהג ספציפי של מתכנתים רבים והוא שמירת סיסמת החיבור (למשל לבסיס נתונים) באותו קובץ עם שאר הגדרות החיבור לאותה מערכת.

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

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

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