הפוסט הזה הוא בהמשך לקודם שסיכם את ההרצאה ב-ISUG. בין היתר הוצע שם פתרון לבעיית ההרשאות בפרוצדורות הכוללות קוד דינמי (שאינו מקבל בירושה את ההרשאה לפרוצדורה) בעזרת Execute As Owner או כל הרשאה אחרת שתאפשר את הקוד הדינמי. כמקובל בעולמנו – לפתרון הזה יש חסרונות משלו, למשל – המשתמש מאבד את זהותו וכעת המערכת תזהה אותו בתור dbo או מישהו אחר, וזה יאפשר לו להגיע למידע שהוא לא אמור היה להגיע אליו. דוגמה שתבהיר את הבעייה המקורית, את הפתרון, את הבעייה בפתרון, ואת הפתרון לבעייה בפתרון.. נתחיל מליצור Login ו-User מעוטי הרשאות להמחשה: --Mixed mode!Use tempdb;Go If Exists (Select...
אצל רבים המונח SQL דינאמי מתקשר אוטומטית עם SQL Injection וכיצד ניתן לנצל זאת כדי "לדחוף" כל מיני תנאים לא צפויים שיאפשרו לשלוף מידע חסוי ואף לשנות מידע קיים. האזהרות כבודן במקומם, אך ל-SQL דינאמי יש יתרונות ושימושים רבים שכדאי להכיר, ולבעיות האבטחה יש פתרונות. נכון- גם לפתרונות קיימים מעקפים ומחבל זדוני ימצא תמיד פירצה דרכה ניתן להזיק, וזה נכון לא רק ל-SQL דינאמי אלא לכל מערכת שהיא. עד כאן ההתנצלויות לגבי "אבל יש סכנה של SQL Injection.." ומכאן למלאכה. נתחיל מ"איך" ולא מ"למה": יש שתי פונקציות עיקריות שמאפשרות לנו להריץ...
אצל רבים המונח SQL דינאמי מתקשר אוטומטית עם SQL Injection וכיצד ניתן לנצל זאת כדי "לדחוף" כל מיני תנאים לא צפויים שיאפשרו לשלוף מידע חסוי ואף לשנות מידע קיים. האזהרות כבודן במקומם, אך ל-SQL דינאמי יש יתרונות ושימושים רבים שכדאי להכיר, ולבעיות האבטחה יש פתרונות. נכון- גם לפתרונות קיימים מעקפים ומחבל זדוני ימצא תמיד פירצה דרכה ניתן להזיק, וזה נכון לא רק ל-SQL דינאמי אלא לכל מערכת שהיא. עד כאן ההתנצלויות לגבי "אבל יש סכנה של SQL Injection.." ומכאן למלאכה. נתחיל מ"איך" ולא מ"למה": יש שתי פונקציות עיקריות שמאפשרות לנו להריץ...
נערוך בוחן פתע – אסור להציץ ואסור לנסות על המחשב, רק לחשוב ולומר מה יהיה הפלט של השליפה הבאה: Select Case When 'abc'='abc ' Then 1 Else 0 End;
אני, אגב, נכשלתי: הייתי בטוח שהתשובה היא 0 שכן ברור שהמחרוזות אינן זהות ולשנייה יש כמה רווחים עודפים בסוף.
אופס – גיליתי לכם את התשובה, אבל אני בטוח שכל הקוראים ידעו את התשובה הנכונה (כאמור- חוץ ממני)..
הבעייה אינה נקודתית לשליפה הטכנית הנ"ל אלא לכל ההשוואות בין המחרוזות, למשל:
Select *
...
אלו מביננו שמעבירים כרטיס בשוכבם ובקומם, בהגיעם ובלכתם מכירים את הסיטואציה: הגענו לעבודה, איננו זוכרים אם בדרך העברנו כרטיס או לא, ולעיתים אנחנו מעבירים שוב ליתר בטחון; או עמדנו לצאת, העברנו כרטיס, אבל פתאום נקראנו בחזרה וכעת צריך להעביר כרטיס שוב כשאנחנו יוצאים "באמת". בהנחה שהמערכת מבחינה בין יציאות וכניסות – יש לבחור בכניסה הראשונה כשיש רצף של מספר כניסות, ובאחרונה כשיש רצף של מספר יציאות. ל-מ-ש-ל: If Object_ID('tempdb..#T','U') Is Not Null Drop Table #T;
Create Table #T(ID Int,
...
נתונה לנו טבלה בה נתוני כניסה ויציאה של עובדים, כשאנחנו מניחים לשם הפשטות שאיש לא שכח להעביר כרטיס (או לחילופין- שהטבלה טוייבה ואין בה חוסרים): Use tempdb;
Go
If Object_ID('T_Nohehut','U') Is Not Null Drop Table T_Nohehut;
Create Table T_Nohehut(Oved Int Not Null,
...
יש טבלה, בטבלה עמודת DateTime עם תאריכים לא שלמים (כוללים חלקי יממה), ואנחנו מחפשים את כל התאריכים בתוך יום מסויים. מה הדרך היעילה לעשות זאת? If Object_ID('tempdb..#Tmp','U') Is Not Null Drop Table #Tmp;
Go
Select *,
DateAdd(Millisecond,Cast(is_event_logged As Int),DateAdd(Second,severity,DateAdd(Minute,language_id,DateAdd(Month,message_id,0)))) Taarih
Into #Tmp
From sys.messages;
Go
Create...
יש לנו סביבת פיתוח וסביבת טסטים, או אולי דטבייס מעודכן ודטביס מגיבוי, ואנחנו מעוניינים להשוות בין הטבלאות והעמודות בשני הדטבייסים. ניצור קודם כל שני דטבייסים להמחשה, ובהם טבלאות: T1 קיימת בראשון אך לא בשני, T2 קיימת בשני אך לא בראשון, T3 קיימת בשניהם אך עמודה I1 רק בראשון, I2 רק בשני, ו-I3 בשניהם, T4 קיימת בשניהם. Use master;
Go
If DB_ID('Try1') Is Not Null Drop Database...
כיצד מאנדקסים טבלה עבור פעולת Distinct? ובאותה הזדמנות – מה ההבדל בין Distinct לביצוע Group By? ניצור שתי טבלאות טכניות (אין משמעות עסקית לתוכן) – אחת Heap ואחת עם Index לפי 3 העמודות שב-Distinct / Group By: If Object_ID('T_Messages','U') Is Not Null Drop Table T_Messages;
Select *
Into T_Messages
From sys.messages;
If Object_ID('T_Messages1','U') Is Not Null Drop Table T_Messages1;
...
תזכורת לאלו ששכחו את החומר מהקורס ל-DBA-ים מתחילים: כשיוצרים טבלה או אינדקס ניתן להגדיר Fill Factor שמציין עד כמה למלא את ה-Pages בנתונים, מתוך כוונה להשאיר מקום פנוי לנתונים הבאים שיכנסו באמצע, כל זאת כדי למנוע Page Split – מצב בו ההמשך הסדרתי של דף מספר 100 אינו דף מספר 101 הסמוך אליו פיזית, אלא דף 1234 וממנו חזרה ל-101, מכיוון שהיה צורך "לדחוף" שורות חדשות באמצע ולא היה מקום. Brent Ozar סבור שההתעסקות בזה מיותרת, התועלת ממנה שולית, ובמקרים רבים שכרנו (השולי) יוצא בהפסדנו (הלא כל כך שולי). כך או כך ננסה לראות...