ASP.NET MVC RequestHttps in Production Only


121

Tôi muốn sử dụng RequestHttpsAttribute để ngăn các yêu cầu HTTP không an toàn được gửi đến một phương thức hành động.

C #

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

VB

<RequireHttps()> _
Public Class SomeController

    <RequireHttps()> _
    Public Function SomeAction() As ActionResult
        ...
    End Function

End Class

Rất tiếc, Máy chủ phát triển ASP.NET không hỗ trợ HTTPS.

Làm cách nào để ứng dụng ASP.NET MVC của tôi sử dụng RequestHttps khi được xuất bản lên môi trường sản xuất, nhưng không phải khi chạy trên máy trạm phát triển của tôi trên Máy chủ phát triển ASP.NET?


3
Kiểm tra với IIS cục bộ của bạn và với IIS Express. Xem SSL blog của tôi blogs.msdn.com/b/rickandy/archive/2011/04/22/...blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

Câu trả lời:


129

Điều này sẽ không hữu ích nếu bạn chạy các bản phát hành trên máy trạm phát triển của mình, nhưng biên dịch có điều kiện có thể thực hiện công việc ...

#if !DEBUG
[RequireHttps] //apply to all actions in controller
#endif
public class SomeController 
{
    //... or ...
#if !DEBUG
    [RequireHttps] //apply to this action only
#endif
    public ActionResult SomeAction()
    {
    }

}

Cập nhật

Trong Visual Basic, các thuộc tính về mặt kỹ thuật là một phần của dòng giống như định nghĩa mà chúng áp dụng. Bạn không thể đặt các câu lệnh biên dịch có điều kiện bên trong một dòng, vì vậy bạn buộc phải viết khai báo hàm hai lần - một lần với thuộc tính và một lần không. Tuy nhiên, nó vẫn hoạt động nếu bạn không bận tâm đến sự xấu xí.

#If Not Debug Then
    <RequireHttps()> _
    Function SomeAction() As ActionResult
#Else
    Function SomeAction() As ActionResult
#End If
        ...
    End Function

Cập nhật 2

Một số người đã đề cập đến việc lấy từ RequireHttpsAttributemà không cung cấp ví dụ, vì vậy đây là một ví dụ dành cho bạn. Tôi nghĩ rằng cách tiếp cận này sẽ gọn gàng hơn nhiều so với cách tiếp cận biên dịch có điều kiện và đó sẽ là ưu tiên của tôi ở vị trí của bạn.

KHUYẾN CÁO: Tôi chưa thử nghiệm mã này, dù chỉ một chút, và VB của tôi khá cũ. Tất cả những gì tôi biết là nó biên dịch. Tôi viết nó dựa trên gợi ý của spot, queen3 và Lance Fisher. Nếu nó không hiệu quả, ít nhất nó phải truyền đạt ý tưởng chung và cung cấp cho bạn điểm xuất phát.

Public Class RemoteRequireHttpsAttribute
    Inherits System.Web.Mvc.RequireHttpsAttribute

    Public Overrides Sub OnAuthorization(ByVal filterContext As  _
                                         System.Web.Mvc.AuthorizationContext)
        If IsNothing(filterContext) Then
            Throw New ArgumentNullException("filterContext")
        End If

        If Not IsNothing(filterContext.HttpContext) AndAlso _
            filterContext.HttpContext.Request.IsLocal Then
            Return
        End If

        MyBase.OnAuthorization(filterContext)
    End Sub

End Class

Về cơ bản, thuộc tính mới chỉ thoát ra thay vì chạy mã ủy quyền SSL mặc định, nếu yêu cầu hiện tại là cục bộ (nghĩa là bạn đang truy cập trang web thông qua localhost). Bạn có thể sử dụng nó như thế này:

<RemoteRequireHttps()> _
Public Class SomeController

    <RemoteRequireHttps()> _
    Public Function SomeAction() As ActionResult
        ...
    End Function

End Class

Sạch sẽ hơn nhiều! Miễn là mã chưa được kiểm tra của tôi thực sự hoạt động.


