Pregunta

¿Cómo hago para usar HTTPS para algunas de las páginas de mi sitio basado en ASP.NET MVC?

Steve Sanderson tiene un tutorial bastante bueno sobre cómo hacer esto de forma SECA en la Vista previa 4 en:

http: // blog .codeville.net / 2008/08/05 / added-httpsssl-support-to-aspnet-mvc-routing /

¿Hay una forma mejor / actualizada con la Vista previa 5 ?,

¿Fue útil?

Solución

Si está utilizando ASP.NET MVC 2 Preview 2 o superior , ahora puedes simplemente usar:

[RequireHttps]
public ActionResult Login()
{
   return View();
}

Sin embargo, vale la pena señalar el parámetro de orden, ya que mencionado aquí .

Otros consejos

MVCFutures tiene el atributo 'RequireSSL'.

(gracias a Adam por señalarlo en tu blogpost actualizado )

Simplemente aplíquelo a su método de acción, con 'Redirect = true' si desea que una solicitud http: // se convierta automáticamente en https: //:

    [RequireSsl(Redirect = true)]

Vea también: ASP.NET MVC RequireHttps solo en producción

Como Amadiere escribió , [RequireHttps] funciona muy bien en MVC 2 para ingresar HTTPS. Pero si solo quieres usar HTTPS para algunas páginas como dijiste, MVC 2 no te hace ninguna ilusión, una vez que cambia de usuario a HTTPS, se quedan atascados allí hasta que los redirecciones manualmente.

El enfoque que utilicé es usar otro atributo personalizado, [ExitHttpsIfNotRequired]. Cuando se adjunta a un controlador o acción, esto redireccionará a HTTP si:

  1. La solicitud fue HTTPS
  2. El atributo [RequireHttps] no se aplicó a la acción (o controlador)
  3. La solicitud era un GET (la redirección de un POST llevaría a todo tipo de problemas).

Es un poco demasiado grande para publicar aquí, pero puedes ver el código aquí más algunos detalles adicionales.

Aquí hay una publicación reciente de Dan Wahlin sobre esto:

http://weblogs.asp.net/dwahlin/archive/2009/08/25/requiring-ssl-for-asp-net-mvc-controllers.aspx

Utiliza un atributo de filtro de acción.

Algunas extensiones de ActionLink: http: // www .squaredroot.com / post / 2008/06/11 / MVC-and-SSL.aspx O un atributo de acción del controlador que redirige a https: // foros http: //. asp.net/p/1260198/2358380.aspx#2358380

Para aquellos que no son fanáticos de los enfoques de desarrollo orientados a atributos, aquí hay un código que podría ayudar:

public static readonly string[] SecurePages = new[] { "login", "join" };
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
    var pageName = RequestHelper.GetPageNameOrDefault();
    if (!HttpContext.Current.Request.IsSecureConnection
        && (HttpContext.Current.Request.IsAuthenticated || SecurePages.Contains(pageName)))
    {
        Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl);
    }
    if (HttpContext.Current.Request.IsSecureConnection
        && !HttpContext.Current.Request.IsAuthenticated
        && !SecurePages.Contains(pageName))
    {
        Response.Redirect("http://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl);
    }
}

Hay varias razones para evitar los atributos y una de ellas es que si desea ver la lista de todas las páginas seguras, tendrá que saltar sobre todos los controladores en la solución.

Fui a través de esta pregunta y espero que mi solución pueda ayudar a alguien.

Tenemos algunos problemas:  - Necesitamos asegurar acciones específicas, por ejemplo " LogOn " en " Cuenta " ;. Podemos usar la construcción en el atributo RequireHttps, que es genial, pero nos redirigirá nuevamente con https: //.  - Debemos hacer que nuestros enlaces, formularios y tales " sean compatibles con SSL " ;.

En general, mi solución permite especificar rutas que usarán la URL absoluta, además de la capacidad de especificar el protocolo. Puede utilizar este método para especificar el " https " protocolo.

Entonces, en primer lugar he creado una enumeración de ConnectionProtocol:

/// <summary>
/// Enum representing the available secure connection requirements
/// </summary>
public enum ConnectionProtocol
{
    /// <summary>
    /// No secure connection requirement
    /// </summary>
    Ignore,

