Adding the ability to enable HTTP Strict Transport Security from the SSL module, fixes #6612

This commit is contained in:
Lombiq
2016-11-24 23:38:20 +01:00
committed by Zoltán Lehóczky
parent e6d4d0c9d4
commit eed3ee6871
6 changed files with 116 additions and 8 deletions

View File

@@ -30,6 +30,9 @@ namespace Orchard.SecureSocketsLayer.Drivers {
protected override DriverResult Editor(SslSettingsPart part, IUpdateModel updater, dynamic shapeHelper) { protected override DriverResult Editor(SslSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
if (updater.TryUpdateModel(part, Prefix, null, null)) { if (updater.TryUpdateModel(part, Prefix, null, null)) {
_signals.Trigger(SslSettingsPart.CacheKey); _signals.Trigger(SslSettingsPart.CacheKey);
if (!part.Enabled) part.SecureEverything = false;
if (!part.SecureEverything) part.SendStrictTransportSecurityHeaders = false;
if (!part.StrictTransportSecurityIncludeSubdomains) part.StrictTransportSecurityPreload = false;
} }
return Editor(part, shapeHelper); return Editor(part, shapeHelper);

View File

@@ -16,8 +16,7 @@ namespace Orchard.SecureSocketsLayer.Models {
public class SslSettingsPart : ContentPart { public class SslSettingsPart : ContentPart {
public const string CacheKey = "SslSettingsPart"; public const string CacheKey = "SslSettingsPart";
public string Urls public string Urls {
{
get { return this.As<InfosetPart>().Get<SslSettingsPart>("Urls"); } get { return this.As<InfosetPart>().Get<SslSettingsPart>("Urls"); }
set { this.As<InfosetPart>().Set<SslSettingsPart>("Urls", value); } set { this.As<InfosetPart>().Set<SslSettingsPart>("Urls", value); }
} }
@@ -57,5 +56,26 @@ namespace Orchard.SecureSocketsLayer.Models {
get { return this.As<InfosetPart>().Get<SslSettingsPart>("InsecureHostName"); } get { return this.As<InfosetPart>().Get<SslSettingsPart>("InsecureHostName"); }
set { this.As<InfosetPart>().Set<SslSettingsPart>("InsecureHostName", value); } set { this.As<InfosetPart>().Set<SslSettingsPart>("InsecureHostName", value); }
} }
public bool SendStrictTransportSecurityHeaders {
get { return this.Retrieve(x => x.SendStrictTransportSecurityHeaders); }
set { this.Store(x => x.SendStrictTransportSecurityHeaders, value); }
}
public bool StrictTransportSecurityIncludeSubdomains {
get { return this.Retrieve(x => x.StrictTransportSecurityIncludeSubdomains); }
set { this.Store(x => x.StrictTransportSecurityIncludeSubdomains, value); }
}
[Required]
public int StrictTransportSecurityMaxAge {
get { return this.Retrieve(x => x.StrictTransportSecurityMaxAge, 31536000); }
set { this.Store(x => x.StrictTransportSecurityMaxAge, value); }
}
public bool StrictTransportSecurityPreload {
get { return this.Retrieve(x => x.StrictTransportSecurityPreload); }
set { this.Store(x => x.StrictTransportSecurityPreload, value); }
}
} }
} }

View File

@@ -49,10 +49,18 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath> <HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.ComponentModel.DataAnnotations"> <Reference Include="System.ComponentModel.DataAnnotations">
@@ -96,6 +104,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\StrictTransportSecurityMiddlewareProvider.cs" />
<Content Include="Module.txt" /> <Content Include="Module.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Logging;
using Orchard.Owin;
using Orchard.SecureSocketsLayer.Models;
using Owin;
namespace Orchard.SecureSocketsLayer.Services {
public class StrictTransportSecurityMiddlewareProvider : IOwinMiddlewareProvider {
private readonly IWorkContextAccessor _wca;
public ILogger Logger { get; set; }
public StrictTransportSecurityMiddlewareProvider(IWorkContextAccessor wca) {
_wca = wca;
Logger = NullLogger.Instance;
}
public IEnumerable<OwinMiddlewareRegistration> GetOwinMiddlewares() {
return new[] {
new OwinMiddlewareRegistration {
Configure = app =>
app.Use(async (context, next) => {
var sslSettings = _wca.GetContext().CurrentSite.As<SslSettingsPart>();
if (sslSettings.SendStrictTransportSecurityHeaders) {
string responseValue = "max-age=" + sslSettings.StrictTransportSecurityMaxAge;
if (sslSettings.StrictTransportSecurityIncludeSubdomains) {
responseValue += "; includeSubDomains";
}
if (sslSettings.StrictTransportSecurityPreload) {
responseValue += "; preload";
}
context.Response.Headers.Append("Strict-Transport-Security", responseValue);
}
await next.Invoke();
})
}
};
}
}
}

View File

@@ -11,18 +11,47 @@
<div> <div>
<label for="@Html.FieldIdFor(m => m.SecureHostName)">@T("Secure Host Name")</label> <label for="@Html.FieldIdFor(m => m.SecureHostName)">@T("Secure Host Name")</label>
@Html.TextBoxFor(m => m.SecureHostName, new { @class = "textMedium" }) @Html.TextBoxFor(m => m.SecureHostName, new { @class = "textMedium" })
<span class="hint">@T("(Mandatory) The host name secured traffic should be redirected to (e.g. localhost:44300, secure.mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. secure.127-0-0-1.org.uk:4333).")</span> @Html.Hint(T("(Mandatory) The host name secured traffic should be redirected to (e.g. localhost:44300, secure.mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. secure.127-0-0-1.org.uk:4333)."))
</div> </div>
<div> <div>
<label for="@Html.FieldIdFor(m => m.InsecureHostName)">@T("Insecure Host Name")</label> <label for="@Html.FieldIdFor(m => m.InsecureHostName)">@T("Insecure Host Name")</label>
@Html.TextBoxFor(m => m.InsecureHostName, new { @class = "textMedium" }) @Html.TextBoxFor(m => m.InsecureHostName, new { @class = "textMedium" })
<span class="hint">@T("(Mandatory) The host name non-secured traffic should be redirected to (e.g. localhost:30321, mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. dev.127-0-0-1.org.uk:4333).")</span> @Html.Hint(T("(Mandatory) The host name non-secured traffic should be redirected to (e.g. localhost:30321, mydomain.com). Don't include the protocol or anything else than the host name. A port can be specified after a colon if necessary (e.g. dev.127-0-0-1.org.uk:4333)."))
</div> </div>
<div> <div>
@Html.EditorFor(m => m.SecureEverything) @Html.EditorFor(m => m.SecureEverything)
<label for="@Html.FieldIdFor(m => m.SecureEverything)" class="forcheckbox">@T("Force SSL on all pages")</label> <label for="@Html.FieldIdFor(m => m.SecureEverything)" class="forcheckbox">@T("Force SSL on all pages")</label>
@Html.ValidationMessage("SecureEverything", "*") @Html.ValidationMessage("SecureEverything", "*")
</div> </div>
<div data-controllerid="@Html.FieldIdFor(m => m.SecureEverything)">
<div>
@Html.EditorFor(m => m.SendStrictTransportSecurityHeaders)
<label for="@Html.FieldIdFor(m => m.SendStrictTransportSecurityHeaders)" class="forcheckbox">@T("Enable HTTP Strict Transport Security")</label>
@Html.Hint(@T("<a href='https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security' target='_blank'>More information about HSTS</a>"))
@Html.ValidationMessage("SendStrictTransportSecurityHeaders", "*")
</div>
<div data-controllerid="@Html.FieldIdFor(m => m.SendStrictTransportSecurityHeaders)">
<div>
<label for="@Html.FieldIdFor(m => m.StrictTransportSecurityMaxAge)">@T("Expiration time")</label>
@Html.TextBoxFor(m => m.StrictTransportSecurityMaxAge, new { @class = "textMedium" })
@Html.Hint(T("(Mandatory) Specify the max age of HSTS in seconds (e.g. 31536000 is one year). If you set this parameter to 0, the HSTS Header will immediately expire."))
</div>
<div>
@Html.EditorFor(m => m.StrictTransportSecurityIncludeSubdomains)
<label for="@Html.FieldIdFor(m => m.StrictTransportSecurityIncludeSubdomains)" class="forcheckbox">@T("Include Subdomains")</label>
@Html.ValidationMessage("StrictTransportSecurityIncludeSubdomains", "*")
@Html.Hint(T("Applies the HSTS rules to all of the site's subdomains."))
</div>
<div data-controllerid="@Html.FieldIdFor(m => m.StrictTransportSecurityIncludeSubdomains)">
<div>
@Html.EditorFor(m => m.StrictTransportSecurityPreload)
<label for="@Html.FieldIdFor(m => m.StrictTransportSecurityPreload)" class="forcheckbox">@T("Preload")</label>
@Html.ValidationMessage("StrictTransportSecurityPreload", "*")
@Html.Hint(T("You can use this if you successfully submitted your domain to an HSTS preload service and serve all subdomains over HTTPS. If you send this directive, it can have PERMANENT CONSEQUENCES. Please read the <a href='https://hstspreload.appspot.com/' target='_blank'>details</a> before sending the header with \"preload\"."))
</div>
</div>
</div>
</div>
<div> <div>
<div> <div>
@Html.EditorFor(m => m.CustomEnabled) @Html.EditorFor(m => m.CustomEnabled)
@@ -34,11 +63,10 @@
<label for="@Html.FieldIdFor(m => m.Urls)">@T("Urls")</label> <label for="@Html.FieldIdFor(m => m.Urls)">@T("Urls")</label>
@Html.TextAreaFor(m => m.Urls, new { @class = "textMedium", rows = "5" }) @Html.TextAreaFor(m => m.Urls, new { @class = "textMedium", rows = "5" })
@Html.ValidationMessage("Urls", "*") @Html.ValidationMessage("Urls", "*")
<span class="hint">@T("Provide a list of urls, one per line. Urls can contains wildcard matches using '*', or root identifier like '~/'")</span> @Html.Hint(T("Provide a list of urls, one per line. Urls can contains wildcard matches using '*', or root identifier like '~/'"))
<span class="hint">@T("Examples: http://mysite.com/mypage, ~/Profile/Edit, ~/Profile/*")</span> @Html.Hint(T("Examples: http://mysite.com/mypage, ~/Profile/Edit, ~/Profile/*"))
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</fieldset> </fieldset>

View File

@@ -3,5 +3,7 @@
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net452" />
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net452" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" /> <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
</packages> </packages>