Cảm ơn vì đã chỉnh sửa bài đăng cho tôi, Zack. Câu hỏi của bạn bằng C #, vì vậy tôi đã đăng một câu trả lời C #. Tôi không biết VB có liên quan. Bất cứ ai biết nếu có một cách để sử dụng biên dịch có điều kiện để kiểm soát các thuộc tính trong VB, hoặc điều đó là không thể?
Joel Mueller

Có, nó hoạt động cho C # và nó cũng hoạt động cho VB, nhưng bạn phải thực hiện một số trùng lặp khá xấu xí đối với định nghĩa hàm / lớp. Xem câu trả lời cập nhật của tôi ở trên.
Joel Mueller

Lấy làm tiếc. Các mẫu mã VB ngày càng khó tìm hơn. Tôi không nghĩ rằng nó sẽ quan trọng. Tôi đã cập nhật câu hỏi ban đầu. Việc biên dịch có điều kiện xung quanh các thuộc tính có hoạt động chắc chắn trong C # không? Tôi chưa thử nghiệm. Đó có vẻ như là một giải pháp hoàn hảo, thanh lịch.
Zack Peterson

Mã RemoteRequireHttpsAttribute của bạn hoạt động hoàn hảo. Điều đó đối với VB thanh lịch hơn nhiều so với biên dịch có điều kiện. Cảm ơn một lần nữa Joel.
Zack Peterson

2
Cảm ơn-- đây chính xác là những gì tôi cần. Chúc mừng!
davecoulter

65

Nếu ai cần phiên bản C #:

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

ok khi đọc cái nàyđây là một biện pháp bảo mật chúng ta có nên thêm filters.Add(new MyRequireHttpsAttribute ());vào FilterConfigkhông?
shaijut

Dựa trên câu trả lời này, tôi đã tạo một giải pháp cho MVC 6 bằng cách sử dụng bộ lọc trong Startup.cs hoặc kiểu thuộc tính trên Bộ điều khiển.
Nick Niebling

26

Bắt nguồn từ RequestHttps là một cách tiếp cận tốt.

Để giải quyết triệt để vấn đề, bạn cũng có thể sử dụng IIS trên máy cục bộ của mình với chứng chỉ tự ký. IIS nhanh hơn máy chủ web tích hợp sẵn và bạn có lợi thế là môi trường phát triển của bạn giống sản xuất hơn.

Scott Hanselman có một tài nguyên tuyệt vời về một số cách triển khai HTTPS cục bộ với VS2010 và IIS Express.


ya - cho đến khi bạn cố gắng thực hiện chuyển tiếp cổng với thiết bị Mifi wifi Verizon và nhận thấy rằng cổng 443 không khả dụng để chuyển tiếp !!! # * & # * & $
Simon_Weaver

Điều tôi không thích khi sử dụng IIS trên máy cục bộ của bạn với chứng chỉ tự ký là tôi phải trải qua một bước triển khai bổ sung để kiểm tra các thay đổi. Tôi nghĩ rằng nếu bạn đang thử nghiệm một thứ gì đó liên quan đến bảo mật thì nó có ý nghĩa, nhưng nếu bạn chỉ đang kiểm tra một số thay đổi nhỏ khác, thì thật là khó khăn khi phải triển khai chỉ để tránh việc Cassini không thể hỗ trợ HTTPS.
davecoulter

1
@davecoulter - Sử dụng IIS express trên các phiên bản máy khách của windows, không cần cassini và nó sẽ hoạt động chính xác như IIS, bao gồm cả khả năng ssl.
Erik Funkenbusch

@Mystere Man - vâng, tôi đã tìm ra điều đó kể từ nhận xét đó. Cảm ơn vì mẹo :)
davecoulter

cần thêm thông tin hoặc liên kết về cách tiếp tục làm những việc như vậy.
stephenbayer

12

Tận dụng hệ thống bộ lọc MVC và Global.asax.cs, tôi cho rằng bạn có thể làm điều này ...

    protected void Application_Start()
    {
      RegisterGlobalFilters(GlobalFilters.Filters);
    }

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
      filters.Add(new HandleErrorAttribute());
      if(Config.IsProduction) //Some flag that you can tell if you are in your production environment.
      {
        filters.Add(new RequireHttpsAttribute());
      }
    }