    /// <summary>
    /// No secure connection should be used, use standard http request.
    /// </summary>
    Http,

    /// <summary>
    /// The connection should be secured using SSL (https protocol).
    /// </summary>
    Https
}

Ahora, he creado la versión enrollada a mano de RequireSsl. He modificado el código fuente original de RequireSsl para permitir la redirección a http: // urls. Además, he puesto un campo que nos permite determinar si deberíamos requerir SSL o no (lo estoy usando con el preprocesador DEBUG).

/* Note:
 * This is hand-rolled version of the original System.Web.Mvc.RequireHttpsAttribute.
 * This version contains three improvements:
 * - Allows to redirect back into http:// addresses, based on the <see cref="SecureConnectionRequirement" /> Requirement property.
 * - Allows to turn the protocol scheme redirection off based on given condition.
 * - Using Request.IsCurrentConnectionSecured() extension method, which contains fix for load-balanced servers.
 */
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
    public RequireHttpsAttribute()
    {
        Protocol = ConnectionProtocol.Ignore;
    }

    /// <summary>
    /// Gets or sets the secure connection required protocol scheme level
    /// </summary>
    public ConnectionProtocol Protocol { get; set; }

    /// <summary>
    /// Gets the value that indicates if secure connections are been allowed
    /// </summary>
    public bool SecureConnectionsAllowed
    {
        get
        {
#if DEBUG
            return false;
#else
            return true;
#endif
        }
    }

    public void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        /* Are we allowed to use secure connections? */
        if (!SecureConnectionsAllowed)
            return;

        switch (Protocol)
        {
            case ConnectionProtocol.Https:
                if (!filterContext.HttpContext.Request.IsCurrentConnectionSecured())
                {
                    HandleNonHttpsRequest(filterContext);
                }
                break;
            case ConnectionProtocol.Http:
                if (filterContext.HttpContext.Request.IsCurrentConnectionSecured())
                {
                    HandleNonHttpRequest(filterContext);
                }
                break;
        }
    }


    private void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        // only redirect for GET requests, otherwise the browser might not propagate the verb and request
        // body correctly.

        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
        }

        // redirect to HTTPS version of page
        string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
        filterContext.Result = new RedirectResult(url);
    }

    private void HandleNonHttpRequest(AuthorizationContext filterContext)
    {
        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("The requested resource can only be accessed without SSL.");
        }

        // redirect to HTTP version of page
        string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
        filterContext.Result = new RedirectResult(url);
    }
}

Ahora, este RequireSsl hará la siguiente base en el valor de atributo de sus Requisitos:  - Ignorar: No haré nada.  - Http: Forzará la redirección al protocolo http.  - Https: forzará la redirección al protocolo https.

Debes crear tu propio controlador base y establecer este atributo en Http.

[RequireSsl(Requirement = ConnectionProtocol.Http)]
public class MyController : Controller
{
    public MyController() { }
}

Ahora, en cada cpntroller / acción que le gustaría requerir SSL, simplemente establezca este atributo con ConnectionProtocol.Https.

Ahora pasemos a las URL: tenemos algunos problemas con el motor de enrutamiento url. Puede leer más sobre ellos en http : //blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ . La solución sugerida en este post es teóricamente buena, pero antigua y no me gusta el enfoque.

Mis soluciones son las siguientes: Cree una subclase de la ruta básica " Ruta " clase:

