programing

ASP.NET MVC의 SSL 페이지

skycolor 2023. 6. 16. 21:35
반응형

ASP.NET MVC의 SSL 페이지

ASP.NET MVC 기반 사이트의 일부 페이지에 HTTPS를 사용하려면 어떻게 해야 합니까?

Steve Sanderson은 다음 Preview 4에서 드라이 방식으로 이 작업을 수행하는 방법에 대한 좋은 튜토리얼을 제공합니다.

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

Preview 5에서 더 나은/업데이트 방법이 있습니까?

ASP.NET MVC 2 Preview 2 이상을 사용하는 경우 이제 다음을 간단히 사용할 수 있습니다.

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

하지만 여기서 언급한 것처럼 주문 매개 변수는 주목할 가치가 있습니다.

MVCFutures에는 '필수'가 있습니다.SSL' 특성입니다.

(업데이트된 블로그 게시물에서 이 점을 지적해 주신 Adam에게 감사드립니다.)

http:// 요청이 자동으로 https://:가 되도록 하려면 'Redirect=true'를 사용하여 작업 방법에 적용하기만 하면 됩니다.

    [RequireSsl(Redirect = true)]

참고 항목: ASP.NET MVC 운영 환경에서만 필요한 https

Amadiere가 썼듯이, [RequireHttps]는 HTTPS 입력을 위한 MVC 2에서 잘 작동합니다.하지만 당신이 말한 것처럼 일부 페이지에만 HTTPS를 사용하고 싶다면 MVC 2는 당신에게 사랑을 주지 않습니다. 일단 사용자를 HTTPS로 전환하면 사용자가 수동으로 리디렉션할 때까지 그들은 거기에 갇혀 있습니다.

제가 사용한 접근 방식은 다른 사용자 지정 특성인 [ExitHttpsIfNotRequired]를 사용하는 것입니다.다음과 같은 경우 컨트롤러 또는 작업에 연결하면 HTTP로 리디렉션됩니다.

  1. 요청은 HTTPS였습니다.
  2. [RequireHttps] 특성이 작업(또는 컨트롤러)에 적용되지 않았습니다.
  3. 요청은 GET(POST를 리디렉션하면 모든 종류의 문제가 발생함)였습니다.

여기에 게시하기에는 너무 크지만, 여기에 코드와 몇 가지 추가 세부 정보를 볼 수 있습니다.

댄 월린이 이에 대해 최근에 쓴 글입니다.

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

그는 ActionFilter 특성을 사용합니다.

일부 ActionLink 확장: http://www.squaredroot.com/post/2008/06/11/MVC-and-SSL.aspx 또는 https:// http://forums.asp.net/p/1260198/2358380.aspx#2358380 으로 리디렉션되는 컨트롤러 작업 특성

속성 지향 개발 접근 방식을 좋아하지 않는 사람들을 위해 다음과 같은 도움이 될 수 있는 코드가 있습니다.

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);
    }
}

속성을 사용하지 않는 몇 가지 이유가 있습니다. 그 중 하나는 보안 페이지 목록을 모두 보려면 솔루션의 모든 컨트롤러를 건너뛰어야 한다는 것입니다.

저는 이 질문을 우연히 마주쳤고 제 솔루션이 누군가를 도울 수 있기를 바랍니다.

몇 가지 문제가 발생했습니다. - "계정"의 "로그온"과 같은 특정 작업을 보호해야 합니다.우리는 RequireHttps 속성의 빌드를 사용할 수 있습니다. 이는 훌륭하지만 https://로 다시 리디렉션됩니다. - 링크, 양식 및 "SSL 인식"을 제공해야 합니다.

일반적으로 내 솔루션은 프로토콜을 지정하는 기능 외에도 절대 URL을 사용할 경로를 지정할 수 있습니다.이 방법을 사용하여 "https" 프로토콜을 지정할 수 있습니다.

먼저 Connection Protocol 열거형을 만들었습니다.

/// <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
}

이제 RequireSsl의 수동 롤 버전을 만들었습니다.원래 RequireSsl 소스 코드를 수정하여 http://url로 리디렉션할 수 있도록 했습니다.또한 SSL이 필요한지 여부를 결정할 수 있는 필드를 추가했습니다(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);
    }
}

