לקוח שואל את העוזר בתוך המוצר: "מה סטטוס החוזה שלנו?" התשובה נשמעת מדויקת, אבל היא מבוססת על מסמך של לקוח אחר. זו לא תקלה קוסמטית ולא בעיית prompt. זו חשיפת מידע, ובמוצר SaaS היא עלולה להפוך מהר מאוד לאירוע אבטחה ואמון. בדיקת הרשאות בתשובות AI חייבת להיות חלק ממסלול הנתונים עצמו, לא שכבת הגנה שמוסיפים אחרי שהמודל כבר קיבל הקשר.
במערכות RAG, המודל לא אמור "לדעת" את כל המידע. הוא מקבל קטעים שנשלפו ממאגר ידע או ממסמכים, ואז מנסח תשובה. אם מנגנון השליפה מחזיר קטעים שאין למשתמש הרשאה לראות, גם מודל מצוין יענה על בסיס מידע אסור. העובדה שהוא ניסח את המידע בשפה טבעית לא משנה את מקור הכשל.
למה הרשאות נשברות דווקא בתשובות AI
במערכת עסקית רגילה, ההרשאה נבדקת בדרך כלל כשה-API מחזיר רשומה: המשתמש שייך לחשבון מסוים, לתפקיד מסוים או לצוות מסוים, והשרת מאשר או דוחה גישה. במערכת RAG יש שלב נוסף: המסמך מפורק לקטעים, כל קטע מומר לוקטור ונשמר במאגר וקטורי לצורך חיפוש סמנטי.
כאן נוצרת מלכודת נפוצה. צוות הפיתוח מגן היטב על המסמך המקורי, אבל האינדקס הווקטורי נשמר עם metadata חלקי, או בלי metadata שימושי בכלל. החיפוש מחזיר את הקטע "הכי רלוונטי" לשאלה, בלי להבין אם מותר להחזיר אותו למשתמש הספציפי. במילים אחרות, מנוע השליפה עובד היטב מבחינת רלוונטיות ומסוכן מבחינת הרשאה.
בעיה נוספת היא פער בין זהות המשתמש בצד המוצר לבין זהות המשתמש בשרשרת ה-AI. לעיתים ה-frontend שולח `tenantId`, אך שירות ה-retrieval לא מאמת אותו מול ה-session או מול token חתום. לעיתים ערך ההרשאה מגיע מהבקשה עצמה במקום מהשרת. במצב כזה, משתמש יכול תיאורטית לבקש הקשר של חשבון אחר, או שקוד פנימי שגוי יעשה זאת בטעות.
גם cache עלול להפוך לתקלה. אם שומרים תשובה לפי נוסח השאלה בלבד, השאלה "מה המדיניות שלנו לגבי החזרים?" יכולה לקבל תשובה שנוצרה קודם עבור חשבון אחר. Cache צריך להיות תחום לפי זהות, חשבון, תפקיד וגרסת מקורות רלוונטית, או לא להישמר כלל במקרים רגישים.
בדיקת הרשאות בתשובות AI מתחילה לפני ה-prompt
העיקרון הפשוט הוא זה: המודל מקבל רק מידע שהמשתמש מורשה לראות. לא מבקשים ממנו "להתעלם ממידע סודי", ולא מסתמכים על הוראה ב-system prompt שתתקן שליפה לא נכונה. Prompt הוא שכבת התנהגות, לא מנגנון בקרת גישה.
בפועל, אנחנו בונים את מסלול הבקשה כך שהזהות המאומתת מגיעה מהשרת, וממנה נגזר context של הרשאות. context כזה יכול לכלול `tenantId`, מזהי צוותים, תפקיד, מזהי פרויקטים מורשים, וסיווגי מידע. רק לאחר מכן שירות ה-retrieval מרכיב query למאגר הווקטורי עם סינון מחייב.
לדוגמה, אם מסמך שייך לחשבון, לכל קטע באינדקס צריך להיות `tenantId` ברור. אם הגישה תלויה גם בפרויקט, בצוות או בתפקיד, גם המידע הזה צריך להיות חלק מה-metadata או זמין לבדיקת הרשאה לפני החזרת הקטע. הבחירה בין מודל הרשאות פשוט למפורט תלויה במוצר: מערכת שבה כל משתמש רואה את כל חומרי החשבון שונה ממערכת שבה חוזים, דוחות ותיקי לקוחות נחשפים רק לקבוצות מסוימות.
הנקודה הקריטית היא שהסינון מתבצע במקור השליפה. סינון אחרי שהקטעים כבר נשלחו למודל מאוחר מדי. גם אם התשובה הסופית לא כוללת ציטוט מפורש למסמך האסור, המודל כבר השתמש בו ליצירת הטקסט.
לא כל מאגר וקטורי מטפל בהרשאות באותה צורה
יש מאגרים שתומכים ב-filtering בזמן חיפוש, ויש מצבים שבהם הפילטרים מוגבלים, איטיים או קשים לביטוי. לעיתים נכון להפריד אינדקסים לפי tenant, ולעיתים עדיף אינדקס משותף עם פילטרים קשיחים. אין תשובה אחת נכונה.
אינדקס נפרד לכל חשבון מפשט בידוד ומקטין את הסיכון לכשל פילטר, אך יכול להכביד על תפעול, אינדוקס, ניטור ועלויות כשיש מספר גדול של חשבונות. אינדקס משותף מקל על תפעול, אבל דורש משמעת גבוהה סביב metadata, בדיקות וגבולות שאי אפשר לעקוף. ההחלטה צריכה להתבסס על מודל הנתונים, היקף הלקוחות, דרישות אבטחה ודפוסי השימוש בפועל.
metadata הוא חלק ממודל האבטחה
צוותים רבים מתייחסים ל-metadata כפרט טכני של מנוע החיפוש. בפרודקשן, זהו חלק ממודל האבטחה. אם אי אפשר לדעת לאיזה חשבון, פרויקט, מסמך וגרסה שייך כל קטע, אי אפשר להבטיח שליפה מבוקרת וגם קשה מאוד לחקור אירוע.
לכל chunk כדאי לשמור מזהה יציב של מקור המסמך, זהות בעלים או tenant, תחום הרשאה מתאים, סטטוס מסמך וגרסת אינדוקס. כאשר מסמך נמחק או הרשאת משתמש משתנה, צריך לדעת אילו קטעים נדרשים למחיקה, לעדכון או לאינדוקס מחדש. אחרת, מסמך שהוסר מהמערכת הראשית יכול להמשיך להופיע בתשובות שבועות לאחר מכן.
כדאי גם להבחין בין הרשאה לקרוא מסמך לבין הרשאה לקבל תשובה שמבוססת עליו. ברוב המקרים אלו אותן הרשאות, אבל לא תמיד. ייתכן שמשתמש יכול לצפות בכותרת פרויקט אך לא בפרטי תקציב, או לראות מסמך בתצוגה מצומצמת בלבד. מערכת RAG שלא יודעת לייצג את ההבחנה הזו תצטרך לבחור בין תשובות דלות מדי לבין חשיפת יתר.
ציטוטים אינם רק UX, הם כלי בקרה
תשובה עם ציטוטים למקורות מאפשרת למשתמש להבין על מה היא מבוססת. עבור הצוות הטכני, היא עושה יותר מזה: היא מספקת trace שאפשר לבדוק כאשר תשובה שגויה, חסרה או חשודה.
הציטוט צריך להתייחס למקור שהורשה ונשלף בפועל, לא לרשימת מסמכים כללית שהמודל המציא. אנחנו מעדיפים לבנות את הציטוטים בקוד מתוך תוצאת ה-retrieval, ולא לבקש מהמודל להמציא מזהים או קישורים. כך אפשר לוודא שהמקור שייך ל-tenant הנכון, שהמשתמש עדיין מורשה לראות אותו, ושיש התאמה בין התשובה לבין החומר שנשלף.
כאשר אין מקור מספיק טוב או מורשה, התשובה הנכונה היא לא השלמה יצירתית. המערכת צריכה לומר שאין לה מידע זמין או להפנות את המשתמש למסלול תמיכה מתאים בתוך המוצר. זה פחות מרשים בדמו, אבל הרבה יותר אמין בפרודקשן.
איך בודקים שהבידוד באמת עובד
בדיקה ידנית של כמה שאלות אינה מספיקה. הרשאות במערכת AI דורשות evals - סט בדיקות קבוע שמריץ שאלות, זהויות משתמש ומצבי מידע שונים, ובודק שהמערכת מתנהגת כפי שהוגדר. במקרה הזה, evals אינם רק מבחן איכות תשובה אלא גם מבחן אבטחה.
סט בדיקות מועיל כולל לפחות ארבעה סוגי מצבים:
- משתמש מחשבון א' שואל שאלה שהתשובה לה קיימת רק בחשבון ב'.
- משתמש בעל תפקיד מוגבל שואל על מסמך שמותר למנהל לראות בלבד.
- משתמש שהרשאתו הוסרה לאחר שהמסמך כבר אונדקס.
- שאלה כללית מאוד שעלולה להחזיר קטעים רלוונטיים מכמה חשבונות.
בכל תרחיש בודקים לא רק את הטקסט הסופי, אלא גם אילו chunks נשלפו, אילו פילטרים הופעלו, אילו מקורות צוטטו ומה נשמר בלוגים. אם בודקים רק את תשובת המודל, אפשר לפספס מצב שבו נשלף מידע אסור אך הוא לא הופיע הפעם בניסוח הסופי.
חשוב להוסיף בדיקות כאלה ל-CI לפני שינוי ב-retrieval, בסכמת metadata, במנוע הווקטורי או במדיניות cache. שינוי שנראה תמים, למשל החלפת ספריית embedding או אופטימיזציה של query, יכול לעקוף פילטר קיים או לשנות את סדר התוצאות.
לוגים, פרטיות ויכולת חקירה
כדי לחקור תשובה בעייתית צריך לוגים, אבל לוגים עצמם עלולים להכיל מידע רגיש. לכן לא שומרים "הכול" כברירת מחדל. שומרים מזהי בקשה, זהות tenant, מזהי מקורות שנשלפו, גרסאות רכיבים, זמן תגובה והחלטות סינון. את תוכן המסמכים או השאלות המלאות שומרים רק כשיש הצדקה ברורה, מדיניות שמירה מוגדרת וגישה מוגבלת.
יכולת חקירה טובה עונה במהירות על שאלות מעשיות: איזה משתמש ביקש את התשובה, איזה retrieval רץ, אילו מקורות נכנסו ל-context, איזו גרסת אינדקס הייתה פעילה, והאם המסמך היה מורשה באותו רגע. בלי הנתונים האלה, כל בדיקת אירוע הופכת לניחוש.
מתי נדרשת בדיקה מעמיקה יותר
אם המערכת משתמשת בכלים נוספים מעבר ל-RAG, כגון API שמחזיר נתוני לקוחות, יש להחיל את אותו עיקרון גם שם. המודל לא צריך לקבל יכולת כללית לקרוא נתונים ולהחליט בעצמו מה מותר. שכבת השרת צריכה לאמת זהות, להפעיל הרשאות ולהחזיר רק את התוצאה המינימלית הדרושה.
זה נכון במיוחד כאשר יש שיחות מרובות שלבים, מסמכים משותפים בין חשבונות, משתמשים חיצוניים, או הרשאות שמשתנות בתדירות גבוהה. ככל שהמודל יותר גמיש במסלולי הפעולה שלו, כך צריך להיות פחות גמיש בגבולות הגישה לנתונים.
אם יש לכם כבר פיצ'ר AI פעיל ואתם לא יכולים להראות בביטחון אילו מקורות נשלפו לכל תשובה ומדוע המשתמש היה מורשה לראות אותם, זה מקום טוב לעצור ולבדוק את המסלול כולו. ב-DevCo Solutions אנחנו מבצעים audit ממוקד של retrieval, הרשאות, ציטוטים ו-evals כדי להפוך מערכת קיימת לאמינה יותר בפרודקשן. אפשר להתחיל בבדיקת מצב קצרה דרך טופס יצירת הקשר באתר - לפני שהשאלה הבאה תחשוף מידע שלא היה אמור לצאת.