clase pública AbsoluteUrlRoute: Ruta     {         #region ctor

    /// <summary>
    /// Initializes a new instance of the System.Web.Routing.Route class, by using
    ///     the specified URL pattern and handler class.
    /// </summary>
    /// <param name="url">The URL pattern for the route.</param>
    /// <param name="routeHandler">The object that processes requests for the route.</param>
    public AbsoluteUrlRoute(string url, IRouteHandler routeHandler)
        : base(url, routeHandler)
    {

    }

    /// <summary>
    /// Initializes a new instance of the System.Web.Routing.Route class, by using
    ///     the specified URL pattern and handler class.
    /// </summary>
    /// <param name="url">The URL pattern for the route.</param>
    /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
    /// <param name="routeHandler">The object that processes requests for the route.</param>
    public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        : base(url, defaults, routeHandler)
    {

    }

    /// <summary>
    /// Initializes a new instance of the System.Web.Routing.Route class, by using
    ///     the specified URL pattern and handler class.
    /// </summary>
    /// <param name="url">The URL pattern for the route.</param>
    /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
    /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
    /// <param name="routeHandler">The object that processes requests for the route.</param>
    public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
                            IRouteHandler routeHandler)
        : base(url, defaults, constraints, routeHandler)
    {

    }

    /// <summary>
    /// Initializes a new instance of the System.Web.Routing.Route class, by using
    ///     the specified URL pattern and handler class.
    /// </summary>
    /// <param name="url">The URL pattern for the route.</param>
    /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param>
    /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
    /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used
    ///     to determine whether the route matches a specific URL pattern. These values
    ///     are passed to the route handler, where they can be used for processing the
    ///     request.</param>
    /// <param name="routeHandler">The object that processes requests for the route.</param>
    public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints,
                            RouteValueDictionary dataTokens, IRouteHandler routeHandler)
        : base(url, defaults, constraints, dataTokens, routeHandler)
    {

    }

    #endregion

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var virtualPath = base.GetVirtualPath(requestContext, values);
        if (virtualPath != null)
        {
            var scheme = "http";
            if (this.DataTokens != null && (string)this.DataTokens["scheme"] != string.Empty)
            {
                scheme = (string) this.DataTokens["scheme"];
            }

            virtualPath.VirtualPath = MakeAbsoluteUrl(requestContext, virtualPath.VirtualPath, scheme);
            return virtualPath;
        }

        return null;
    }

    #region Helpers

    /// <summary>
    /// Creates an absolute url
    /// </summary>
    /// <param name="requestContext">The request context</param>
    /// <param name="virtualPath">The initial virtual relative path</param>
    /// <param name="scheme">The protocol scheme</param>
    /// <returns>The absolute URL</returns>
    private string MakeAbsoluteUrl(RequestContext requestContext, string virtualPath, string scheme)
    {
        return string.Format("{0}://{1}{2}{3}{4}",
                             scheme,
                             requestContext.HttpContext.Request.Url.Host,
                             requestContext.HttpContext.Request.ApplicationPath,
                             requestContext.HttpContext.Request.ApplicationPath.EndsWith("/") ? "" : "/",
                             virtualPath);
    }

    #endregion
}

Esta versión de " Ruta " clase creará url absoluta. El truco aquí, seguido de la sugerencia del autor de la publicación del blog, es utilizar DataToken para especificar el esquema (ejemplo al final :)).

Ahora, si generaremos una URL, por ejemplo, para la ruta " Cuenta / Inicio de sesión " obtendremos " / http://example.com/Account/LogOn " - eso es porque UrlRoutingModule ve todas las direcciones URL como relativas. Podemos arreglar eso usando HttpModule personalizado:

public class AbsoluteUrlRoutingModule : UrlRoutingModule
{
    protected override void Init(System.Web.HttpApplication application)
    {
        application.PostMapRequestHandler += application_PostMapRequestHandler;
        base.Init(application);
    }

    protected void application_PostMapRequestHandler(object sender, EventArgs e)
    {
        var wrapper = new AbsoluteUrlAwareHttpContextWrapper(((HttpApplication)sender).Context);
    }

    public override void PostResolveRequestCache(HttpContextBase context)
    {
        base.PostResolveRequestCache(new AbsoluteUrlAwareHttpContextWrapper(HttpContext.Current));
    }

    private class AbsoluteUrlAwareHttpContextWrapper : HttpContextWrapper
    {
        private readonly HttpContext _context;
        private HttpResponseBase _response = null;

        public AbsoluteUrlAwareHttpContextWrapper(HttpContext context)
            : base(context)
        {
            this._context = context;
        }

        public override HttpResponseBase Response
        {
            get
            {
                return _response ??
                       (_response =
                        new AbsoluteUrlAwareHttpResponseWrapper(_context.Response));
            }
        }


        private class AbsoluteUrlAwareHttpResponseWrapper : HttpResponseWrapper
        {
            public AbsoluteUrlAwareHttpResponseWrapper(HttpResponse response)
                : base(response)
            {

            }

