DCSIMG
Impersonation: מי מפעיל את הפרוצדורה? - גרי רשף

Impersonation: מי מפעיל את הפרוצדורה?

פרוצדורות וסקריפטים אחרים כוללים אופציה לבצע אותם בזהות אחרת בעזרת אופציית Execute As, בעיקר בשל בעיית הרשאות. ננסה לראות איך זה נראה "מבפנים"..
ניצור Login ולו User עם הרשאות חלקיות, בשעה שאנחנו (כלומר- אני, זה שמריץ את הקודים עם ה-Login וה-User שהוגדרו לי) עם הרשאות sa בשרת ו-dbo בדטבייס בו נעבוד (tempdb),
ולמי שלא זוכר- Login הוא אובייקט ברמת השרת ואף הדומיין כולו, ו-User הוא אובייקט ברמת הדטבייס שמייצג בדרך כלל את ה-Login:

Use tempdb;
Go
 
Create Login MyLogin With Password='MyLogin', 
Default_Database=tempdb,Check_Policy=Off;
Go
 
Create User MyUser For Login MyLogin With Default_Schema=MySchema;
Go
 
Exec SP_AddRoleMember 'DB_DDLAdmin', 'MyUser'
Go
 
Create Schema MySchema Authorization MyUser;
Go

הסבר: ה-Login הוא MyLogin, ה-User שלו ב-tempdb הוא MyUser;
ניצור בנוסף סכימה בשם MySchema בדטבייס tempdb,
ו-MyUser יהיה ה-Owner שלה והיא תהיה ברירת המחדל שלו.
לבסוף- ניתן ל-MyUser הרשאות מתאימות ליצור פרוצדורות, ובהמשך נוסיף לו עוד על פי הצורך (לא להתלהב- בשלב זה יש מעט מאוד דברים שהוא יכול לעשות או לראות בדטבייס..).
כעת ניצור טבלה מבלי לתת לו הרשאות:

If Object_ID('T','U') Is Not Null Drop Table T;
Go
 
Create Table T(I Int);
Go
 
Execute As User='MyUser';
Select * From T;
Revert;

image

הטבלה נוצרה, אבל ל-MyUser אין הרשאות לצפות בה (אין בה נתונים אבל זה לא משנה).
השתמשתי ב-Execute As כדי לבצע את הפקודה עם ההרשאות שלו, וחזרתי לעצמי בעזרת Revert.
לא ניתן ל-MyUser הרשאות באופן ישיר, אך ננחם אותו בשלוש פרוצדורות שייגשו בעצמן לטבלה וניתן לו הרשאות להריצן:

-----------------------------------------------------
If Object_ID('P_T1','P') Is Not Null Drop Proc P_T1;
Go
 
Create Proc P_T1 As
Select * From T;
Go
 
Grant Exec On P_T1 To MyUser;
Go
-----------------------------------------------------
If Object_ID('P_T2','P') Is Not Null Drop Proc P_T2;
Go
 
Create Proc P_T2 As
Exec('Select * From T;')
Go
 
Grant Exec On P_T2 To MyUser;
Go
-----------------------------------------------------
If Object_ID('P_T3','P') Is Not Null Drop Proc P_T3;
Go
 
Create Proc P_T3 With Execute As Owner As
Exec('Select * From T;')
Go
 
Grant Exec On P_T3 To MyUser;
Go
-----------------------------------------------------
Exec dbo.P_T1;
Exec dbo.P_T2;
Exec dbo.P_T3;

image

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

Execute As User='MyUser';
Exec dbo.P_T1;
Exec dbo.P_T2;
Exec dbo.P_T3;
Revert;

image

הפרוצדורות הראשונה והשלישית הצליחו, והשניה נכשלה.
הסבר: הראשונה הצליחה מכיוון של-MyUser ניתנה הרשאה להריץ את הפרוצדורה, ובעקיפין ובאופן כללי ניתנו בכך הרשאות לכל האובייקטים שהפרוצדורה מפעילה או ניגשת אליהם (זה לא תמיד כך- כפי שנראה, אבל באופן כללי כן).
הפרוצדורה השניה ביצעה Select באמצעות SQL דינאמי, במקרה זה ההרשאות אינן עוברות לקוד הדינאמי, ומכיוון של-MyUser אין הרשאות ישירות על הטבלה- ה-Select נכשל.
הפרוצדורה השלישית מופעלת עם הרשאות של dbo (הוא ה-Owner של הפרוצדורה כי היא בסכימה dbo שלו), ולכן ה-SQL הדינאמי מצליח כי הוא מתבצע עם הרשאות של dbo.

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

