Row Level Security: אכיפת הרשאות ברמת מסד הנתונים, לא רק בקוד

מאת צוות מדיה דיל · 03.09.2026 · אבטחת מידע · 6 דק׳

USING מול WITH CHECK, מקור ההקשר לזיהוי משתמש, בידוד Multi-Tenant דרך Policies, מחיר ביצועים, וסכנת BYPASSRLS שקל לשכוח.

כל אפליקציה שבודקת "האם המשתמש הזה מורשה לראות את השורה הזו" רק בקוד השרת חשופה לטעות אנוש אחת — Endpoint חדש, Query גולמי, או תיקון דחוף שמפספס את הבדיקה. Row Level Security (RLS) מעביר את האכיפה למסד הנתונים עצמו, כך שגם שאילתה שנכתבה בטעות בלי סינון פשוט לא תחזיר שורות שאסור לה לראות.

מה RLS בעצם עושה

כשמפעילים RLS על טבלה, Postgres מוסיף אוטומטית תנאי סינון לכל שאילתה שמגיעה מ-Role שלא Bypass — SELECT, UPDATE ו-DELETE כולם מסוננים דרך Policy שהוגדרה מראש, בלי שהאפליקציה צריכה לזכור להוסיף WHERE tenant_id = ... בכל שאילתה בקוד. זו אכיפה ברמת המסד, לא המלצה שקל לשכוח ליישם.

USING מול WITH CHECK: שני כיוונים שונים

הביטוי ב-USING קובע אילו שורות קיימות נראות ל-Role בכלל (SELECT, UPDATE, DELETE) — שורה שלא עומדת בתנאי פשוט לא מוחזרת, כאילו לא קיימת. WITH CHECK קובע אילו שורות מותר ליצור או לעדכן אליהן — ניסיון לכתוב שורה שלא עומדת בתנאי נכשל בשגיאה. חשוב להגדיר את שניהם בנפרד: אפשר לראות שורה אבל לא להרשות לשנות אותה לערך שיוצא מהתחום המותר.

מקור ההקשר: מי בעצם המשתמש הנוכחי

ה-Policy צריך לדעת מי מבצע את השאילתה — נעשה בדרך כלל דרך current_setting() עם משתנה session שהאפליקציה מגדירה בתחילת כל בקשה, או דרך auth.uid() בסביבת Supabase שמזהה את המשתמש המחובר אוטומטית מתוך ה-JWT. טעות נפוצה היא לסמוך על Connection Pooling משותף בלי לאפס נכון את משתני ה-Session בין בקשות של משתמשים שונים.

RLS כבידוד Multi-Tenant אמיתי

אחד השימושים הנפוצים והחזקים ביותר ב-RLS הוא בידוד דיירים ב-Multi-Tenant: Policy יחיד שמגביל כל שורה ל-tenant_id הנוכחי הופך את הבידוד לעובדה מובנית במסד, לא הסכם שהקוד צריך לכבד בכל מקום. ראו ארכיטקטורת Multi-Tenant להשוואה בין המודל הזה (Shared Schema עם RLS) לחלופות אחרות של בידוד.

המחיר: Policies יקרות שרצות על כל שורה

Policy שמבצע Subquery יקר (למשל בדיקה מול טבלת הרשאות נפרדת) רץ בפועל על כל שורה מועמדת בכל שאילתה, ויכול להפוך שאילתה שהיתה אמורה להיות מהירה לאיטית משמעותית. הפתרון בדרך כלל הוא לפשט את תנאי ה-Policy ככל האפשר ולוודא שיש אינדקס תומך על העמודה שה-Policy מסנן לפיה, בדיוק כמו על כל תנאי WHERE אחר.

BYPASSRLS: החריג המסוכן שקל לשכוח

Role עם הרשאת BYPASSRLS (כמו superuser או תפקידי ניהול פנימיים) עוקף לחלוטין את כל ה-Policies — כולל בטעות, אם חיבור לאפליקציה משתמש בתפקיד הלא נכון. ביקורת תקופתית של אילו Roles מוגדרים כ-BYPASSRLS, ושה-Connection String של האפליקציה עצמה לא משתמש באחד מהם, היא בדיקת אבטחה בסיסית שקל להזניח.

RLS הוא שכבה, לא הפתרון היחיד

RLS מונע דליפת נתונים ברמת השורה, אבל הוא לא מחליף בקרות ברמת Application — הרשאות פעולה (מי יכול ליצור הזמנה), ולידציה עסקית, וניהול הרשאות ברמת API עדיין נדרשים בקוד. העיקרון תואם את הגישה שמתוארת בארכיטקטורת Zero Trust — כל שכבה מאמתת בעצמה, בלי לסמוך שהשכבה הקודמת כבר סיננה נכון.

מלכודת נפוצה: לשכוח להפעיל RLS על טבלה חדשה

RLS לא פעיל כברירת מחדל על טבלה חדשה — צריך ENABLE ROW LEVEL SECURITY מפורש, ובלי Policy מוגדר בכלל, ברירת המחדל היא לחסום הכל (Deny by Default) ל-Roles רגילים. הסיכון האמיתי הוא בכיוון ההפוך: טבלה שנוצרה בלי RLS מופעל כלל, ונשארת פתוחה לחלוטין עד שמישהו שם לב.

בונים מערכת Multi-Tenant וצריכים בידוד נתונים אמיתי ברמת המסד? נשמח לעזור לכם לתכנן את שכבת ה-RLS בוואטסאפ.

תגיות: Row Level Security · RLS · Postgres · אבטחת מידע · Multi-Tenant

← חזרה לבלוג · צור קשר