שימוש מתקדם ב-ViewState

פעמים רבות אני נחשף לעובדה שמתכנתים מקבלים טכנולוגיה כלשהי מבלי להעמיק ולחקור את כל ההיבטים שלה.

מקרה שכזה קרה לי עם ה-ViewState של ASP.NET.

הקדמה

מודל התקשורת באינטרנט הוא Stateless, הכוונה היא שבין קריאת שרת אחת לשניה אין קשר כלשהו כך שלא ניתן לקשר בין  Request אחד לשני ולקבוע בוודאות ששניהם קשורים. כל Request הוא אינדבידואלי והוא נגמר ברגע שהשרת מסיים את הResponse.

כיון שאנחנו בכל זאת צריכים לפתח מערכות מרובי תהליכים שמורכבים מכמה קריאות שרת לכן פותחו מספר פתרונות כשברובם הרעיון הכללי הוא בערך כך:
  1. שליחת ID יחודי ללקוח ושמירתו בדף או בעוגיה או בכתובת URL או בכל רעיון אחר שיש לכם..
  2. קישור ה-ID הנ"ל למידע ששמור בשרת
שיטה זו (שניתנת לישום בצורות רבות)  מאפשרת לנו לשמר מידע בין קריאה אחת לשניה כאשר הקישור בינהם נעשה לפי ה-ID המועבר אלינו בעת הקריאה. זאת השיטה שנעשה בה שימוש בSession של ASP.NET. הלקוח מקבל עוגיה עם ה-SessionID ובכל קריאה לשרת נשלחת העוגיה ונקראת בשרת ועל פי זה הדוט נט טוען את המידע ששמרנו בSESSION שלנו.

זאת אמנם שיטה טובה לשמר State באתר אינטרנט, אבל, מה אם אנחנו רוצים לשמור מידע ספציפי לדף מסויים או לControl מסויים שבנינו?

לשם כך יש לנו את ה-ViewState.


שימוש ב-ViewState

ה-ViewState, מאפשר לנו לשמור נתונים, בצורה דומה ל-Session, על ידי ציון key ו-value. המידע נאגר בתוך ה-ViewState ונשמר בין קריאות חוזרות לדף.
ASP.NET מצליח לשמור את המידע של ה-ViewStae תודות לשדה input ששמור בתוך הדף ומועבר לשרת בכל Postback. במבט מהיר בקוד מקור של הדף נראה שהשדה מעורבל בצורה שאי אפשר להבין, למעשה התחושה היא שהתוכן בשדה מוצפן כך שמשתמש רגיל לא יכול לדעת מה יש שם. ולכן מתכנתים רבים מסתמכים עליו כדי לשמור מידע רגיש (IsAdmin).
האם באמת אפשר לסמוך עליו? אז זהו שלא!

תוכן השדה של ה-ViewState אמנם מקודד אך לא מוצפן!

ההבדל בין קידוד להצפנה הוא פשוט:

  • קידוד זוהי שיטה ליצוג מידע. למשל Unicode היא שיטה ליצוג תויים. כנ"ל גם base64 היא שיטה ליצוג מערך של בייטים. בקידוד השיטה ידועה לכל ולכן כולם יכולים לפענח את המסר המקודד למסר המקורי.
  • הצפנה זוהי שיטת קידוד שמערבלת את המידע על ידי שימוש במפתח סודי, כך שגם אם אתם מכירים את שיטת הקידוד עדיין תצטרכו את המפתח שקודד את המסר כדי לקבל את המסר המקורי.
ולכן, כיון ש-ViewState אינו מוצפן אלא רק מקודד לא ניתן לסמוך עליו בעת שמירת מידע. הקידוד שה-ViewState משתמש בו הוא base64. ניתן בקלות לראות את המידע השמור שם על ידי שימוש במפענח חינמי ל-base64 (מפענח אונליין http://www.opinionatedgeek.com/dotnet/tools/base64decode) או בפונקציה Convert.FromBase64String של דוט נט.
בנוסף יש מקודד/מפענח משוכלל יותר שכתבתי וניתן להוריד אותו כולל קוד מקור בלינק הבא:
http://base64.codeplex.com/


שימוש מתקדם ב-ViewState


אמנם כברירת מחדל דוט נט אינו מצפין את ה-ViewState אלא רק מקודד אותו. בכל אופן אפשר להגדיר שה-ViewStae אכן יהיה מוצפן. בפשטות ניתן להגדיר את זה בראש הדף, כך:
<%@Page ViewStateEncryptionMode="Always" %>
המאפיין ViewStateEncryptionMode יכול לקבל מספר ערכים:
  1. Auto - מספיק שרכיב (Control) אחד בדף ידרוש הצפנה כדי להצפין את כל ה-ViewState.
  2. Never - ה-ViewState לעולם לא יוצפן גם אם ישנה דרישה כזו על ידי רכיב מסוים בדף.
  3. Always - ה-ViewState תמיד יוצפן בין אם נדרש או לא.
כך ניתן להבטיח שהמידע ששמרנו ב-VIewState אכן מוצפן ומוגן מפני מניפולציות.


הגנה מפני "תקיפה בהקלקה" (One Click Attack)

במקרים מסוימים הצפנה עדיין לא מהווה הגנה מספקת וניתן עדיין לתקוף את האתר באמצעות שימוש במידע המוצפן מבלי לדעת מה יש בתוכו. דוגמה לכך היא מתקפת "Cross-site request forgery" שמאפשרת לתוקף לבצע postback לשרת על סמך ViewState מוצפן אחר מבלי צורך לדעת מה יש בפנים ובכך לקבל גישה למידע שלא שיך לו.

כדי למנוע זאת ניתן להשתמש במאפיין ViewStateUserKey של הדף בעת התרחשות האירוע Page.Init כדי לבדל כל ViewState באמצעות פרמטר יחודי למשתמש, למשל שם המשתמש שלו או קוד אקראי אחר שיוקצה לו. כתוצאה מכך רק המשתמש הנוכחי יוכל להשתמש ב-ViewState שלו בצורה תקינה.


סיכום

אמנם זה נחמד לקבל ימבה של טכנולוגיה על מגש של כסף וזה מה שדוט נט עושה בהרבה מהמקרים, אבל ראוי לבחון היטב לפני השימוש ברכיב ספציפי - האם אנחנו באמת יודעים כיצד הוא עובד ומה ההשלכות של השימוש בו?






תגובות

רשומות פופולריות