            public override string ApplyAppPathModifier(string virtualPath)
            {
                int length = virtualPath.Length;
                if (length > 7 && virtualPath.Substring(0, 7) == "/http:/")
                    return virtualPath.Substring(1);
                else if (length > 8 && virtualPath.Substring(0, 8) == "/https:/")
                    return virtualPath.Substring(1);

                return base.ApplyAppPathModifier(virtualPath);
            }
        }
    }
}

Dado que este módulo está anulando la implementación base de UrlRoutingModule, deberíamos eliminar el httpModule base y registrar el nuestro en web.config. Entonces, bajo " system.web " conjunto:

<httpModules>
  <!-- Removing the default UrlRoutingModule and inserting our own absolute url routing module -->
  <remove name="UrlRoutingModule-4.0" />
  <add name="UrlRoutingModule-4.0" type="MyApp.Web.Mvc.Routing.AbsoluteUrlRoutingModule" />
</httpModules>

Eso es :).

Para registrar una ruta absoluta / protocolo seguido, debes hacer:

        routes.Add(new AbsoluteUrlRoute("Account/LogOn", new MvcRouteHandler())
            {
                Defaults = new RouteValueDictionary(new {controller = "Account", action = "LogOn", area = ""}),
                DataTokens = new RouteValueDictionary(new {scheme = "https"})
            });

Me encantará escuchar tus comentarios + mejoras. Espero que pueda ayudar! :)

Editar: Olvidé incluir el método de extensión IsCurrentConnectionSecured () (demasiados fragmentos: P). Este es un método de extensión que generalmente utiliza Request.IsSecuredConnection. Sin embargo, este enfoque no funcionará cuando se use el equilibrio de carga, por lo que este método puede omitir esto (tomado de nopCommerce).

    /// <summary>
    /// Gets a value indicating whether current connection is secured
    /// </summary>
    /// <param name="request">The base request context</param>
    /// <returns>true - secured, false - not secured</returns>
    /// <remarks><![CDATA[ This method checks whether or not the connection is secured.
    /// There's a standard Request.IsSecureConnection attribute, but it won't be loaded correctly in case of load-balancer.
    /// See: <a href="http://nopcommerce.codeplex.com/SourceControl/changeset/view/16de4a113aa9#src/Libraries/Nop.Core/WebHelper.cs">nopCommerce WebHelper IsCurrentConnectionSecured()</a>]]></remarks>
    public static bool IsCurrentConnectionSecured(this HttpRequestBase request)
    {
        return request != null && request.IsSecureConnection;

        //  when your hosting uses a load balancer on their server then the Request.IsSecureConnection is never got set to true, use the statement below
        //  just uncomment it
        //return request != null && request.ServerVariables["HTTP_CLUSTER_HTTPS"] == "on";
    }

Aquí hay un publicación de blog de Pablo M. Cibrano de enero de 2009 que recopila un par de técnicas que incluyen un HttpModule y métodos de extensión.

Aquí hay un publicación de blog de Adam Salvo que utiliza un ActionFilter .

Esto no es necesariamente específico de MVC, pero esta solución funciona tanto para los formularios web de ASP.NET como para MVC:

http://www.codeproject.com/KB/web-security /WebPageSecurity_v2.aspx

He usado esto durante varios años y me gusta la separación de inquietudes y la administración a través del archivo web.config.

MVC 6 (ASP.NET Core 1.0) funciona ligeramente diferente con Startup.cs.

Para usar RequireHttpsAttribute (como se menciona en answer por Amadiere) en todas las páginas, puede agregar esto en Startup.cs en lugar de usar el estilo de atributo en cada controlador (o en lugar de crear un BaseController para que todos los controladores puedan heredar).

Startup.cs : filtro de registro:

public void ConfigureServices(IServiceCollection services)
{
    // TODO: Register other services

    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(RequireHttpsAttribute));
    });
}

Para obtener más información sobre las decisiones de diseño para el enfoque anterior, vea mi respuesta en una pregunta similar sobre cómo excluir las solicitudes de localhost para que no sean manejadas por el RequireHttpsAttribute .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top