在ASP.NET中强制整个站点使用HTTPS的最佳方法是什么?

211
大约6个月前,我推出了一个网站,每个请求都需要通过https进行。当时我找到的唯一确保每个页面请求都经过https的方法是在页面加载事件中进行检查。如果请求不是通过https发送的,我会使用response.redirect("https://example.com")进行响应重定向。

有没有更好的方法--最好是在web.config中设置?


请查看我的答案:https://dev59.com/OZHea4cB1Zd3GeqPomME#33882351 - Shady Mohamed Sherif
16个回答

275
请使用HSTS(HTTP严格传输安全)
来自http://www.hanselman.com/blog/HowToEnableHTTPStrictTransportSecurityHSTSInIIS7.aspx
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="HTTP to HTTPS redirect" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
                        redirectType="Permanent" />
                </rule>
            </rules>
            <outboundRules>
                <rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
                    <match serverVariable="RESPONSE_Strict_Transport_Security"
                        pattern=".*" />
                    <conditions>
                        <add input="{HTTPS}" pattern="on" ignoreCase="true" />
                    </conditions>
                    <action type="Rewrite" value="max-age=31536000" />
                </rule>
            </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>

原始答案(于2015年12月4日更改为上述内容)

基本上

protected void Application_BeginRequest(Object sender, EventArgs e)
{
   if (HttpContext.Current.Request.IsSecureConnection.Equals(false) && HttpContext.Current.Request.IsLocal.Equals(false))
   {
    Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"]
+   HttpContext.Current.Request.RawUrl);
   }
}

这将放置在global.asax.cs(或global.asax.vb)中

我不知道在web.config中指定的方法


9
这段代码虽然可用,但对我来说很危险:当我试图在本地使用VS 2010运行此代码时,我的起始页面从未加载;相反,我只收到一个“此网页不可用”的消息。为了解决这个问题,我添加了第二个条件来测试URL是否包含字符串“localhost”:如果不包含,则强制使用HTTPS。 - mg1075
3
这给我造成了重定向循环。在添加这段代码之前它运行得很好。有任何建议吗? - Joe
13
请注意,这样做并没有提供任何有用的安全保障。实际上,它只能保护那些已经安全的用户的连接,并无法保护正在遭受攻击的用户(因为中间人攻击者可以完全忽略重定向并将所有内容转发到您的“安全”站点)。在我看来,重定向用户代理只是一种心理安慰,提供了一种有时非常危险的安全幻觉。唯一有效的方法是指示用户代理只请求安全资源,如果它们不符合条件,则不进行重定向。这就是HSTS所做的事情——请参见下面的答案。 - tne
4
此回答应被视为“有害的”,不应使用。如上面@tne的评论所述。 - Rosdi Kasim
2
@RosdiKasim 自2015年12月4日的编辑以来,这个答案是否仍然应被视为有害? - Andrew Morton
显示剩余12条评论

130

另外一件你可以做的事情是使用HSTS,通过向浏览器返回“Strict-Transport-Security”标头。浏览器必须支持此功能(目前主要是Chrome和Firefox),但这意味着一旦设置,浏览器将不会使用HTTP向网站发出请求,而是在发出请求之前将其转换为HTTPS请求。尝试将其与从HTTP重定向配合使用:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
  switch (Request.Url.Scheme)
  {
    case "https":
      Response.AddHeader("Strict-Transport-Security", "max-age=300");
      break;
    case "http":
      var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
      Response.Status = "301 Moved Permanently";
      Response.AddHeader("Location", path);
      break;
  }
}

不支持HSTS的浏览器将只会忽略该头信息,但仍会被 switch 语句捕获并重定向到HTTPS。


6
之前从未听说过 HSTS header,但看起来很不错。为什么要使用这么小的 max-age 值(5 分钟)呢?你提供的维基百科文章建议将其设置为较大的值(6-12 个月)。 - dana
5
请查看Troy博客上这篇非常详细的文章,其中包含有关仅使用重定向可能会降低安全性的详细信息。提示:它可能会让您容易受到SSL剥离工具等攻击。链接:http://www.troyhunt.com/2011/11/owasp-top-10-for-net-developers-part-9.html - Oran Dennison
3
另外值得一提的是NWebsec,它使得这个(以及更多)变得非常容易。 - Tieson T.
18
为了避免破坏调试过程,建议将这个开关包裹在 if(!Request.IsLocal) 中。 - Justin J Stark
1
好的回答。有一个微妙之处 - 对于Http头(“Strict-Transport-Security”),最好使用像NWebSec这样的库,因为有多个选项集中在一个配置位置,而不是到处分散。 - Ognyan Dimitrov
显示剩余5条评论

92