Tôi thích câu trả lời này vì nó liên quan đến một lần kiểm tra cho mỗi vòng đời ứng dụng hơn là triển khai một bộ lọc mới sẽ được thực thi \ được gọi với mọi yêu cầu đơn lẻ.
Abdulhameed

10

Vì máy chủ phát triển ASP.Net đã gây ra sự cố của bạn ngay từ đầu, điều đáng chú ý là Microsoft hiện đã có IIS Express , đi kèm với Visual Studio (kể từ VS2010 SP1). Đây là phiên bản rút gọn của IIS dễ sử dụng như Máy chủ phát triển, nhưng hỗ trợ bộ tính năng đầy đủ của IIS 7.5 bao gồm SSL.

Scott Hanselman có một bài đăng chi tiết về cách làm việc với SSL trong IIS Express .


9

Làm thế nào về việc kế thừa thuộc tính RequestHttps trong một thuộc tính tùy chỉnh. Sau đó, bên trong thuộc tính tùy chỉnh của bạn, hãy kiểm tra thuộc tính IsLocal của yêu cầu hiện tại để xem liệu yêu cầu có đến từ máy cục bộ hay không. Nếu có, thì không áp dụng chức năng cơ bản. Nếu không, hãy gọi hoạt động cơ sở.


4

Điều này đã làm việc cho tôi, MVC 6 (ASP.NET Core 1.0) . Mã kiểm tra xem gỡ lỗi có đang được phát triển hay không, và nếu không, thì không cần ssl. Tất cả các chỉnh sửa đều có trong Startup.cs .

Thêm vào:

private IHostingEnvironment CurrentEnvironment { get; set; }

Thêm vào:

public Startup(IHostingEnvironment env)
{
    CurrentEnvironment = env;
}

Biên tập:

public void ConfigureServices(IServiceCollection services)
{
    // additional services...

    services.AddMvc(options =>
    {
        if (!CurrentEnvironment.IsDevelopment())
        {
            options.Filters.Add(typeof(RequireHttpsAttribute));
        }
    });
}

3

Nếu bạn có thể lấy và ghi đè - hãy làm điều đó. Nếu bạn không thể - MVC đi kèm với các nguồn, chỉ cần lấy các nguồn và tạo thuộc tính [ForceHttps] của riêng bạn để kiểm tra IsLocal.


3

Đối với MVC 3, tôi đã thêm FilterProvider của riêng mình (dựa trên mã tìm thấy ở đây: Bộ lọc toàn cầu và có điều kiện , trong số những thứ khác (hiển thị thông tin Gỡ lỗi cho người dùng cục bộ, v.v.) sẽ trang trí tất cả các hành động với RequireHttpsAttributekhi nào HttpContext.Request.IsLocal == false.


Hoặc bạn chỉ có thể thêm nó vào bộ lọc chung có điều kiện khi yêu cầu là cục bộ. Lưu ý rằng bạn sẽ muốn kiểm tra điều này trong khối thử / bắt nếu ứng dụng được thiết lập để khởi động ngay lập tức vì yêu cầu có thể không khả dụng.
tvanfosson

3

Sau khi nghiên cứu kỹ lưỡng, tôi đã có thể giải quyết vấn đề này bằng IIS Express và ghi đè phương thức OnAuthorization của lớp Controller (Tham khảo # 1). Tôi cũng đã đi theo tuyến đường do Hanselman đề xuất (Tham khảo # 2). Tuy nhiên, tôi không hoàn toàn hài lòng với hai giải pháp này do hai lý do: 1. OnAuthorization của Ref # 1 chỉ hoạt động ở cấp hành động, không hoạt động ở cấp độ bộ điều khiển 2. Tham khảo # 2 yêu cầu nhiều thiết lập (Win7 SDK cho makecert ), các lệnh netsh, và, để sử dụng cổng 80 và cổng 443, tôi cần khởi chạy VS2010 với tư cách quản trị viên, điều này khiến tôi băn khoăn.

Vì vậy, tôi đã đưa ra giải pháp tập trung vào sự đơn giản với các điều kiện sau:

  1. Tôi muốn có thể sử dụng phần đính kèm RequestHttps ở lớp Bộ điều khiển hoặc cấp độ hành động

  2. Tôi muốn MVC sử dụng HTTPS khi có thuộc tính RequestHttps và sử dụng HTTP nếu không có

  3. Tôi không muốn phải chạy Visual Studio với tư cách quản trị viên

  4. Tôi muốn có thể sử dụng bất kỳ cổng HTTP và HTTPS nào được chỉ định bởi IIS Express (Xem Lưu ý # 1)

  5. Tôi có thể sử dụng lại chứng chỉ SSL tự ký của IIS Express và tôi không quan tâm nếu tôi thấy lời nhắc SSL không hợp lệ

  6. Tôi muốn nhà phát triển, thử nghiệm và sản xuất có cùng một cơ sở mã chính xác và cùng một hệ nhị phân và độc lập với thiết lập bổ sung (ví dụ: sử dụng netsh, mmc cert snap-in, v.v.) càng tốt

Bây giờ, với thông tin cơ bản và giải thích rõ ràng, tôi hy vọng mã này sẽ giúp ích cho ai đó và tiết kiệm thời gian. Về cơ bản, tạo một lớp BaseController kế thừa từ Bộ điều khiển và dẫn xuất các lớp bộ điều khiển của bạn từ lớp cơ sở này. Vì bạn đã đọc đến đây, tôi cho rằng bạn biết cách làm những điều này. Vì vậy, chúc bạn mã hóa vui vẻ!

Lưu ý # 1: Điều này đạt được bằng cách sử dụng một hàm hữu ích 'getConfig' (xem mã)

Tham khảo số 1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html

Tham khảo # 2: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

========== Mã trong BaseController ===================

     #region Override to reroute to non-SSL port if controller action does not have RequireHttps attribute to save on CPU 
    // By L. Keng, 2012/08/27
    // Note that this code works with RequireHttps at the controller class or action level.
    // Credit: Various stackoverflow.com posts and http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        // if the controller class or the action has RequireHttps attribute
        var requireHttps = (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0 
                            || filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0);
        if (Request.IsSecureConnection)
        {
            // If request has a secure connection but we don't need SSL, and we are not on a child action   
            if (!requireHttps && !filterContext.IsChildAction)
            {
                var uriBuilder = new UriBuilder(Request.Url)
                {
                    Scheme = "http",
                    Port = int.Parse(getConfig("HttpPort", "80")) // grab from config; default to port 80
                };
                filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
            }
        }
        else
        {
            // If request does not have a secure connection but we need SSL, and we are not on a child action   
            if (requireHttps && !filterContext.IsChildAction)
            {
                var uriBuilder = new UriBuilder(Request.Url)
                {
                    Scheme = "https",
                    Port = int.Parse(getConfig("HttpsPort", "443")) // grab from config; default to port 443
                };
                filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
            }
        }
        base.OnAuthorization(filterContext);
    }
    #endregion

    // a useful helper function to get appSettings value; allow caller to specify a default value if one cannot be found
    internal static string getConfig(string name, string defaultValue = null)
    {
        var val = System.Configuration.ConfigurationManager.AppSettings[name];
        return (val == null ? defaultValue : val);
    }

============== mã kết thúc ================

Trong Web.Release.Config, thêm phần sau để xóa HttpPort và HttpsPort (để sử dụng mặc định 80 và 443).

<appSettings>
<add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>

3

Một giải pháp bạn có thể sử dụng trên sản xuất cũng như trên máy trạm phát triển. Nó dựa trên tùy chọn của bạn từ cài đặt ứng dụng trong web.config

<appSettings>
     <!--Use SSL port 44300 in IIS Express on development workstation-->
     <add key="UseSSL" value="44300" />
</appSettings>

Nếu bạn không muốn sử dụng SSL, hãy xóa khóa. Nếu bạn sử dụng cổng SSL tiêu chuẩn 443, thì hãy xóa giá trị hoặc chỉ định 443.

Sau đó, sử dụng triển khai tùy chỉnh của RequestHttpsAttribute để xử lý tình trạng của bạn. Nó thực sự có nguồn gốc từ RequiHttps và sử dụng cùng một cách triển khai của phương thức cơ sở ngoại trừ việc thêm điều kiện.

public class RequireHttpsConditional : RequireHttpsAttribute
{
    protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        var useSslConfig = ConfigurationManager.AppSettings["UseSSL"];
        if (useSslConfig != null)
        {
            if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            {
                throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
            }

            var request = filterContext.HttpContext.Request;
            string url = null;
            int sslPort;

            if (Int32.TryParse(useSslConfig, out sslPort) && sslPort > 0)
            {
                url = "https://" + request.Url.Host + request.RawUrl;

                if (sslPort != 443)
                {
                    var builder = new UriBuilder(url) {Port = sslPort};
                    url = builder.Uri.ToString();
                }
            }

            if (sslPort != request.Url.Port)
            {
                filterContext.Result = new RedirectResult(url);
            }
        }
    }
}

Đừng quên trang trí phương thức LogOn trong AccountController

[RequireHttpsConditional]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)

