BinaryVision

למה מתייחסים לSQL שונה?!

מאת בתאריך 14/07/09, תחת כללי

הקדמה
קיימים הרבה מאוד סוגים של פרצות אבטחה, לדוגמא XSS, אני יכול להבין למה XSS קורה, הרי לפעמים יש מקרי קצה שבהם רוצים לתת למתכנת יכול לכתוב html, או לחלופין, קשה לבקר (לא באמת, אבל נניח) כל כתיבה שאנחנו מבצעים ולכן אפשר לפספס ובטעות לכתוב מחרוזת ששכחנו לעשות לה escaping.
אותו דבר אני יכול להגיד על buffer overflow (גם לא באמת, אבל נניח) וישנן עוד הרבה פרצות שעליהן אפשר להגיד אותו דבר (לפעמים המצב עגום יותר ולפעמים פחות).
אבל במקרה של sql injection אני פשוט לא מבין איך זה עדיין קיים. אבל לא על זה מדובר בכתבה, אלא על הבעיתיות והקלת הראש שאנשים מייחסים לשרת sql שלהם.

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

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

2. להשתמש בפונקציות מיוחדות בשפה (אם קיימות במקרה המדובר) אשר נותנות לעביר כפרמטרים את הנתונים שמתקבלים מהמשתמש וככה הבאג הזה בכלל לא קיים, כי מתייחסים לזה בתור אובייקט ולא מחרוזת משורשרת, לדוגמא sqlite ב python מאפשר את זה בקלות.

אז איך זה שאנשים עדיין לא עושים את זה? מעבר לבינתי.

הבעיה האמיתית ופתרון בשבילה
הפתרונות שהצגתי כמובן נפוצים ומשתמשים בהם בכל מיני מקומות, והם הדרך לטיפול בבעיה, אבל לא מעט פעמים נתקלים בכל זאת בפרצות. אבל זה בהחלט לא מספיק!
הבעיה האמיתית היא, איך בכלל הגענו למצב שבו כל מה שאנחנו צריכים זה לקרוא מידע מ"טבלאת הידיעות של האתר" ובכל זאת יש לנו הרשאות כתיבה\קריאה ל\מטבלאת המשתמשים?
כל איש אבטחה יודע שהדבר הראשון שעושים זה "הפרדת סמכויות" כי בסופו של דבר, לא משנה כמה ה firewall שלך חוסם כל תעבורה וכל התוכנות שאתה משתמש בהן לא פגיעות, אם ל guest account שלך יש הרשאות root, יהיה לך גהנום בשרת 🙂
בכל שרת sql בתחום יש שמות משתמש וססמאות, יתרה מכך, לכל שרת יש תמיכה באפשרויות הגבלת גישה מתקדמות.
אז למה אנשים לא משתמשים ב user אחד בשביל לקרוא\לכתוב מהטבלאת משתמשים, ביוזר אחר בשביל לקרוא מידע וביוזר אחרון בשביל לכתוב מידע? (כאשר מידע = הכל חוץ מטבלאות משתמשים) או אפילו אם צריך לפצל את זה עוד יותר (לדוגמא מידע אישי רגיש יהיה מפוצל מהבלוג של האתר).

ההנחה שלי היא: עצלנות.

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

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

הערה חשובה:
אני יודע שלא תמיד יש לכם גישה ליצירת גישות (לדוגמא שרת חינמי) אבל כל עוד אתם משלמים על השרת, זכותכם לדרוש כמה יוזרים שתרצו! ככה שזה לא תירוץ!!!

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

