12 Commits

Author SHA1 Message Date
Tommy Parnell
13f2ed4531 add missing quotes for unsafe-inline 2019-02-03 17:23:33 -05:00
Tommy Parnell
1f1c9ebd96 add upgrade insecure request 2019-02-03 13:40:54 -05:00
Tommy Parnell
1709e44e55 Merge pull request #11 from TerribleDev/2.0
upgrade to 2.0
2018-11-04 09:36:48 -05:00
Tommy Parnell
74f99573a5 upgrade to 2.0 2018-11-04 09:34:31 -05:00
Tommy Parnell
6019640b13 Merge pull request #10 from TerribleDev/expectCt
add expect ct
2017-10-27 23:44:10 -04:00
Tommy Parnell
72c55bd87b src link pkg 2017-10-27 19:10:16 -04:00
Tommy Parnell
e940621548 include source link 2017-10-27 19:06:55 -04:00
Tommy Parnell
aa72521275 Update Readme.md 2017-06-21 13:03:24 -04:00
Tommy Parnell
12414ae258 Update Readme.md 2017-06-21 08:51:58 -04:00
Tommy Parnell
4f4c5d3750 add expect ct 2017-06-17 17:57:13 -04:00
Tommy Parnell
27790bfd7d remove pre 2017-06-17 17:29:07 -04:00
Tommy Parnell
b7a6de3c17 add build badge 2017-06-17 17:28:05 -04:00
16 changed files with 136 additions and 29 deletions

View File

@@ -1,5 +1,7 @@
# Hard Hat # Hard Hat
[![Build status](https://ci.appveyor.com/api/projects/status/orm7sjpwxde03pbj/branch/master?svg=true)](https://ci.appveyor.com/project/tparnell8/hardhat/branch/master)
<img src="Hat.png" width="350px"/> <img src="Hat.png" width="350px"/>
HardHat is a set of .net core middleware that adds various headers to help protect your site from vulnerabilities. Inspired by [helmetJS](https://helmetjs.github.io). We have [some docs](docs/Readme.md) they are still a work in progress, so please feel free to submit changes to them. HardHat is a set of .net core middleware that adds various headers to help protect your site from vulnerabilities. Inspired by [helmetJS](https://helmetjs.github.io). We have [some docs](docs/Readme.md) they are still a work in progress, so please feel free to submit changes to them.
@@ -51,5 +53,5 @@ In short this allows:
## Getting started ## Getting started
* Install the nuget package `Install-Package HardHat -Pre` * Install the nuget package `Install-Package HardHat`
* Add the middleware you desire to your configure block. * Add the middleware you desire to your configure block.

View File

@@ -9,6 +9,10 @@ build_script:
{ {
dotnet pack src\HardHat\HardHat.csproj --configuration Release --output ..\..\output /p:Version=$env:APPVEYOR_REPO_TAG_NAME dotnet pack src\HardHat\HardHat.csproj --configuration Release --output ..\..\output /p:Version=$env:APPVEYOR_REPO_TAG_NAME
} }
else
{
dotnet pack src\HardHat\HardHat.csproj --configuration Release --output ..\..\output /p:Version=0.0.1-build-$env:APPVEYOR_BUILD_NUMBER
}
test_script: test_script:
- ps: dotnet test src\HardHat.UnitTests\HardHat.UnitTests.csproj - ps: dotnet test src\HardHat.UnitTests\HardHat.UnitTests.csproj
artifacts: artifacts:
@@ -16,6 +20,6 @@ artifacts:
deploy: deploy:
- provider: NuGet - provider: NuGet
api_key: api_key:
secure: bGn7M6dHOJ3QjwYIv7e34tcY/n9cCUZmL1MnM6jRfmnJOOfwlrS+cdRj2n8Wf31n secure: //tKHlb2yqAtpxnR6p9IAtXwQNaq8UYYyIFSD0QVF3XnEasIxG2gTWdmWuG87fUX
on: on:
appveyor_repo_tag: true appveyor_repo_tag: true

View File

@@ -1,6 +1,6 @@
## Index ## Index
Hard hat is essentially a group of 12+ individual middleware that help you improve the security of your aspnet core based applications. Hard hat is essentially a group of individual middleware that help you improve the security of your aspnet core based applications.
Each middleware has a seperate readme file. These docs were inspired by helmetjs. Each middleware has a seperate readme file. These docs were inspired by helmetjs.

View File

@@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>HardHat.Example</AssemblyName> <AssemblyName>HardHat.Example</AssemblyName>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackageId>HardHat.Example</PackageId> <PackageId>HardHat.Example</PackageId>
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -20,19 +19,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.0.2" /> <PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.3" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.0.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.0.1" />
</ItemGroup> </ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish"> <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">

View File

@@ -37,7 +37,6 @@ namespace HardHat.Example
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
app.UseBrowserLink();
} }
else else
{ {

View File

@@ -25,10 +25,11 @@ namespace HardHat.UnitTests
FormAction = new HashSet<string>() { "http://*.example.com" }, FormAction = new HashSet<string>() { "http://*.example.com" },
FrameAncestors = new HashSet<string>() { "http://*.example.com" }, FrameAncestors = new HashSet<string>() { "http://*.example.com" },
PluginTypes = new HashSet<string>() { "http://*.example.com" }, PluginTypes = new HashSet<string>() { "http://*.example.com" },
Sandbox = SandboxOption.AllowPointerLock Sandbox = SandboxOption.AllowPointerLock,
UpgradeInsecureRequests = true
}); });
Assert.Equal<string>(@"default-src 'self' 'none' http://*.example.com; script-src http://*.example.com; style-src http://*.example.com; img-src http://*.example.com; connect-src http://*.example.com; font-src http://*.example.com; object-src http://*.example.com; media-src http://*.example.com; child-src http://*.example.com; form-action http://*.example.com; frame-ancestors http://*.example.com; sandbox allow-pointer-lock; plugin-types http://*.example.com;", builder); Assert.Equal<string>(@"default-src 'self' 'none' http://*.example.com; script-src http://*.example.com; style-src http://*.example.com; img-src http://*.example.com; connect-src http://*.example.com; font-src http://*.example.com; object-src http://*.example.com; media-src http://*.example.com; child-src http://*.example.com; form-action http://*.example.com; frame-ancestors http://*.example.com; sandbox allow-pointer-lock; plugin-types http://*.example.com; upgrade-insecure-requests;", builder);
} }
[Fact] [Fact]