נתחיל את החלק הזה ביצירת שתי פרוצדורות הכוללות Execute As dbo – אחת בכל סכימה, וניתן ל-MyUser הרשאות על זו שב-dbo (על זו שב-MySchema אין צורך לתת כי הןא ה-Owner שלה):

---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('P_Exec_dbo_1','P') Is Not Null Drop Proc P_Exec_dbo_1;
Go
 
Create Proc P_Exec_dbo_1 With Execute As 'dbo' As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Grant Exec On P_Exec_dbo_1 To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('MySchema.P_Exec_dbo_2','P') Is Not Null Drop Proc MySchema.P_Exec_dbo_2;
Go
 
Create Proc MySchema.P_Exec_dbo_2 With Execute As 'dbo' As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
---------------------------------------------------------------------------------------------------------------------------------

כעת נבצע את הראשונה- פעם עם הזהות שלי ופעם עם זו של MyUser;
ואת השניה- שוב, פעם עם הזהות שלי ופעם עם זו של MyUser:

Exec dbo.P_Exec_dbo_1;
 
Execute As User='MyUser';
Exec dbo.P_Exec_dbo_1;
Revert;
 
Exec MySchema.P_Exec_dbo_2;
 
Execute As User='MyUser';
Exec MySchema.P_Exec_dbo_2;
Revert;

image

כפי שניתן לראות- בכל ארבעת המקרים הפלט זהה: המערכת זיהתה את הסכימה של מי שמריץ אותה בתור dbo, את ה-User בתור dbo, ואת ה-Login (של ה-dbo) בתור sa; ללא קשר למי שבאמת מריץ ולסכימה שאליה הפרוצדורה שייכת.
כדאי להזכיר שב-SQL Server המונח dbo הוא גם סכימה וגם User.
מזה אפשר להבין שאם מבצעים Trace על הריצות של הפרוצדורות עלולה להיווצר כאן בעייה בשל ה-Impersonation: כיצד נדע מי באמת הריץ מה? ניתן להגיע לכך באמצעות הפונקציה Original_Login() שמציגה תמיד את ה-Login המקורי בו התחברו למערכת. אני אינני עושה בו כאן שימוש מפני שבכל מקרה הוא יציג את Login_Geri, גם במקרה בו אני מריץ את הפרוצדורה בתור MyUser.
אפשרות אחרת (להרצה בתור dbo) היא לתת לפרוצדורה לרוץ בהקשר (קונטקסט) של הבעלים (Owner) שלה, ובאופן דומה לנ"ל ניצור שתי פרוצדורות ונריץ כל אחת פעמיים- עם כל User בנפרד:

---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('P_Exec_Owner_1','P') Is Not Null Drop Proc P_Exec_Owner_1;
Go
 
Create Proc P_Exec_Owner_1 With Execute As Owner As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Grant Exec On P_Exec_Owner_1 To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('MySchema.P_Exec_Owner_2','P') Is Not Null Drop Proc MySchema.P_Exec_Owner_2;
Go
 
Create Proc MySchema.P_Exec_Owner_2 With Execute As Owner As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
---------------------------------------------------------------------------------------------------------------------------------

image

במקרה זה התוצאות לא תלויות בשאלה מי הריץ באמת: הפרוצדורה זיהתה בכל ארבעת המקרים את ה-User בתור ה-Owner שלה (כלומר- הסכימה לה היא שייכת), את סכימה בתור הסכימה שלה, ואת ה-Login בתור זה של ה-User.
שוב- בפרוצדורה הראשונה dbo הוא גם הסכימה וגם ה-User (שניהם קיימים כברירת מחדל),
ובפרוצדורה השנייה- הסכימה היא MySchema וה-User הוא MyUser (שניהם נוצרו על ידינו בהתחלה).
לסיכום שני המקרים שבדקנו עד כה- פרוצדורה בסכימה dbo תתנהג באופן זהה אם נריץ אותה בתור dbo או בתור ה-Owner מפני שה-Owner הוא dbo. בסכימה MySchema יהיה כמובן הבדל אם נריץ בתור dbo או בתור ה-Owner; אבל בכל מקרה אין זה משנה מי הריץ באמת.
מקרה שלישי שנבדוק הוא הרצה בתור Self, כלומר- בתור זה שיצר את הפרוצדורה. במקרה זה נבדוק 4 פרוצדורות- שתיים בסכימה dbo שאחת נוצרה על ידי והשנייה על ידי MyUser, ושתיים בסכימה MySchema; ואת כל אחת מארבע הפרוצדורות נריץ פעמיים- פעם אחת על ידי ופעם אחת על ידי MyUser:

---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('P_Exec_Self_dbo_Geri','P') Is Not Null Drop Proc 
P_Exec_Self_dbo_Geri;
Go
 
Create Proc P_Exec_Self_dbo_GeriWith Execute As Self As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Grant Exec On P_Exec_Self_dbo_Geri To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
Execute As User='MyUser';
 
If Object_ID('P_Exec_Self_dbo_MyUser','P') Is Not Null Drop Proc P_Exec_Self_dbo_MyUser;
Go
 
Create Proc dbo.P_Exec_Self_dbo_MyUser With Execute As Self As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Revert;
 
Grant Exec On dbo.P_Exec_Self_dbo_MyUser To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('MySchema.P_Exec_Self_MySchema_Geri','P') Is Not Null Drop Proc MySchema.P_Exec_Self_MySchema_Geri;
Go
 
Create Proc MySchema.P_Exec_Self_MySchema_Geri With Execute As Self As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Grant Exec On MySchema.P_Exec_Self_MySchema_Geri To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
Execute As User='MyUser';
 
If Object_ID('MySchema.P_Exec_Self_MySchema_MyUser','P') Is Not Null Drop Proc MySchema.P_Exec_Self_MySchema_MyUser;
Go
 
Create Proc MySchema.P_Exec_Self_MySchema_MyUser With Execute As Self As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
 
Revert;
---------------------------------------------------------------------------------------------------------------------------------
Exec dbo.P_Exec_Self_dbo_Geri;
 
Execute As User='MyUser';
Exec dbo.P_Exec_Self_dbo_Geri;
Revert;
-----------------------------------------------------
Exec dbo.P_Exec_Self_dbo_MyUser;
 
Execute As User='MyUser';
Exec dbo.P_Exec_Self_dbo_MyUser;
Revert;
-----------------------------------------------------
Exec MySchema.P_Exec_Self_MySchema_Geri;
 
Execute As User='MyUser';
Exec MySchema.P_Exec_Self_MySchema_Geri;
Revert;
-----------------------------------------------------
Exec MySchema.P_Exec_Self_MySchema_MyUser;
 
Execute As User='MyUser';
Exec MySchema.P_Exec_Self_MySchema_MyUser;
Revert;
-----------------------------------------------------

image

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

לסיום- יש אופציות נוספות לביצוע Impersonation, נדגים ונסביר:

---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('dbo.P_1','P') Is Not Null Drop Proc dbo.P_1;
Go
 
Create Proc dbo.P_1 As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Go
---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('dbo.P_2','P') Is Not Null Drop Proc dbo.P_2;
Go
 
Create Proc dbo.P_2 With Execute As Caller As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Exec dbo.P_1;
Go
---------------------------------------------------------------------------------------------------------------------------------
If Object_ID('dbo.P_3','P') Is Not Null Drop Proc dbo.P_3;
Go
 
Create Proc dbo.P_3 With Execute As 'MyUser' As
Select Schema_Name() [Schema_Name],Object_Name(@@ProcID) [Proc],Current_User [Current_User],System_User [System_User];
Exec dbo.P_2;
Go
 
Grant Exec On dbo.P_3 To MyUser;
Go
---------------------------------------------------------------------------------------------------------------------------------
Exec P_3;
 
Execute As User='MyUser';
Exec P_3;
Revert;

image

ההפעלה הראשונית היא של הפרוצדורה P_3 שמתבצעת בתור MyUser ללא קשר למי שבאמת הפעיל אותה.
P_3 מפעילה את P_2 שמתבצעת As Caller, כלומר כמי שהפעיל אותה ("מבחינתה"- זה מי ש- P_3 מזהה בתור המפעיל).
P_2 מפעילה את P_1 ללא כל חיווי מפורש לגבי "המפעיל".
כתוצאה מכך בשני המקרים הופעלו שלוש הפרוצדורות על ידי MyUser, וניתן לראות שאם לא מציינים במפורש מי מבצע הרי זה בעצם As Caller.

תוכן התגובה

# Impersonation: ???? ?????????? ???? ??????????????????? « ?????????? ???? ?????? ??????

Pingback from  Impersonation: ???? ?????????? ???? ??????????????????? « ?????????? ???? ?????? ??????

שלח תגובה

(שדה חובה) 
(שדה חובה) 
(אופציונלי)
(שדה חובה) 

Enter the numbers above: