en-UShe-IL
You are here:   Blog
Register   |  Login

Blog Archive:

Maximize
* Can be used in order to search for older blogs Entries

Search in blogs


Blog Categories:

Maximize
* Can be used in order to search for blogs Entries by Categories

Blog Tags:

Maximize
* Can be used in order to search for blogs by keywords

TNWikiSummit


Awared MVP 


 


Microsoft® Community Contributor 


Microsoft® Community Contributor


 Read first, before you you use the blog! Maximize
מרץ15

Written by: ronen ariely
15/03/2012 10:33 RssIcon

הפתרון המובנה ב MVC מגרסה 2 לעבודה עם SSL הוא באמצעות המאפיין RequireHttps. מאפיין זה קובע שרק פניות בפרוטוקול HTTPS יועברו אל הבלוק עליו הוא הוגדר.

ישנם מספר שאלות/בעיות שעולות עם השימוש במאפיין שבחלקן ננסה לדון כאן תוך הצגת פתרון סביר.

נתחיל מלהציג כיצד נשתמש ב RequireHttpsAttribute :

[RequireHttps] //apply to all actions in controller
public class SomeController 
{
    [RequireHttps] //apply to this action only
    public ActionResult SomeAction()
    {
        ...
    }
}

בעיה:

שרת הפיתוח (Cassini) של ה VS אינו תומך בעבודה עם SSL. כיצד נוכל להגדיר לשרת שבזמן הפיתוח לא יעשה שימוש במאפיין זה ובזמן העבודה בשרת החי כן?

הפתרון:

אפשרות 1:הכנסת המאפיין בין הוראות קימפול

#if !DEBUG
[RequireHttps] //apply to all actions in controller
#endif

אפשרות 2: הגדרת מחלקה חדשה שנגזרת מהמחלקה RequireHttpsAttribute. במחלקה זו נגדיר שאם הכתובת היא מקומית אז יש פשוט לצאת החוצה. ז"א לא יהיה שימוש במאפיין.

using System;
using System.Web.Mvc;
  
namespace My.Utils
{
    public class MyRequireHttpsAttribute : RequireHttpsAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
  
            if (filterContext.HttpContext != null && filterContext.HttpContext.Request.IsLocal)
            {
                return;
            }
  
            base.OnAuthorization(filterContext);
        }
    }
}

אפשרות 3: הרחבה של הפילטר של ה MVC על ידי הגדרה בקובץ global.asax.

protected void Application_Start()
{
  RegisterGlobalFilters(GlobalFilters.Filters);
}
 
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
  filters.Add(new HandleErrorAttribute());
  if(Config.IsProduction) //Some flag  to check if you are in your production environment.
  {
    filters.Add(new RequireHttpsAttribute());
  }
}

* הערה: יש פתרון הרבה יותר פשוט ומתאים וזה פשוט לא לעבוד עם השרת Cassini אלא עם שרת ה IIS המלא או עם IISExpress

בעיה:

עד כאן טוב ויפה לשימוש בבלוק בו רוצים לעשות שימוש במאפיין אבל התוצאה כוללת 2 בעיות

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

ב.      אחרי שסיים המשתמש לעבוד בפרוטוקול המאובטח הוא "יתקע" בו וכל המשך הגלישה יבוצע בפרוטוקול מאובטח.

כיצד נוכל לבצע העברה אוטומטית של המשתמש מכתובת לא מאובטחת לכתובת מאובטחת והפוך?

הפתרון:

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

כך למשל נוכל להגדיר מאפיין בשם NotRequireHttps אשר יבדוק אם אנחנו נמצאים בפרוטוקול מאובטח ואם כן יעביר אותנו לפרוטוקול HTTP רגיל. ומאפיין הפוך בשם RedirectHttps אשר יבצע עבורנו את המעבר לפרוטוקול מאובטח (לא רק יבדוק אם אנחנו נמצאים בפרוטוקול מאובטח ויציג שגיאה אם לא).

דוגמה מעשית לשימוש תהיה על ידי הגדרת המאפיין NotRequireHttps על פני כל המחלקה בעוד רק על מתודות מסוימות נגדיר מאפיין RequireHttps. בצורה זו קיבלנו בכניסה אל ה Action עם המאפיין RequireHttps תהיה בדיקה שאנחנו בפרוטוקול מאובטח אבל מייד כשנעבור ל Action שונה הגולש יועבר לפרוטוקול HTTP רגיל. מדובר בדרך יעילה ונוחה לוודא שבסיום העבודה עם החלק שמחייב פרוטוקול מאובטח נחזור לעבודה בפרוטוקול רגיל.

public class NotRequireHttps : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        // abort if it's not a secure connection
        if (!filterContext.HttpContext.Request.IsSecureConnection) return;
 
        // abort if a [RequireHttps] attribute is applied to controller
        if (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(
                typeof(RequireHttpsAttribute), true
            ).Length > 0)
            return;
        // abort if a [RequireHttps] attribute is applied to action
        if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Length > 0) return;
 
        // abort if a [RetainHttps] attribute is applied to controller or action
        if (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RetainHttpsAttribute), true).Length > 0) return;
        if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(RetainHttpsAttribute), true).Length > 0) return;
 
        // abort if it's not a GET request - we don't want to be redirecting on a form post
        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) return;
 
        // redirect to HTTP
        string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
        filterContext.Result = new RedirectResult(url);
    }
}
 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RetainHttpsAttribute : Attribute
{
}

הערה: יש לזכור שבזמן ביצוע Redirect אנו מאבדים את כל הנתונים שהגיעו בשיטת POST כמובן. יש להקפיד לא לעשות שימוש בשיטה זו כאשר מדובר על Action אליו מגיעים בשיטת POST. מצד שני אין הדיון שנקודה זו תפריע לנו בדרך כלל מכיוון שאת המעבר לפרוטוקול אבטחה אנחנו רוצים לבצע לפני ה POST.

הערה: שיטה זו למעשה מאפשרת לעשות שימוש במאפיין שלנו במקום המאפיין RequireHttps המובנה. דבר שיכול לעזור למשל בעבודה עם גרסאות קודמות של MVC.

public class RedirectHttps : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsSecureConnection)
        {
            filterContext.Result =
                new RedirectResult(filterContext.HttpContext.Request.Url.
                    ToString().Replace("http:", "https:"));
            filterContext.Result.ExecuteResult(filterContext);
        }
        base.OnActionExecuting(filterContext);
    }
}

מקורות ומידע נוסף:

Adding HTTPS/SSL support to ASP.NET MVC routing:
http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/

Entering and exiting HTTPS with ASP.NET MVC:
http://lukesampson.com/post/471548689/entering-and-exiting-https-with-asp-net-mvc

SSL support in ASP.NET MVC:
http://fknet.wordpress.com/2010/01/04/ssl-support-in-asp-net-mvc/

Working with SSL at Development Time is easier with IISExpress:
http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

Enabling SSL on IIS 7.0 Using Self-Signed Certificates
http://weblogs.asp.net/scottgu/archive/2007/04/06/tip-trick-enabling-ssl-on-iis7-using-self-signed-certificates.aspx