và một cái gì đó như thế này trong bạn đăng nhập Xem theo trình tự bài dạng qua https.

<% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>

tôi gặp lỗi này: XMLHttpRequest không thể tải m.XXX.com/Auth/SignIn . Không có tiêu đề 'Access-Control-Allow-Origin' có trên tài nguyên được yêu cầu. Origin ' m.XXX.com ' do đó không được phép truy cập.
Ranjith Kumar Nagiri

2

Như Joel đã đề cập, bạn có thể thay đổi quá trình biên dịch bằng cách sử dụng #if !DEBUGchỉ thị.

Tôi vừa phát hiện ra rằng bạn có thể thay đổi giá trị của biểu tượng DEBUG trong phần tử biên dịch tệp web.config. Hy vọng rằng sẽ giúp.


1

MVC 6 (ASP.NET Core 1.0):

Giải pháp thích hợp sẽ là sử dụng env.IsProduction () hoặc env.IsDevelopment (). Đọc thêm về lý do đằng sau câu trả lời này về cách chỉ yêu cầu https trong sản xuất .

Câu trả lời cô đọng bên dưới (xem liên kết ở trên để đọc thêm về các quyết định thiết kế) cho 2 phong cách khác nhau:

  1. Startup.cs - bộ lọc đăng ký
  2. BaseController - kiểu thuộc tính

Startup.cs (bộ lọc đăng ký):

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

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

BaseController.cs (kiểu thuộc tính):

[RequireHttpsInProductionAttribute]
public class BaseController : Controller
{
    // Maybe you have other shared controller logic..
}

public class HomeController : BaseController
{
    // Add endpoints (GET / POST) for Home controller
}

RequireHttpsInProductionAttribute : Cả hai trên đang sử dụng thuộc tính tùy chỉnh kế thừa từ RequireHttpsAttribute :

public class RequireHttpsInProductionAttribute : RequireHttpsAttribute
{
    private bool IsProduction { get; }

    public RequireHttpsInProductionAttribute(IHostingEnvironment environment)
    {
        if (environment == null)
            throw new ArgumentNullException(nameof(environment));
        this.IsProduction = environment.IsProduction(); 
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (this.IsProduction)
            base.OnAuthorization(filterContext);
    }
    protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        if(this.IsProduction)
            base.HandleNonHttpsRequest(filterContext);
    }
}

1

Đây là cách sạch sẽ nhất đối với tôi. Trong App_Start\FilterConfig.cshồ sơ của tôi . Không thể chạy các bản dựng phát hành nữa.

... 
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        if (!Web.HttpContext.Current.IsDebuggingEnabled) {
            filters.Add(new RequireHttpsAttribute());   
        }
        ...
}

Ngoài ra, bạn có thể đặt nó thành chỉ yêu cầu https khi trang lỗi tùy chỉnh của bạn được bật.

... 
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        if (Web.HttpContext.Current.IsCustomErrorEnabled) {
            filters.Add(new RequireHttpsAttribute());   
        }
        ...
}

Đây là một giải pháp dễ dàng hoạt động tuyệt vời trong MVC 5 :)
MWD

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.