View File

@@ -0,0 +1,33 @@
using HardHat.Builders;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;
namespace HardHat.UnitTests
{
public class ExpectCtHeaderTests
{
[Fact]
public void TestExceptions()
{
Assert.Throws<ArgumentNullException>(() => ExpectCtHeaderBuilder.Build(0, string.Empty));
}
[Fact]
public void TestHeader()
{
var result = ExpectCtHeaderBuilder.Build(0, "/awesome");
Assert.Equal("max-age=0; report-uri=\"/awesome\"", result);
}
[Fact]
public void TestHeaderWithEnforce()
{
var result = ExpectCtHeaderBuilder.Build(0, "/awesome", true);
Assert.Equal("max-age=0; report-uri=\"/awesome\"; enforce", result);
}
}
}

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.1.2" /> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="Moq" Version="4.7.25" /> <PackageReference Include="Moq" Version="4.7.25" />
<PackageReference Include="xunit" Version="2.2.0" /> <PackageReference Include="xunit" Version="2.2.0" />

View File

@@ -91,6 +91,10 @@ namespace HardHat.Builders
stringBuilder.Append(Constants.CSPDirectives.PluginTypes); stringBuilder.Append(Constants.CSPDirectives.PluginTypes);
stringBuilder.Append($" {string.Join(" ", policy.PluginTypes)}; "); stringBuilder.Append($" {string.Join(" ", policy.PluginTypes)}; ");
} }
if(policy.UpgradeInsecureRequests)
{
stringBuilder.Append($"{Constants.CSPDirectives.UpgradeInsecureRequests}; ");
}
return stringBuilder.ToString().TrimEnd(); return stringBuilder.ToString().TrimEnd();
} }
} }

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace HardHat.Builders
{
internal static class ExpectCtHeaderBuilder
{
internal static string Build(ulong maxAge, string reportUri, bool enforce = false)
{
if (string.IsNullOrWhiteSpace(reportUri))
{
throw new ArgumentNullException(nameof(reportUri), "Report URI must have a value");
}
var builder = new StringBuilder($"max-age={maxAge}; report-uri=\"{reportUri}\"");
if (enforce)
{
builder.Append("; enforce");
}
return builder.ToString();
}
}
}

View File

@@ -18,11 +18,11 @@ namespace HardHat
/// <summary> /// <summary>
/// Allows the use of inline resources, such as inline <script> elements, javascript: URLs, inline event handlers, and inline <style> elements. You must include the single quotes. /// Allows the use of inline resources, such as inline <script> elements, javascript: URLs, inline event handlers, and inline <style> elements. You must include the single quotes.
/// </summary> /// </summary>
public const string UnsafeInline = "unsafe-inline"; public const string UnsafeInline = "'unsafe-inline'";
/// <summary> /// <summary>
/// Allows the use of eval() and similar methods for creating code from strings. You must include the single quotes. /// Allows the use of eval() and similar methods for creating code from strings. You must include the single quotes.
/// </summary> /// </summary>
public const string UnsafeEval = "unsafe-eval"; public const string UnsafeEval = "'unsafe-eval'";
/// <summary> /// <summary>
/// Refers to the empty set; that is, no URLs match. The single quotes are required. /// Refers to the empty set; that is, no URLs match. The single quotes are required.
/// </summary> /// </summary>
@@ -30,7 +30,7 @@ namespace HardHat
/// <summary> /// <summary>
/// The strict-dynamic source expression specifies that the trust explicitly given to a script present in the markup, by accompanying it with a nonce or a hash, shall be propagated to all the scripts loaded by that root script. At the same time, any whitelist or source expressions such as 'self' or 'unsafe-inline' will be ignored. /// The strict-dynamic source expression specifies that the trust explicitly given to a script present in the markup, by accompanying it with a nonce or a hash, shall be propagated to all the scripts loaded by that root script. At the same time, any whitelist or source expressions such as 'self' or 'unsafe-inline' will be ignored.
/// </summary> /// </summary>
public const string StrictDynamic = "strict-dynamic"; public const string StrictDynamic = "'strict-dynamic'";
/// <summary> /// <summary>
/// A whitelist for specific inline scripts using a cryptographic nonce (number used once). The server must generate a unique nonce value each time it transmits a policy. It is critical to provide an unguessable nonce, as bypassing a resources policy is otherwise trivial. See unsafe inline script for an example. /// A whitelist for specific inline scripts using a cryptographic nonce (number used once). The server must generate a unique nonce value each time it transmits a policy. It is critical to provide an unguessable nonce, as bypassing a resources policy is otherwise trivial. See unsafe inline script for an example.
/// </summary> /// </summary>

View File

@@ -22,6 +22,7 @@
internal const string semicolon = ";"; internal const string semicolon = ";";
internal const string HpKpHeader = "Public-Key-Pins"; internal const string HpKpHeader = "Public-Key-Pins";
internal const string HpKpHeaderReportOnly = "Public-Key-Pins-Report-Only"; internal const string HpKpHeaderReportOnly = "Public-Key-Pins-Report-Only";
internal const string ExpectCt = "Expect-CT";
internal static class Referrers internal static class Referrers
{ {
internal const string NoReferrer = "no-referrer"; internal const string NoReferrer = "no-referrer";
@@ -51,6 +52,7 @@
internal const string FormAction = "form-action"; internal const string FormAction = "form-action";
internal const string FrameAncestors = "frame-ancestors"; internal const string FrameAncestors = "frame-ancestors";
internal const string PluginTypes = "plugin-types"; internal const string PluginTypes = "plugin-types";
internal const string UpgradeInsecureRequests = "upgrade-insecure-requests";
} }
} }
} }

View File

@@ -68,5 +68,6 @@ namespace HardHat
/// </summary> /// </summary>
public bool OnlySendReport { get; set; } = false; public bool OnlySendReport { get; set; } = false;
public bool UpgradeInsecureRequests { get; set; } = false;
} }
} }

View File

@@ -90,6 +90,17 @@ namespace Microsoft.AspNetCore.Builder
/// <returns></returns> /// <returns></returns>
public static IApplicationBuilder UseHpkp(this IApplicationBuilder app, ulong maxAge, ICollection<PublicKeyPin> keys, bool includeSubDomains = false, string reportUri = "", bool reportOnly = false) => app.UseMiddleware<Hpkp>(maxAge, keys, includeSubDomains, reportUri, reportOnly); public static IApplicationBuilder UseHpkp(this IApplicationBuilder app, ulong maxAge, ICollection<PublicKeyPin> keys, bool includeSubDomains = false, string reportUri = "", bool reportOnly = false) => app.UseMiddleware<Hpkp>(maxAge, keys, includeSubDomains, reportUri, reportOnly);
/// <summary>
/// NOTE: This is still in draft spec, browser support maybe very limited
/// <para>Certificate Transparency is an open framework for monitoring and auditing the certificates issued by Certificate Authorities in near real-time. By requiring a CA to log all certificates they generate, site owners can quickly identify mis-issued certificates and it becomes much easier to detect a rogue CA. <see href="https://scotthelme.co.uk/a-new-security-header-expect-ct/"/></para>
/// </summary>
/// <param name="app"></param>
/// <param name="maxAge"> specifies the number of seconds that the browser should cache and apply the received policy for, whether enforced or report-only.</param>
/// <param name="reportUri"> specifies where the browser should send reports if it does not receive valid CT information. This is specified as an absolute URI.</param>
/// <param name="enforce">controls whether the browser should enforce the policy or treat it as report-only mode. The directive has no value so you simply include it or not depending on whether or not you want the browser to enforce the policy or just report on it.</param>
/// <returns></returns>
public static IApplicationBuilder UseCertificateTransparency(this IApplicationBuilder app, ulong maxAge, string reportUri, bool enforce = false) => app.UseMiddleware<ExpectCt>(maxAge, reportUri, enforce);
/// <summary> /// <summary>
/// change or remove the server header. /// change or remove the server header.
/// </summary> /// </summary>

View File

@@ -3,18 +3,28 @@
<PropertyGroup> <PropertyGroup>
<Description>.Net core Middleware, Add various headers to help secure your site. Disable XSS attacks with Content Security Policies, and make sure browsers do not mime sniff</Description> <Description>.Net core Middleware, Add various headers to help secure your site. Disable XSS attacks with Content Security Policies, and make sure browsers do not mime sniff</Description>
<Authors>Tommy Parnell</Authors> <Authors>Tommy Parnell</Authors>
<TargetFramework>netstandard1.6</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>HardHat</AssemblyName> <AssemblyName>HardHat</AssemblyName>
<PackageId>HardHat</PackageId> <PackageId>HardHat</PackageId>
<PackageTags>xss;clickjack;clickjacking;security;.net core;Middleware;core;Content Security Policy;CSP</PackageTags> <PackageTags>xss;clickjack;clickjacking;security;.net core;Middleware;core;Content Security Policy;CSP</PackageTags>
<PackageIconUrl>https://media.githubusercontent.com/media/TerribleDev/HardHat/master/Hat.png</PackageIconUrl> <PackageIconUrl>https://media.githubusercontent.com/media/TerribleDev/HardHat/master/Hat.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/TerribleDev/HardHat</PackageProjectUrl> <PackageProjectUrl>https://github.com/TerribleDev/HardHat</PackageProjectUrl>
<PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl> <PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> <SourceLinkServerType>GitHub</SourceLinkServerType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.4.0" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);IncludePDBsInPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="IncludePDBsInPackage" Condition="'$(IncludeBuildOutput)' != 'false'">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\$(AssemblyName).pdb" PackagePath="lib\$(TargetFramework)" />
</ItemGroup>
</Target>
</Project> </Project>

View File

@@ -0,0 +1,30 @@
using HardHat.Builders;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace HardHat.Middlewares
{
public class ExpectCt
{
private readonly RequestDelegate _next;
private readonly string headerValue;
public ExpectCt(RequestDelegate next, ulong maxAge, string reportUri, bool enforce = false)
{
this._next = next;
if(string.IsNullOrWhiteSpace(reportUri))
{
throw new ArgumentNullException(nameof(reportUri), "Report URI must have a value");
}
headerValue = ExpectCtHeaderBuilder.Build(maxAge, reportUri, enforce);
}
public Task Invoke(HttpContext context)
{
context.Response.Headers[Constants.ExpectCt] = headerValue;
return _next?.Invoke(context);
}
}
}