Yalnızca Üretimde ASP.NET MVC RequireHttps


121

RequireHttpsAttribute kullanmak istiyorumGüvenli olmayan HTTP isteklerinin bir eylem yöntemine gönderilmesini önlemek .

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

Maalesef, ASP.NET Geliştirme Sunucusu HTTPS'yi desteklemiyor.

ASP.NET MVC uygulamamın üretim ortamına yayımlandığında RequireHttps kullanmasını ancak ASP.NET Geliştirme Sunucusundaki geliştirme iş istasyonumda çalıştırmadığında nasıl olmasını sağlayabilirim?


Yanıtlar:


129

Geliştirme iş istasyonunuzda Sürüm derlemeleri çalıştırırsanız bu yardımcı olmaz, ancak koşullu derleme işi yapabilir ...

#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()
    {
    }

}

Güncelleme

Visual Basic'te, öznitelikler teknik olarak uygulandıkları tanımla aynı satırın parçasıdır. Koşullu derleme ifadelerini bir satırın içine koyamazsınız, bu nedenle işlev bildirimini iki kez yazmak zorunda kalırsınız - biri öznitelikle, diğeri olmadan. Çirkinliğe aldırmazsan işe yarıyor.

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

Güncelleme 2

Birkaç kişi RequireHttpsAttributebir örnek vermeden türetmekten bahsetti , işte size bir tane. Bu yaklaşımın şartlı derleme yaklaşımından çok daha temiz olacağını düşünüyorum ve sizin konumunuzda tercihim olacaktır.

SORUMLULUK REDDİ: Bu kodu biraz bile test etmedim ve VB'm oldukça paslı. Tek bildiğim, derlediği. Spot, queen3 ve Lance Fisher'ın önerilerine dayanarak yazdım. İşe yaramazsa, en azından genel fikri aktarmalı ve size başlangıç ​​noktası vermelidir.

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

Temel olarak, mevcut istek yerelse (yani, siteye localhost üzerinden erişiyorsanız), yeni öznitelik varsayılan SSL yetkilendirme kodunu çalıştırmak yerine çıkış yapar. Bunu şu şekilde kullanabilirsiniz:

<RemoteRequireHttps()> _
Public Class SomeController

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

End Class

Çok daha temiz! Test edilmemiş kodumun gerçekten çalışması şartıyla.


Gönderimi benim için düzenlediğin için teşekkürler, Zack. Sorunuz C # dilinde olduğu için bir C # yanıtı gönderdim. VB'nin alakalı olduğunu bilmiyordum. VB'deki öznitelikleri kontrol etmek için koşullu derlemeyi kullanmanın bir yolu olup olmadığını bilen var mı, yoksa bu mümkün değil mi?
Joel Mueller

Evet, C # için çalışıyor ve VB için de çalışıyor, ancak işlev / sınıf tanımının oldukça çirkin bir kopyasını yapmanız gerekiyor. Yukarıdaki güncellenmiş cevabıma bakın.
Joel Mueller

Afedersiniz. VB kod örneklerinin elde edilmesi gittikçe zorlaşıyor. Önemli olacağını düşünmedim. Orijinal soruyu güncelledim. Öznitelikler etrafında koşullu derleme C # 'da kesin olarak çalışıyor mu? Test etmedim. Bu mükemmel ve zarif bir çözüm gibi görünüyor.
Zack Peterson

RemoteRequireHttpsAttribute kodunuz mükemmel çalışıyor. Bu, VB için koşullu derlemeden çok daha zariftir. Tekrar teşekkürler Joel.
Zack Peterson

2
Teşekkürler-- bu tam olarak ihtiyacım olan şeydi. Şerefe!
davecoulter

65

C # sürümüne ihtiyacı varsa:

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

okurken Tamam bu ve bu bir güvenlik önlemi olarak biz eklemek gerekir filters.Add(new MyRequireHttpsAttribute ());içinde FilterConfig?
shaijut

Bu cevaba dayanarak MVC 6 için Startup.cs içindeki filtreyi veya Controller üzerindeki öznitelik stilini kullanarak bir çözüm oluşturdum .
Nick Niebling

26

RequireHttps'tan türetmek iyi bir yaklaşımdır.

Sorunu tamamen ortadan kaldırmak için, IIS'yi yerel makinenizde kendinden imzalı bir sertifika ile de kullanabilirsiniz. IIS, yerleşik web sunucusundan daha hızlıdır ve geliştirme ortamınızın daha çok üretime benzemesi avantajına sahipsiniz.

Scott Hanselman, VS2010 ve IIS Express ile yerel HTTPS'yi uygulamanın birkaç yolu hakkında harika bir kaynağa sahiptir.


ya - bir Mifi wifi Verizon cihazıyla bağlantı noktası yönlendirmeyi deneyene ve 443 numaralı bağlantı noktasının yönlendirilemeyeceğini bulana kadar !!! # * & # * & $
Simon_Weaver

Yerel makinenizde IIS'yi kendinden imzalı bir sertifika ile kullanmaktan hoşlanmadığım şey, değişiklikleri test etmek için fazladan bir dağıtım adımından geçmem gerekmesidir. Bence güvenlikle ilgili bir şeyi mantıklı olandan daha fazla test ediyorsanız, ancak sadece başka küçük bir değişikliği kontrol ediyorsanız, Cassini'nin HTTPS'yi destekleyememesini aşmak için dağıtmak zorunda kalmanın bir acı olduğunu düşünüyorum.
davecoulter

1
@davecoulter - Windows istemci sürümlerinde IIS express kullanın, cassini gerekmez ve ssl özelliği dahil olmak üzere tam olarak IIS gibi çalışacaktır.
Erik Funkenbusch

@Mystere Man - evet, bunu o yorumdan beri buldum.
Tavsiye

Bu tür şeylerin nasıl yapılacağına dair daha fazla bilgi veya bağlantı eklenmelidir.
stephenbayer

12

MVC filtre sistemini ve Global.asax.cs'yi kullanarak, bunu yapabileceğinizi varsayıyorum ...

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

Bu yanıtı, her istekte yürütülecek \ çağrılan yeni bir filtre uygulamak yerine, uygulama ömrü başına bir denetim içerdiğinden tercih ediyorum.
Abdulhameed

10

Sorununuza ilk etapta neden olan ASP.Net Geliştirme Sunucusu olduğundan, Microsoft'un artık Visual Studio ile birlikte gelen (VS2010 SP1'den beri) IIS Express'e sahip olduğunu belirtmek gerekir . Bu, Geliştirme Sunucusu kadar kullanımı kolay, ancak SSL dahil IIS 7.5'in tüm özellik kümesini destekleyen IIS'nin kısaltılmış bir sürümüdür.

Scott Hanselman'ın IIS Express'te SSL ile çalışma konusunda ayrıntılı bir yazısı var .


9

Özel bir öznitelikte RequireHttps özniteliğini devralmaya ne dersiniz? Ardından, özel özniteliğinizin içinde, isteğin yerel makineden gelip gelmediğini görmek için geçerli isteğin IsLocal özelliğini kontrol edin. Öyleyse, temel işlevi uygulamayın. Aksi takdirde, temel operasyonu arayın.


4

Bu benim için çalıştı, MVC 6 (ASP.NET Core 1.0) . Kod, hata ayıklamanın geliştirilme aşamasında olup olmadığını denetler ve değilse, ssl gerekmez. Tüm düzenlemeler Startup.cs içindedir .

Ekle:

private IHostingEnvironment CurrentEnvironment { get; set; }

Ekle:

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

Düzenle:

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

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

3

Eğer türetebilir ve geçersiz kılabiliyorsanız - yapın. Yapamıyorsanız - MVC kaynaklarla birlikte gelir, sadece kaynakları alın ve IsLocal'ı kontrol eden kendi [ForceHttps] özniteliğinizi oluşturun.


3

MVC 3 için kendi FilterProvider'ımı ekledim (burada bulunan koda dayalı olarak: Global ve Koşullu Filtreler , diğer şeylerin yanı sıra (yerel kullanıcılar için Hata Ayıklama bilgilerini görüntüleme vb.) Tüm eylemleri RequireHttpsAttributene zaman ile süsleyecek HttpContext.Request.IsLocal == false.


Veya istek yerel olduğunda bunu koşullu olarak genel filtre koleksiyonuna ekleyebilirsiniz. Uygulama hemen başlayacak şekilde ayarlanmışsa, istek kullanılamayabileceğinden bunu bir dene / yakala bloğunda kontrol etmek isteyeceğinizi unutmayın.
tvanfosson

3

Araştırma yaptıktan sonra, bu sorunu IIS Express ve Controller sınıfının OnAuthorization yöntemini geçersiz kılarak çözebildim (Ref # 1). Ben de Hanselman tarafından önerilen rotaya gittim (Ref # 2). Ancak, iki nedenden dolayı bu iki çözümden tam olarak memnun kalmadım: 1. Ref # 1'in OnAuthorization, denetleyici sınıfı düzeyinde değil, yalnızca eylem düzeyinde çalışır 2. Ref # 2, çok fazla kurulum gerektirir (makecert için Win7 SDK ), netsh komutları ve 80 numaralı bağlantı noktasını ve 443 numaralı bağlantı noktasını kullanmak için VS2010'u yönetici olarak başlatmam gerekiyor, buna kaşlarını çatıyorum.

Bu nedenle, aşağıdaki koşullarla sadeliğe odaklanan bu çözümü buldum:

  1. Denetleyici sınıfında veya eylem düzeyinde RequireHttps özniteliğini kullanabilmek istiyorum

  2. MVC'nin RequireHttps özniteliği varken HTTPS kullanmasını ve yoksa HTTP kullanmasını istiyorum

  3. Visual Studio'yu yönetici olarak çalıştırmak zorunda kalmak istemiyorum

  4. IIS Express tarafından atanan herhangi bir HTTP ve HTTPS bağlantı noktasını kullanabilmek istiyorum (Bkz. Not 1)

  5. IIS Express'in kendinden imzalı SSL sertifikasını yeniden kullanabilirim ve geçersiz SSL istemini görmem umurumda değil

  6. Geliştirme, test ve üretimin tamamen aynı kod tabanına ve aynı ikili programa sahip olmasını ve ek kurulumdan (ör. Netsh, mmc cert ek bileşenini kullanarak vb.) Bağımsız olmasını istiyorum.

Şimdi, arka plan ve açıklamanın dışında, umarım bu kod birisine yardımcı olur ve biraz zaman kazandırır. Temel olarak, Controller'dan miras alan bir BaseController sınıfı oluşturun ve denetleyici sınıflarınızı bu temel sınıftan türetin. Şimdiye kadar okuduğunuza göre, bunları nasıl yapacağınızı bildiğinizi varsayıyorum. Öyleyse, mutlu kodlamalar!

Not 1: Bu, yararlı bir 'getConfig' işlevi kullanılarak elde edilir (koda bakın)

Referans # 1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html

Referans # 2: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

========== BaseController'daki Kod =====================

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

============== bitiş kodu ================

Web.Release.Config'de, HttpPort ve HttpsPort'u temizlemek için aşağıdakileri ekleyin (varsayılan 80 ve 443'ü kullanmak için).

<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

Üretimde ve geliştirme iş istasyonunda kullanabileceğiniz bir çözüm. Web.config'deki uygulama ayarlarındaki seçeneğinize bağlıdır.

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

SSL kullanmak istemiyorsanız anahtarı kaldırın. Standart SSL bağlantı noktası 443 kullanıyorsanız, değeri kaldırın veya 443'ü belirtin.

Ardından , durumunuzla ilgilenen özel RequireHttpsAttribute uygulamasını kullanın . Aslında RequireHttps'den türetilmiştir ve koşul ekleme dışında temel yöntemin aynı uygulamasını kullanır.

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

AccountController'da LogOn yöntemini dekore etmeyi unutmayın

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

formunuzu https üzerinden göndermek için LogOn Görünümünüzde buna benzer bir şey .

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

şu hatayı alıyorum: XMLHttpRequest m.XXX.com/Auth/SignIn'i yükleyemiyor . İstenen kaynakta 'Access-Control-Allow-Origin' başlığı yok. Menşe ' m.XXX.com ' erişimine bu nedenle izin verilmiyor.
Ranjith Kumar Nagiri

2

Joel'in bahsettiği gibi, #if !DEBUGyönergeyi kullanarak derlemeyi değiştirebilirsiniz .

Web.config dosyası derleme öğesindeki DEBUG sembolünün değerini değiştirebileceğinizi öğrendim. Umarım yardımcı olur.


1

MVC 6 (ASP.NET Core 1.0):

Uygun çözüm env.IsProduction () veya env.IsDevelopment () kullanmak olacaktır. Https'nin yalnızca üretimde nasıl gerekli olacağına ilişkin bu yanıtın arkasındaki neden hakkında daha fazla bilgi edinin. .

2 farklı stil için aşağıdaki kısaltılmış cevap (tasarım kararları hakkında daha fazla bilgi için yukarıdaki bağlantıya bakın):

  1. Startup.cs - kayıt filtresi
  2. BaseController - öznitelik stili

Startup.cs (kayıt filtresi):

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

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

BaseController.cs (öznitelik stili):

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

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

RequireHttpsInProductionAttribute : Yukarıdakilerin her ikisi de RequireHttpsAttribute'tan devralan özel özniteliği kullanıyor :

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

Bu benim için en temiz yoldu. Benim içinde App_Start\FilterConfig.csdosyaya. Artık sürüm derlemeleri çalıştıramıyor.

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

Alternatif olarak, bunu yalnızca özel hata sayfanız açıkken https gerektirecek şekilde ayarlayabilirsiniz.

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

Bu, MVC 5'te harika çalışan kolay bir çözüm :)
MWD

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.