הפעלה של ג'ובים או של פרוצדורות מערכת שפועלות מול ה-File System (המחיצות והקבצים) הן בעייתיות מכיוון שמי שמבצע את הפקודות מזדהה בתור ה-Agent של ה-SQL Server ולא בתור המשתמש הספציפי שהריץ את הפקודה, וכך המשתמש מקבל הרשאות חריגות שעלולות להיות מנוצלות לרעה: הוא יכול להגיע למחיצות וקבצים שאין לו הרשאה להגיע אליהם או לשנותם, הוא יכול להריץ פקודות SQL בעזרת SQLCmd ולעשות ככל העולה על רוחו.
הפתרון לבעייה הוא ליצור Proxy Account כך שהכלים הנ"ל יזדהו איתו ויקבלו את ההרשאות של ה-Credential המשוייך אליו, הרשאות שניתן להגביל אותן בהתאם לצורך.
הפעלת ג'ובים
הדוגמה להלן מניחה שהשרת מוגדר ב-Properties => Security בתור SQL Server and Windows Authentication mode.
1. יצרתי Login בשם Try001 עם SQL Server Authentication וסיסמה 1234 (הורדתי את הסימונים בתיבות לגבי ה-Policy),
וב-User Mapping שייכתי אותו ל-msdb כדי שיוכל בהמשך לקבל הרשאות להפעלת ג'ובים.
2. יצרתי ג'וב ששוייך ל-Try001 הנ"ל (Owner) ובו צעד אחד מסוג (Operating System (CmdExec והפקודה Dir C:\ > C:\Try001.txt (פקודה פשוטה שמריצה פקודת Dir ומפנה את התוצאה לקובץ Try001.txt).
בסעיף Run As כתוב SQL Agent Service Account, כלומר- הפקודה מתוכננת להיות מופעלת על ידי ה-Agent עם ההרשאות שלו.
3. ב-SSMS פתחתי New Query ושיניתי את ה-Connection על ידי קליק ימני ל-Try001 עם הסיסמה הנ"ל (1234).
4. ניסיתי ב-Query החדש שפתחתי להפעיל את הג'וב:
Exec msdb..sp_start_job 'Job001';
ההפעלה נכשלה בשל העדר הרשאות Exec של משתמש Try001 לפרוצדורה sp_start_job.
5. נתתי למשתמש הנ"ל הרשאות מתאימות ב-Query "שלי" (לא זה שפתחתי בסעיף 3 עבור Try001):
Use msdb;
Go
Grant Execute On Object::sp_start_job To Try001;
Go
6. כעת ההפעלה של הג'וב על ידי המשתמש Try001 הצליחה, אבל הג'וב עצמו נכשל (ניתן לראות זאת על ידי קליק ימני על הג'וב ו-History) מכיוון שה-Owner שלו הוא Try001 ואינו יכול להפעיל פקודות שמורצות על ידי ה-Agent. גם אם אפיל אותו עם הרשאות ה-Admin שלי הוא יכשל בגלל ה-Owner.
כדי לפתור את הבעייה יש להגדיר Credential שיזהה משתמש עם הרשאות מתאימות, וחשבון Proxy שבעזרתו יופעל הצעד של הג'וב ושההרשאות שלו הן של ה-Credential שישוייך לו.
7. יצרתי Credential (תחת Security) בשם Cred001 עם המשתמש שלי (ניתן לראות אותו על ידי Select System_User) והסיסמה שלי.
8. יצרתי Proxy (תחת SQL Server Agent) מסוג (Operating System (CmdExec ששמו Proxy001,
ציינתי את Cred001 ב-General,
ואת Try001 ב-Principals (בהמשך נוכל לראות את הג'וב Job001 ב-References).
9. בג'וב, בצעד שהגדרתי בו, ציינתי את Proxy001 ב-Run As (במקום AQL Agent Service Account).
10. הפעלתי שוב את הג'וב בתור Try001 והוא הופעל ורץ בהצלחה (כדאי לוודא שהקובץ Try001.txt באמת נוצר).
הפעלה של xp_CmdShell
הפרוצדורה הזו מאפשרת להריץ פקודות שורה (Command Lines) מתוך ה-SQL Server, פקודה שימושית מאוד אך גם מסוכנת!
1. מצב ברירת המחדל הוא שלא ניתן להפעיל את הפקודה, כל זאת כדי לא ליצור פירצה הקוראת לגנב. כל עוד האדמיניסטרטור לא איפשר להשתמש בה במפורש- היא חסומה.
ניתן לאפשר אותה כך:
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE --RECONFIGURE WITH OVERRIDE אם זה לא עובד אז לנסות
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE --RECONFIGURE WITH OVERRIDE אם זה לא עובד אז לנסות
GO
2. בתור אדמיניסטרטור לא אמורה להיות בעייה להריץ כעת פקודה בסגנון של:
Exec xp_CmdShell 'Dir C:\ > C:\Try001.txt';
Go
אולם את ננסה להריץ אותה בתור Try001 ב-Query שלו, נקבל הודעת שגיאה על העדר הרשאות על xp_CmdShell.
ניתן לו אותן:
Use master;
Go
Create User Try001 For Login Try001;
Go
Grant Execute On Object::xp_CmdShell To Try001;
Go
3. כעת ההפעלה נכשלת מכיוון של-Try001 שאינו אדמיניסטרטור אין הרשאה לפקודות מול מערכת ההפעלה מהסיבות שמניתי קודם, ומתקבלת הודעת שגיאה:
Msg 15153, Level 16, State 1, Procedure xp_cmdshell, Line 1
The xp_cmdshell proxy account information cannot be retrieved or is invalid. Verify that the '##xp_cmdshell_proxy_account##' credential exists and contains valid information.
4. כדי לאפשר להפעיל את הפקודה עם Proxy שמתאים לה ספציפית, נריץ בתור אדמיניסטרטורים את הפקודה הבאה:
Exec master..sp_xp_CmdShell_Proxy_Account 'MyLogin','MyPassword';
Go
את MyLogin יש להחליף ב-Login עם ההרשאות המתאימות מול מערכת ההפעלה. שלנו, למשל, יתקבל על ידי Select SYSTEM_USER.
כעת הפעלת xp_CmdShell על ידי Try001 אמורה להצליח.
5. אם מתחרטים ורוצים לבטל את שיוך ה-Proxy לפקודה נפעיל את הפקודה:
Exec master..sp_xp_CmdShell_Proxy_Account Null;
Go