:, , , ,
23 תגובות:
  1. cP

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

    נהנתי לקרוא 🙂

    (נערך לתיקון typo לבקשת cp)

  2. TAsn

    אני שמח שנהנת.

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

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

  3. Ch(1)33t4

    אחלה פוסט טאסן! נהניתי לקרוא 😉
    הבלוג הזה הולך ומשתפר.. מגיע לכם ח"ח (:

  4. TAsn

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

  5. עמנואל

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

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

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

  6. TAsn

    עמנואל:
    בנוגע לחלק הראשון של מה שכתבת, אני מודע לזה, זה היה סתם rant בשביל הקדמה 🙂

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

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

    בנוגע לסמיילים, אני מניח שכבר שמת לב שיש סמיילים…

  7. shesek

    בנוגע ל"להשתמש בפונקציות מיוחדות בשפה", ב PHP אפשר לעבוד עם PDO, או אם בא לכם ORM, יש את Doctrine ו Propel (וגם כמה נוספים)

    ובנוגע לנקודה העיקרית של הפוסט – לא פרקטי, לא מבחינת ביצועים/עומס על השרת, ולא מבחינת שימושיות.

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

    ובעיה של פרקטיות:
    בהרבה מאוד מקרים ניגשים למידע מטבלאות שונות (JOIN, UNION, Sub Query). שיטת עבודה כמו שהצעת פשוט לא תאפשר את זה.

  8. shesek

    קראתי את התגובות הקודמות ואני רוצה להוסיף משהו

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

    ומצב שיקרה כזה עם JOIN הוא למשל הוצאה של 5 מאמרים אחרונים + מידע על הכותבים שלהם מטבלת משתמשים

  9. cP

    או פשוט עמוד "עריכת פרטים אישיים"..

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

  10. TAsn

    שסק:

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

    בקשר לאיחוד טבלאות. לא אמרתי שאסור שיהיה משתמש כללי, בסופו של דבר אם אין ברירה אין ברירה, אבל בדרך כלל יש ברירה, במיוחד אם אתה עושה חלוקה של read vs write או טבלאת ססמאות vs כל השאר. אז אני לא חושב שתתקל בזה. בכלל, החלוקה שהצעתי (לדוגמא מידור בין חלקים חשובים) נוגעת לחלוקה לדברים ש*לא* אמורים להיות ביחד, לא לדברים שאתה תצטרך לעשות עליהם join למיניהם.
    או בקיצור, אני לא מסכים איתך בכלל.

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

    לסיכום: אני ממש מסכים עם cP שגם אם זה היה גורם לעומס, צריך היה להשתמש בזה, כי על אבטחה לא מוותרים.

  11. shesek

    cp, הנקודה היא ששימוש ב PDO/ORM מונע SQL Injection לחלוטין בלי השפעה כל כך מסיבית על ביצועים. אז נכון שאני יכול להוציא יותר כסף ולקנות שרת חזק יותר, אבל להכפיל/לשלש את מספר החיבורים שהדאטאבייס שלך מטפל בהם זה פשוט לא תכנון נכון של אפליקציה. מעבר לזה, שרת חזק יותר זה לא פתרון ישים לכולם.

    TAsn, אם אתה מכפיל את מספר החיבורים תהיה השפעה מורגשת על מהירות ושימוש במשאבים. זה שהחיבורים לא מוחזקים פתוחים בו זמנית לא ישנה הרבה.

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

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

    שסק

  12. TAsn

    שסק,

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

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

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

  13. shesek

    http://blog.jemm.net/articles/databases/best-practices-database-connections/

    1. Connections are expensive

    It takes time to open connection and the less connections there are open, the better.

    While it may sometimes seem that it doesn’t take so long to get data from the database to the client, opening of the database connections don’t come without price – especially when the number of users rise.

    Luckily connection pooling takes the most strain away thus making connections less expensive. More about connection pooling in the next section.

    While opening a connection is expensive, it would be worse to keep the connection always open or to have too many simultaneous open connections.
    ….
    One problem happens when there are database credentials for each user. This would require connection string for each user thus making the connection pool useless.

    http://www.server-management.co.uk/features/Database_connections_in_depth
    * Creating a connection is performance-expensive compared to all other tasks a database application can perform.
    * Open connections use a substantial amount of memory on both the database server and database client machines.
    * Establishing a connection takes multiple network round trips to and from the database server.
    * Opening numerous connections can contribute to out-of-memory conditions, which might cause paging of memory to disk and, thus, overall performance degradation.

  14. shesek

    ושוב – PDO/ORM חוסם SQL Injection לחלוטין, עם פגיעה יותר מינימלית בשימוש במשאבים.

  15. shesek

    ועוד משהו קטן שלא שמתי לב שהיה בלינק השני:

    Why Connections Are Performance-Expensive

    Developers often assume that establishing a connection is a simple request that results in the driver making a single network round trip to the database server to initialize a user. In reality, a connection typically involves many network round trips between the driver and the database server. For example, when a driver connects to Oracle or Sybase, that connection may take anywhere from seven to ten network round trips to perform the following actions:
    • Validate the user’s credentials.
    • Negotiate code page settings between what the database driver expects and what the database has available, if necessary. • Get database version information.
    • Establish the optimal database protocol packet size to be used for communication.
    • Set session settings.

    In addition, the database management system establishes resources on behalf of the connection, which involves performance-expensive disk I/O and memory allocation. You might be thinking that you can eliminate network round trips if you place your applications on the same machine as the database system. This is, in most cases, not realistic because of the complexity of real-world enterprises—many, many applications accessing many database systems with applications running on several application servers. In addition, the server on which the database system runs must be well tuned for the database system, not for many different applications. Even if one machine would fit the bill, would you really want a single point of failure?

  16. spdr

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

    ועוד דבר – במתקפת DoS נגד שרת ווב שמשתמש ב SQL, בד"כ ה sqld יהיה הראשון שיגרום לבעיות ועומס על השרת

  17. TAsn

    spdr, ידוע ש sqld הוא הראשון שיקרוס, הרי הוא עושה הרבה מאוד עבודה עבור כל קצת עבודה שנעשית בשרת הווב. (הוצאה מdb יקרה יותר).
    אבל זה בגלל ההוצאה עצמה מה db ולא בגלל כמות החיבורים. אם אתה דואג כל הזמן לנתק חיבורים, כמות החיבורים בו"ז לא גדולה בהרבה ולכן אם אין לך בעיה של הגבלת כמות החיבורים בו זמנית.

    לגבי הפגיעה במהירות, אני באמת חושב שאתה טועים, אבל אני אומר את זה לפני שקראתי את מה ששסק כתב (אני ער 40+ שעות ואין לי כח כרגע).

    אבל בקשר לPDO, שסק, אני מסכים, ואפילו ציינתי את זה בכתבה עצמה (ועוד שישים פעמים בתגובות כל פעם שהעלת את זה), זה בהחלט הפתרון שצריך להשתמש בו. אבל לך תדע איזה עוד פרצות יכו עלינו בעתיד. זה היה rant על sql injection אבל באותה מידה יכול להתגלות באג בדרך שבה PDO מנהל את הדברים ואז חזרנו לנקודת ההתחלה.

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

  18. שסק

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

  19. TAsn

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

    אבל כמו ש cp אמר, זה לא תמיד רלוונטי, אבטחה עדיפה על יעילות, ולאבטחה יש מחיר.

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

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

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

  20. רמי

    של רב TASN,

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

    שוקור בדארנה

  21. TAsn

    רמי: שלחתי לך מייל.

  22. cP

    פישינג! גנבו ל- TASN ת'מייל! צפה לספאם עד שארית חייך!
    :X

  23. TAsn

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

השאר תגובה

מחפש משהו?

תשתמש בטופס למטה כדי לחפש באתר: