vLLM ו-trust-remote-code: נעילת גרסה, מפתח API ובדיקת קוד לא עצרו בקשה אחת
אם אתה מריץ vLLM על שרת משלך ומגיש מודל מולטימודלי עם --trust-remote-code, סביר שאתה מסתמך על שלוש הגנות. בדקת את קוד ה-processor לפני שהפעלת את הדגל. נעלת את גרסת המודל (revision) כדי שהקוד לא ישתנה בלי ידיעתך. הגדרת --api-key כדי שרק מי שאישרת יוכל לשלוח בקשות. הודעת אבטחה חדשה של פרויקט vLLM מתארת מצב שבו, בתצורה מסוימת עם --trust-remote-code, בקשה אחת לנתיב /tokenize יכולה לקבוע איזה קוד Python ירוץ בשרת. הנתיב הזה אינו מוגן במפתח ה-API.
הטענה שלנו היא שכל אחת מההגנות האלה נאכפת בתוך האפליקציה, לפי שדה או נתיב, ונשענה על הנחה שלא עמדה במבחן. המסקנה המעשית היא לעדכן, ולצד העדכון לקבוע ברמת המארח מי יכול להגיע לשרת ההסקה ולאילו יעדים השרת יכול להתחבר. שתי ההחלטות האלה אינן תלויות באופן שבו vLLM מפרש בקשה.
מה פורסם
ב-6 באוקטובר 2026 פורסמה הודעת האבטחה GHSA-h3rc-6mm3-gc2m. לפי ההודעה, בקשה מולטימודלית יכולה לקבוע את הערך code_revision בתוך השדה mm_processor_kwargs. כשה-processor של המודל ב-Hugging Face מוגדר כקוד דינמי (auto_map) והמפעיל הפעיל --trust-remote-code, הערך הזה מועבר ל-AutoProcessor.from_pretrained של Transformers. הוא קובע מאיזו גרסה של המאגר ייובא קוד ה-processor. ייבוא מודול Python פירושו הרצת קוד שרירותי בתהליך ה-API/renderer של vLLM.
עיקרי ההודעה:
- גרסאות: מ-0.7.3 ועד לפני 0.31.0. התיקון נכלל ב-0.31.0 ומוזג ב-pull request מספר 58830. ההודעה מציינת שהפגיעות אומתה על commit מסוים עם Transformers 5.15.0 ו-5.16.1, ושטווח הגרסאות המושפעות לא אומת בחיפוש בינארי (bisect).
- תנאים: processor דינמי דרך
auto_map;trust_remote_codeמופעל; השרת יכול להוריד את הגרסה שנבחרה בבקשה, או שהיא כבר במטמון; התוקף יכול ליצור גרסה במאגר המודל המוגש או לשלוט בה — לפי ההודעה, ref של pull request קהילתי ב-Hugging Face הוא דוגמה לכך, כשהמאגר מאפשר זאת; ובקשה מולטימודלית מגיעה למסלול ה-processor. בתצורת ברירת המחדל,trust_remote_code=False, השרת אינו מושפע. - הזדהות: לפי ההודעה, רכיב ההזדהות של vLLM מגן רק על נתיבים שמתחילים ב-
/v1,/v2,/inferenceו-/cohere. השדה מתקבל גם בבקשת tokenize של צ'אט, ולכן/tokenizeנשאר פתוח גם כשהוגדר--api-key, אלא אם proxy חיצוני מגן עליו. - חומרה: המדווח הציע ציון 9.8, אך הדירוג שפורסם הוא High עם CVSS של 8.1 ומורכבות תקיפה גבוהה. אין מזהה CVE. הסיווג הוא CWE-94 ו-CWE-829.
- הדגמה: לפי ההודעה, ההדגמה הורצה ללא חיבור לרשת, בלי GPU, בלי משקולות מודל ובלי לשנות מאגר אמיתי. ההודעה אינה מתארת ניצול בפועל.
בתקציר הסיקור של LinuxSecurity, העדכון מתואר ככולל תיקונים קריטיים. עם זאת, הדירוג שפורסם בהודעה עצמה הוא High.
נעילת גרסה נועלת נתונים, לא קוד
מי שנועל revision מניח שהוא נועל את כל מה שהמודל מביא איתו. לפי ההודעה, vLLM אכן נעל את revision ואת trust_remote_code, אבל העביר את שאר ההגדרות מהבקשה בלי להגן על code_revision. Transformers משתמשת בערך הזה כדי לבחור את הקוד, ומסירה אותו לפני טעינת נתוני ה-processor. התוצאה, כפי שהיא מתוארת בהודעה: הקוד מגיע מהגרסה שבשליטת התוקף, ואילו התצורה והנתונים נשארים בגרסה שהמפעיל נעל. אם בדקת את הקוד לפני שהפעלת את הדגל, בדקת גרסה אחת. בפגיעות הזו רצה גרסה אחרת.
לכן --trust-remote-code אינו רק הגדרה טכנית שמפעילים כדי שמודל יעבוד. הפעלתו היא החלטה להריץ קוד ממקור חיצוני. השאלה המכרעת היא מי בוחר את הקוד. בגרסה הפגיעה, הבקשה קבעה זאת.
מפתח API מגן על קידומות, לא על השרת
קל להניח שהגדרת --api-key חוסמת את הגישה לשרת בפני מי שאין לו מפתח. לפי ההודעה, ההגנה מוגדרת לפי קידומות נתיב, ו-/tokenize אינו נכלל בהן. גם מדריך האבטחה של vLLM מציין אותו כנתיב לא מוגן. לכן, בשרת vLLM שנגיש ברשת, לא די לשאול אם הגדרת מפתח. צריך לבדוק אילו נתיבים נגישים בלי מפתח ומה כל אחד מהם מעבד.
לכן חשוב יותר לבדוק את החשיפה של השירות כולו מאשר את ההגדרה של נתיב בודד. אם רק השירותים שזקוקים ל-vLLM יכולים להגיע לפורט שלו, המשמעות של נתיב שנותר מחוץ למנגנון ההזדהות מצטמצמת מאוד.
היכולת להוריד היא חלק מהתקיפה
אחד התנאים בהודעה הוא שהשרת יוכל להוריד את הגרסה שנבחרה בבקשה, או שהיא כבר תהיה במטמון. שרת שהמודל שלו הוכן מראש לא בהכרח צריך לפנות למאגר בזמן ריצה. אפשר לחסום את הגישה לכל יעד שאינו נחוץ לו. חשוב לזכור: אם אתה משאיר גישה ל-Hugging Face עצמו, הגרסה שבשליטת התוקף נמצאת באותו מאגר. לכן העדכון ל-0.31.0 נשאר התיקון.
כדאי לבדוק גם את התנאי הנוגע למאגר. לפי ההודעה, ref של pull request קהילתי הוא דוגמה לגרסה שתוקף יכול לשלוט בה, במאגר שמאפשר זאת. במובן הזה, מאגר ציבורי שפתוח לתרומות הוא מקור קוד שאינו בשליטתך המלאה.
הקו המשותף
בשלושת המקרים, ההגנה נאכפה במקום שבו האפליקציה מפרשת קלט: שדה אחד שלא הוגן, נתיב אחד שלא נכלל. הגנות כאלה תלויות בכך שהמפתח חשב על כל שדה ועל כל נתיב. לעומתן, שתי שאלות ברמת המארח אינן משתנות גם כשמתגלה שדה חדש: מי יכול להגיע לפורט של שרת ההסקה, ולאילו יעדים התהליך שלו יכול להתחבר. לפי ההודעה, הקוד רץ בתהליך ה-API/renderer ופוגע בסודיות, בשלמות ובזמינות.
מה לעשות בשרת
- עדכן ל-0.31.0. בדוק את הגרסה בתהליך שמקבל את הבקשות בפועל, לא רק את התג של ה-image שהורדת. קונטיינר שכבר רץ אינו מתעדכן מעצמו.
- בדוק אם אתה צריך את
--trust-remote-code. אם המודל אינו דורש אותו, כבה אותו. אם הוא דורש אותו, בדוק אם ה-processor שלו מוגדר כקוד דינמי דרךauto_map. - בדוק את המאגר של המודל המוגש. האם הוא מאפשר pull requests קהילתיים? מי יכול ליצור בו גרסאות?
- הגבל את הגישה לשרת ההסקה מהרשת. אל תחשוף את vLLM ישירות לאינטרנט. אפשר גישה רק לשירותים שזקוקים לו. אם יש proxy לפניו, ודא שהוא דורש הזדהות גם ב-
/tokenize. בדוק זאת בבקשה נפרדת לנתיב הזה. - מפה את התעבורה היוצאת של שרת ההסקה. בדוק לאילו יעדים הוא מתחבר בזמן ריצה, והגבל את הגישה ליעדים שהוא באמת צריך.
- בדוק מה עוד רץ. קל לפספס שרת מודלים שהוקם לבדיקה ונשאר פעיל בגרסה ישנה, עם הדגל מופעל.
איך nuDefend עוזר
לדעת מה רץ. הפקודה nudefend workloads מציגה את כלי ה-AI שפועלים בשרת, ובהם שרתי מודלים, בין שהם רצים ישירות על המארח ובין שהם רצים בתוך קונטיינר. לצד כל כלי מוצג המידע שעליו התבסס הזיהוי, למשל יחידת systemd, שם image או דפוס בשורת הפקודה. כך אפשר למצוא גם שרת מודלים שהוקם לבדיקה ונשכח.
לדעת מי יכול להגיע אליו. הפקודה nudefend expose מציגה כל שירות שמאזין בשרת ומי יכול להגיע אליו, מנקודת המבט של השרת עצמו. התצוגה כוללת גם שירותים שמפורסמים מתוך קונטיינר. אם שירות שהיה פנימי בלבד נעשה נגיש מבחוץ, nuDefend מזהה את השינוי ומתריע עליו. אם שרת ההסקה חשוף בניגוד לכוונתך, אפשר לחסום את הפורט שלו מהדאשבורד או משורת הפקודה. לפני החסימה מוצגת תצוגה מקדימה שמראה בדיוק מה ייחסם. כל חסימה מתועדת וניתנת לביטול בלחיצה. פרטים בעמוד על החשיפה.
לשלוט לאן הוא מתחבר. nuDefend מתעד לאילו יעדים כל כלי AI בשרת מתחבר, את היקף התעבורה לכל יעד ואת המועד שבו היעד זוהה לראשונה. כשהיעד מזוהה, מוצגים שמו וסוג הפעילות שלו, למשל מקור להורדת תוכנה ומודלים. על סמך המידע הזה אפשר להגביל כלי שבחרת ליעדים שאישרת. כל ניסיון להתחבר ליעד אחר נחסם לפני שהתעבורה יוצאת מהשרת, ושאר השרת ממשיך לפעול כרגיל. nuDefend מגביל גם את הכתובות שהכלי רשאי לברר, וכל בירור שנעצר מוצג לצד שם הכלי. פרטים בעמוד על התעבורה היוצאת.
שכבת בסיס. תעבורה אל כתובות IP ודומיינים הידועים כזדוניים, וגם תעבורה שמגיעה מהם, נחסמת בחומת האש של הליבה. החסימה נעשית לפי רשימות איומים חתומות שמתרעננות כל 30 דקות. בדאשבורד מוצגת ספירה של תעבורה יוצאת שנחסמה. ערך מעל אפס עשוי להיות אזהרה מוקדמת לכך שמשהו בשרת ניסה ליצור קשר עם מקור זדוני.
השורה התחתונה
הפגיעות ב-vLLM מראה ששלוש הגנות סבירות — נעילת גרסה, מפתח API ובדיקת קוד — נאכפו כולן בתוך האפליקציה ונעקפו באמצעות שדה אחד ונתיב אחד. עדכן ל-0.31.0 וכבה את --trust-remote-code אם אינך צריך אותו. לצד העדכון, קבע ברמת המארח מי יכול להגיע לשרת ההסקה ולאילו יעדים השרת יכול להתחבר. ההחלטות האלה נשארות בתוקף גם כשיתגלה השדה הבא.
nuDefend מציג לאילו יעדים שרת המודלים שלך מתחבר ומאפשר להגביל כלי שבחרת ליעדים שאישרת. כל ניסיון להתחבר ליעד אחר נחסם לפני שהתעבורה יוצאת מהשרת.