היעדר בדיקת אבטחה בנקודת קצה (endpoint) של אוסף (collection) מסוג GET אפשר לכל בעל חשבון CoopCycle בסיסי למשוך את ספר הכתובות המלא של כל חנות במופע (instance) משותף, ובכך לחשוף שמות, כתובות רחוב ומיקודים של לקוחות רבים. הפרצה תוקנה תוך יומיים, ומשתמשים נקראים לשדרג לגרסה האחרונה ששוחררה.

כיצד אירעה הדליפה

CoopCycle – פלטפורמת לוגיסטיקה בקוד פתוח המשמשת קואופרטיבים למשלוחי מזון – מגדירה את ה-API שלה באמצעות ה-framework של PHP בשם API Platform. ב-framework זה, כל פעולה (POST, GET וכו') חייבת להיות מצומדת לביטוי אבטחה (security expression); אם הביטוי אינו מופיע, ה-framework מריץ את הקוד ללא כל בדיקת הרשאה (authorization check).

המפתחים הגנו על בקשת ה-POST שיוצרת או מעדכנת את רשימת הכתובות של חנות באמצעות הביטוי הסטנדרטי is_granted('edit', object). זה עובד מכיוון שהבקשה מכוונת לישות (entity) של חנות בודדת, מה שנותן ל-framework "אובייקט" מוחשי להערכה.

בקשת ה-GET שקוראת את אותו משאב מכוונת לאוסף: /api/stores/{id}/addresses. לאוסף אין אובייקט בודד, ולכן לא ניתן להחיל עליו את אותו ביטוי is_granted('edit', object). מכיוון שהמפתחים השמיטו את שורת האבטחה, ה-framework סיפק את נתוני הכתובות לכל משתמש מחובר, ללא קשר לשייכותו (tenancy).

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

מדוע הבאג שרד

הבעיה לא הייתה מעידה פשוטה. למודל האבטחה הדקלרטיבי של API Platform חסרה דרך ישירה לבטא ש"המשתמש חייב להיות שייך לאותו tenant כמו כל אובייקט באוסף". שורת הקוד החסרה שכנה בדיוק במקום שבו ה-framework הפך את נושא ההרשאות למסורבל.

מה שהחמיר את הבעיה הוא שסדרת הבדיקות (test suite) של הפרויקט למעשה קבעה שהתגובה של ה-GET, המכילה את כל הכתובות, היא ההתנהגות המצופה. במילים אחרות, הבדיקות האוטומטיות עברו כי ה-fixtures ששימשו לבדיקה אפשרו גישה חוצת-tenants, ובכך הסתירו בפועל את הפגיעות. סדרת בדיקות "ירוקה" (עוברת) במקרה זה, נתנה תחושת ביטחון כוזבת.

מי מרוויח ומי מפסיד

  • לקוחות: המידע המאפשר זיהוי אישי (PII) שלהם – שמות מלאים וכתובות מגורים – נחשף לכל משתמש בפלטפורמה. למרות שהנתונים לא פורסמו לציבור, הפריצה פגעה בפרטיות במספר קואופרטיבים.
  • קואופרטיבים המשתמשים ב-CoopCycle: האמון ביכולת הפלטפורמה להגן על נתוני הלקוחות (tenant data) נערער. כל קואופרטיב שטרם שדרג עמד בפני הסיכון לחשיפה מתמשכת.
  • מנהלי (maintainers) CoopCycle: התגובה המהירה שלהם – תיקון תוך יומיים והוספת בדיקות רגרסיה – הגבילה את חלון הניצול והוכיחה ניהול אחראי של קוד פתוח. עם זאת, האירוע מדגיש את הצורך בתהליכי סקירת אבטחה הדוקים יותר, במיוחד סביב הגדרות ברירת מחדל המונעות על ידי framework.

מה מפתחים ומבקרים צריכים לחפש

  • אסימטריה בפעולות: אם פעולת POST (או כל פעולה משנה אחרת) בנתיב מסוים מוגנת, אך ה-GET המקביל פתוח, הפער הזה הוא נורת אזהרה. ה-POST חושף את כוונת המפתחים להגן על המשאב.
  • נקודות קצה של אוספים (Collection endpoints): כל דבר שמחזיר רשימה במקום פריט בודד נוטה לעיתים קרובות לחרוג מדפוסי האבטחה הרגילים. ודאו שבודקות הרשאה מוספות במפורש לקריאות מרובות (bulk reads).
  • ריאליזם של סדרת הבדיקות (Test suite realism): ודאו שה-fixtures משקפים את גבולות ה-tenancy האמיתיים. בדיקה שעוברת ומאמתת דליפת נתונים חוצת-tenants היא סימן אזהרה, לא אור ירוק.

התיקון והצעדים הבאים

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

משתמשי CoopCycle צריכים:

  1. לוודא שהם מריצים גרסה עדכנית של התוכנה.
  2. לבחון הרחבות (extensions) או תוספים (plugins) מותאמים אישית שעלולים להוביל לפערים דומים ברמת האוסף.
  3. להריץ מחדש סריקות אבטחה תוך התמקדות באסימטריה בין קריאות כתיבה לקריאה בכל נתיבי ה-API.

שורה תחתונה

פריימוורקים שהופכים את האבטחה להצהרתית עלולים להסתיר פערים מסוכנים כאשר מפתחים מסתמכים על תבניות שעובדות רק עבור אובייקטים בודדים. בדיקה פשוטה — האם לצד הקריאה של endpoint יש את אותה הגנה כמו לצד הכתיבה? — יכולה לחשוף סוג של דליפות cross-tenant שבכל מקרה אחר היו נשארות חבויות מאחורי סדרות בדיקות שעוברות בהצלחה.