¿Cómo puedo 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 Preview 4 en:
http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/
¿Existe una forma mejor / actualizada con Preview 5?
asp.net
asp.net-mvc
ssl
https
David Laing
fuente
fuente
Respuestas:
Si está utilizando ASP.NET MVC 2 Preview 2 o superior , ahora puede simplemente usar:
[RequireHttps] public ActionResult Login() { return View(); }
Sin embargo, vale la pena señalar el parámetro de orden, como se menciona aquí .
fuente
MVCFutures tiene un atributo 'RequireSSL'.
(gracias Adam por señalarlo en tu publicación de blog actualizada)
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)]
Consulte también: ASP.NET MVC RequireHttps solo en producción
fuente
Como escribió Amadiere , [RequireHttps] funciona muy bien en MVC 2 para ingresar HTTPS. Pero si solo desea usar HTTPS para algunas páginas como dijo, MVC 2 no le da ningún amor: una vez que cambia a un usuario a HTTPS, se atascan allí hasta que los redirige manualmente.
El enfoque que utilicé es utilizar otro atributo personalizado, [ExitHttpsIfNotRequired]. Cuando se adjunta a un controlador o acción, se redirigirá a HTTP si:
Es demasiado grande para publicarlo aquí, pero puedes ver el código aquí más algunos detalles adicionales.
fuente
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 ActionFilter.
fuente
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 redirecciona a https: // http://forums.asp.net /p/1260198/2358380.aspx#2358380
fuente
Para aquellos que no son fanáticos de los enfoques de desarrollo orientados a atributos, aquí hay un fragmento de 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 pasar por alto todos los controladores en la solución.
fuente
Crucé esta pregunta y espero que mi solución pueda ayudar a alguien.
Tenemos algunos problemas: - Necesitamos asegurar acciones específicas, por ejemplo, "Iniciar sesión" en "Cuenta". Podemos usar la compilación en el atributo RequireHttps, lo cual es genial, pero nos redirigirá con https: //. - Debemos hacer que nuestros enlaces, formularios y tales "sean compatibles con SSL".
Generalmente, mi solución permite especificar rutas que usarán una URL absoluta, además de la capacidad de especificar el protocolo. Puede utilizar este enfoque para especificar el protocolo "https".
Entonces, en primer lugar, he creado una enumeración 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 una versión enrollada a mano de RequireSsl. He modificado el código fuente de RequireSsl original para permitir la redirección a http: // urls. Además, he puesto un campo que nos permite determinar si debemos 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 su atributo de Requisitos: - Ignorar: no hará nada. - Http: forzará la redirección al protocolo http. - Https: forzará la redirección al protocolo https.
Debe crear su propio controlador base y establecer este atributo en Http.
[RequireSsl(Requirement = ConnectionProtocol.Http)] public class MyController : Controller { public MyController() { } }
Ahora, en cada cpntroller / action que le gustaría requerir SSL, simplemente configure este atributo con ConnectionProtocol.Https.
Ahora pasemos a las URL: tenemos algunos problemas con el motor de enrutamiento de 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 esta publicación es teóricamente buena, pero vieja y no me gusta el enfoque.
Mis soluciones son las siguientes: Cree una subclase de la clase básica "Ruta":
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 la clase "Ruta" creará una URL absoluta. El truco aquí, seguido de la sugerencia del autor de la publicación del blog, es usar DataToken para especificar el esquema (ejemplo al final :)).
Ahora, si generamos una URL, por ejemplo, para la ruta "Account / LogOn" obtendremos "/ http://example.com/Account/LogOn ", ya que UrlRoutingModule ve todas las 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 anula la implementación base de UrlRoutingModule, deberíamos eliminar el httpModule base y registrar el nuestro en web.config. Entonces, en "system.web", establezca:
<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 / seguida por protocolo, debe 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 sus comentarios y 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 usa Request.IsSecuredConnection. Sin embargo, este enfoque no funcionará cuando se use el equilibrio de carga, por lo que este método puede omitirlo (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"; }
fuente
Aquí hay una publicación de blog de Pablo M. Cibrano de enero de 2009 que reúne un par de técnicas, incluido un HttpModule y métodos de extensión.
fuente
Aquí hay una publicación de blog de Adam Salvo que usa ActionFilter.
fuente
Esto no es necesariamente específico de MVC, pero esta solución funciona tanto para ASP.NET WebForms 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 preocupaciones y administración a través del archivo web.config.
fuente
MVC 6 (ASP.NET Core 1.0) funciona de forma ligeramente diferente con Startup.cs.
Para usar RequireHttpsAttribute (como se menciona en la respuesta de 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 sus controladores hereden).
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, consulte mi respuesta a una pregunta similar sobre cómo excluir que las solicitudes de localhost sean manejadas por RequireHttpsAttribute .
fuente
Alternativamente agregue un filtro a Global.asax.cs
RequireHttpsAttribute (clase)
using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; namespace xxxxxxxx { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); GlobalFilters.Filters.Add(new RequireHttpsAttribute()); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } } }
fuente