לדלג לתוכן

מה זה Race Condition באתרים ולמה קשה כל כך לתפוס אותו בבדיקות רגילות

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

אז מה זה בעצם Race Condition

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

TOCTOU - הרעיון שעומד מאחורי כמעט כל Race Condition

יש מונח שכדאי להכיר כי הוא מסביר את רוב המקרים האלה - TOCTOU, ראשי תיבות של Time-Of-Check to Time-Of-Use. הרעיון פשוט להפליא: הקוד בודק תנאי מסוים ברגע אחד, ואז מבצע פעולה על סמך התנאי הזה ברגע מאוחר יותר. הבעיה היא שבין הבדיקה לביצוע יש חלון זמן, קטן ככל שיהיה, ובחלון הזה המצב יכול להשתנות.

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

דוגמאות מהעולם האמיתי

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

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

למה זה כל כך קשה לתפוס בבדיקות רגילות

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

למה זה השתנה עם HTTP/2

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

איך מגנים על קוד מפני Race Condition

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

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

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

הצטרפו לקהילה בדיסקורד

לסיכום

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