这个 IIS7 模块可以让你进行重定向。

    <rewrite>
        <rules>
            <rule name="Redirect HTTP to HTTPS" stopProcessing="true">
                <match url="(.*)"/>
                <conditions>
                    <add input="{HTTPS}" pattern="^OFF$"/>
                </conditions>
                <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="SeeOther"/>
            </rule>
        </rules>
    </rewrite>

13
对于 IIS 7.0,您需要安装 Url Rewrite 模块 2.0。 - Chris
我发现这个链接简单易懂,对于让特定页面仅接受https请求非常有帮助 - http://support.microsoft.com/kb/239875 - Manik Arora

25
对于使用ASP.NET MVC的用户,您可以通过以下两种方式之一强制在整个站点上使用SSL/TLS来进行HTTPS:

较为困难的方式

1 - 将RequireHttpsAttribute添加到全局过滤器中:

GlobalFilters.Filters.Add(new RequireHttpsAttribute());

2- 强制使用SSL / TLS的防伪令牌:

AntiForgeryConfig.RequireSsl = true;

3 - 通过更改Web.config文件,要求Cookies默认使用HTTPS:

<system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>

4 - 使用NWebSec.Owin NuGet包,并添加以下代码行以启用整个站点的严格传输安全性。不要忘记在下面添加Preload指令并将您的站点提交到HSTS Preload site。更多信息herehere。请注意,如果您没有使用OWIN,则可以在NWebSec网站上查看Web.config方法。

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHsts(options => options.MaxAge(days: 30).Preload());

5 - 使用NWebSec.Owin NuGet包,并添加以下代码行以在整个站点上启用公钥固定(HPKP)。更多信息这里这里

// app is your OWIN IAppBuilder app in Startup.cs
app.UseHpkp(options => options
    .Sha256Pins(
        "Base64 encoded SHA-256 hash of your first certificate e.g. cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
        "Base64 encoded SHA-256 hash of your second backup certificate e.g. M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=")
    .MaxAge(days: 30));

6 - 在使用任何URL时,请包含https方案。 内容安全策略(CSP) HTTP标头和子资源完整性(SRI)在某些浏览器中无法正确处理模拟方案。更好的方法是明确使用HTTPS。例如:

<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js"></script>

简单快捷的方法

使用ASP.NET MVC Boilerplate Visual Studio项目模板生成一个已包含所有这些内容和更多功能的项目。你也可以在GitHub上查看代码。


3
如果使用<authentication mode="Forms">,则需要在内部添加<forms requireSSL="true"> - Pluto
1
@muhammad-rehan-saeed 我正在使用mvc5脚手架,但是在生产服务器上网站没有自动将http重定向到https,只有在本地主机上才会这样做。我是否遗漏了什么? - Diin
这不是问这个问题的正确论坛。请在 GitHub 网站上发布一个 issue。RequireHttpsAttribute会重定向。只要你有它,就应该没问题了。 - Muhammad Rehan Saeed
@MuhammadRehanSaeed,很喜欢你的回答。但是...我如何获取使用MakeCert创建的证书的SHA256哈希值?我只有一个SHA-1指纹...你知道吗? - Diana
1
@Diana 这个链接可以向你展示如何进行 HPKP(HTTP 公钥固定)。 - Muhammad Rehan Saeed

13
如果由于任何原因无法在IIS中设置此项,您可以创建一个HTTP模块来为您执行重定向。
using System;
using System.Web;

namespace HttpsOnly
{
    /// <summary>
    /// Redirects the Request to HTTPS if it comes in on an insecure channel.
    /// </summary>
    public class HttpsOnlyModule : IHttpModule
    {
        public void Init(HttpApplication app)
        {
            // Note we cannot trust IsSecureConnection when 
            // in a webfarm, because usually only the load balancer 
            // will come in on a secure port the request will be then 
            // internally redirected to local machine on a specified port.

            // Move this to a config file, if your behind a farm, 
            // set this to the local port used internally.
            int specialPort = 443;

            if (!app.Context.Request.IsSecureConnection 
               || app.Context.Request.Url.Port != specialPort)
            {
               app.Context.Response.Redirect("https://" 
                  + app.Context.Request.ServerVariables["HTTP_HOST"] 
                  + app.Context.Request.RawUrl);    
            }
        }

        public void Dispose()
        {
            // Needed for IHttpModule
        }
    }
}

然后将其编译为DLL,将其添加为项目的引用,并将其放置在web.config中:

 <httpModules>
      <add name="HttpsOnlyModule" type="HttpsOnly.HttpsOnlyModule, HttpsOnly" />
 </httpModules>