이제 이 RequireSsl은 Requirements 특성 값을 기반으로 다음 작업을 수행합니다. - 무시:아무것도 하지 않습니다. - Http: 강제로 http 프로토콜로 리디렉션합니다. - Https: 강제로 https 프로토콜로 리디렉션합니다.

자체 기본 컨트롤러를 만들고 이 속성을 Http로 설정해야 합니다.

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

이제 각 cpntroller/action에서 SSL이 필요합니다. Connection Protocol을 사용하여 이 속성을 설정하기만 하면 됩니다.Https.

이제 URL로 이동하겠습니다.url 라우팅 엔진에 몇 가지 문제가 있습니다.여러분은 http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ 에서 그것들에 대해 더 읽을 수 있습니다.이 게시물에서 제안된 해결책은 이론적으로 좋지만 오래되어 접근 방식이 마음에 들지 않습니다.

제 솔루션은 다음과 같습니다.기본 "경로" 클래스의 하위 클래스를 만듭니다.

public 클래스 AbsoluteUrlRoute : 경로 {#regionctor

    /// <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
}

이 버전의 "Route" 클래스는 절대 URL을 만듭니다.여기서 블로그 게시물 작성자 제안에 이은 요령은 DataToken을 사용하여 체계를 지정하는 것입니다(마지막 예:).

예를 들어 "Account/LogOn" 경로에 대해 URL을 생성하면 "/http://example.com/Account/LogOn "이 표시됩니다. 이는 UrlRoutingModule이 모든 URL을 상대적으로 보기 때문입니다.사용자 지정 HttpModule을 사용하여 이 문제를 해결할 수 있습니다.

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);
            }
        }
    }
}

이 모듈은 UrlRoutingModule의 기본 구현을 재정의하므로 기본 httpModule을 제거하고 web.config에 등록해야 합니다.그래서, "시스템" 아래에서.web" 집합:

<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>

이상입니다 :).

절대/프로토콜을 따르는 경로를 등록하려면 다음을 수행해야 합니다.

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

피드백 + 개선 사항을 듣고 싶습니다.도움이 되길 바랍니다! :)

편집: IsCurrentConnectionSecured() 확장 메서드를 포함하지 않았습니다(너무 많은 스니펫:P).일반적으로 요청을 사용하는 확장 메서드입니다.보안 연결입니다.그러나 로드 밸런싱을 사용할 때는 이 접근 방식이 작동하지 않으므로 이 방법은 이를 무시할 수 있습니다(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";
    }

다음은 2009년 1월 파블로 M. 시브라노의 블로그 게시물입니다. HttpModule과 확장 방법을 포함한 몇 가지 기술을 수집합니다.

여기 액션필터를 사용하는 아담 살보의 블로그 게시물이 있습니다.

이 솔루션은 MVC에만 해당되는 것은 아니지만 ASP.NET WebForms와 MVC 모두에서 작동합니다.

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

저는 이것을 몇 년 동안 사용했고 web.config 파일을 통해 우려 사항과 관리를 분리하는 것을 좋아합니다.

MVC 6(ASP.NET Core 1.0)은 Startup.cs 에서 약간 다르게 작동합니다.

모든 페이지에서 RequireHttpsAttribute(Amadiere의 답변에서 언급한 대로)를 사용하려면 각 컨트롤러에서 특성 스타일을 사용하는 대신(또는 상속할 모든 컨트롤러에 대한 기본 컨트롤러를 만드는 대신) Startup.cs 에 이 특성을 추가할 수 있습니다.

Startup.cs - 필터 등록:

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

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

위의 접근 방식에 대한 설계 결정에 대한 자세한 내용은 RequireHttpsAttribute에서 localhost 요청을 처리하지 않는 방법에 대한 유사한 질문에 대한 내 답변을 참조하십시오.

또는 Global.asax.cs 에 필터를 추가합니다.

전역 필터.필터.추가(새 RequireHttpsAttribute();

필수 Https 특성 클래스

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);
        }
    }
}

언급URL : https://stackoverflow.com/questions/156748/ssl-pages-under-asp-net-mvc

반응형