这似乎比仅仅将其放入global.asax中更为复杂 - 只是好奇,有什么优势吗? - Brian MacKay
1
优点在于,当您不想使用它时,只需在web.config中注释掉该模块即可。这个解决方案是可配置的,而另一个则不是。 - Bob Yexley
2
我有点困惑。我原本期望在Init方法中会出现类似于app.BeginRequest += new OnBeginRequest;的代码,并且OnBeginRequest方法会包含当前Init方法所包含的内容。你确定这个模块能够正常工作吗? - Jakub Šturc
它不起作用。你需要添加OnBeginRequest事件等,然后它才能工作。 - SnAzBaZ
我会编辑这个有问题的代码,但为了使其更安全,您还需要使用HSTS。只需采用Troy Hunt的答案并将其制作成模块即可;请参见https://support.microsoft.com/en-us/kb/307996(虽然有点老,但仍然很好)。 - Marc L.

5
您需要做的是:
1)根据生产或阶段服务器,在web.config中添加一个键,如下所示
<add key="HttpsServer" value="stage"/>
             or
<add key="HttpsServer" value="prod"/>

2) 在您的Global.asax文件中添加以下方法。

void Application_BeginRequest(Object sender, EventArgs e)
{
    //if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "prod")
    if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "stage")
    {
        if (!HttpContext.Current.Request.IsSecureConnection)
        {
            if (!Request.Url.GetLeftPart(UriPartial.Authority).Contains("www"))
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://www."), true);
            }
            else
            {
                HttpContext.Current.Response.Redirect(
                    Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://"), true);
            }
        }
    }
}

5
在IIS10(Windows 10和Server 2016)中,从版本1709开始,有一个新的更简单的选项可以为网站启用HSTS。
Microsoft在这里描述了新方法的优点,并提供了许多不同的示例,说明如何通过编程或直接编辑ApplicationHost.config文件(类似于web.config但在IIS级别而不是单个站点级别上运行)来实现更改。 ApplicationHost.config可在C:\ Windows \ System32 \ inetsrv \ config中找到。
我在此概述了两种示例方法,以避免链接失效。 方法1 - 直接编辑ApplicationHost.config文件,在<site>标签之间添加此行:
<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />

方法2 - 命令行: 在管理员权限的命令提示符中执行以下命令(即右键单击CMD并以管理员身份运行)。请记得将Contoso替换为在IIS管理器中显示的您的站点名称。

c:
cd C:\WINDOWS\system32\inetsrv\
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.enabled:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.max-age:31536000" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.includeSubDomains:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.redirectHttpToHttps:True" /commit:apphost

如果您在托管环境中的访问权限受限,那么该文章中微软提供的其他方法可能是更好的选择。

请记住,IIS10版本1709现在可在Windows 10上使用,但对于Windows Server 2016来说,它处于不同的发布轨道,并且不会作为补丁或服务包发布。有关1709的详细信息,请参见此处


3

如果您的网站不支持配置SSL(即应该能够打开/关闭https)- 您可以在任何您希望保护的控制器/控制器操作上使用[RequireHttps]属性。


3

这是基于@Troy Hunt的更完整的答案。将此函数添加到Global.asax.cs文件中的WebApplication类中:

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        // Allow https pages in debugging
        if (Request.IsLocal)
        {
            if (Request.Url.Scheme == "http")
            {
                int localSslPort = 44362; // Your local IIS port for HTTPS

                var path = "https://" + Request.Url.Host + ":" + localSslPort + Request.Url.PathAndQuery;

                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", path);
            }
        }
        else
        {
            switch (Request.Url.Scheme)
            {
                case "https":
                    Response.AddHeader("Strict-Transport-Security", "max-age=31536000");
                    break;
                case "http":
                    var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
                    Response.Status = "301 Moved Permanently";
                    Response.AddHeader("Location", path);
                    break;
            }
        }
    }

要在本地构建中启用SSL,请在项目的“属性”窗口中启用它。


2

我花了一些时间寻找有意义的最佳实践,并发现以下内容对我非常有效。希望这可以为您节省一些时间。

使用配置文件(例如asp.net网站) https://blogs.msdn.microsoft.com/kaushal/2013/05/22/http-to-https-redirects-on-iis-7-x-and-higher/

或在您自己的服务器上 https://www.sslshopper.com/iis7-redirect-http-to-https.html

[简短回答] 只需将下面的代码放入内部即可:

<system.webServer> 
 <rewrite>
     <rules>
       <rule name="HTTP/S to HTTPS Redirect" enabled="true" 
           stopProcessing="true">
       <match url="(.*)" />
        <conditions logicalGrouping="MatchAny">
        <add input="{SERVER_PORT_SECURE}" pattern="^0$" />
       </conditions>
       <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" 
        redirectType="Permanent" />
        </rule>
       </rules>
 </rewrite>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接