19 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
Tommy Parnell
bbcb8550a4 Merge pull request #9 from TerribleDev/docs
add docs
2017-06-17 17:22:39 -04:00
Tommy Parnell
3978cfc694 add docs 2017-06-17 17:20:36 -04:00
Tommy Parnell
61ca5caf47 Merge pull request #8 from TerribleDev/hpkp
add hpkp
2017-06-17 16:22:57 -04:00
Tommy Parnell
abb4dc3e82 add hpkp 2017-06-17 16:18:21 -04:00
Tommy Parnell
8d2eb9855c fix readme 2017-06-09 22:46:36 -04:00
Tommy Parnell
a2be4d6d48 Merge branch 'csp' 2017-06-09 22:45:40 -04:00
Tommy Parnell
2a5180a1d0 fix package 2017-06-09 22:45:28 -04:00
30 changed files with 578 additions and 41 deletions

View File

@@ -1,8 +1,10 @@
# 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"/>
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). Currently in beta, Content Security Policy, Unit tests, documentation due before 1.0.0. Even still, this should work fine.
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.
In short this allows:
@@ -22,7 +24,7 @@ In short this allows:
app.UseIENoOpen(); // don't allow old ie to open files in the context of your site
app.UseNoMimeSniff(); // prevent MIME sniffing https://en.wikipedia.org/wiki/Content_sniffing
app.UseCrossSiteScriptingFilters(); //add headers to have the browsers auto detect and block some xss attacks
app.UseContentSecurityPolicy(
app.UseContentSecurityPolicy( // Provide a security policy so only content can come from trusted sources
new ContentSecurityPolicyBuilder()
.WithDefaultSource(CSPConstants.Self)
.WithImageSource("http://images.mysite.com")
@@ -30,6 +32,10 @@ In short this allows:
.WithFrameAncestors(CSPConstants.None)
.BuildPolicy()
);
app.UseHpkp(maxAge: 5184000, keys: new List<PublicKeyPin>{ // Prevent man in the middle attacks by providing a hash of your public keys
new PublicKeyPin("cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=", HpKpCrypto.sha256),
new PublicKeyPin("M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=", HpKpCrypto.sha256)
}, includeSubDomains: true, reportUri: "/report", reportOnly: false);
...
app.UseMvc(routes =>
{
@@ -47,5 +53,5 @@ In short this allows:
## 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.

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

View File

@@ -0,0 +1,31 @@
sets the `Content-Security-Policy` header which can help protect against malicious injection of JavaScript, CSS, plugins, and more.
## Attack
When hackers can place content onto your site, they can do bad things! For example, javascript executing can give them someones credit card data. Or they could place a 1x1 transparent gif on your site to collect data.
## The Header
The `Content-Security-Policy` header tells browsers which domains content can come from. This is essentially a white list of domains where content can be loaded. For example, images could only come from your images subdomain.
## Code
Here we are saying images can come from any subdomain of my site. Fonts can come from the current domain.
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseContentSecurityPolicy(
new ContentSecurityPolicyBuilder()
.WithDefaultSource(CSPConstants.Self)
.WithImageSource("http://*.mysite.com")
.WithFontSource(CSPConstants.Self)
.WithFrameAncestors(CSPConstants.None)
.BuildPolicy()
);
}
```

View File

@@ -0,0 +1,21 @@
The Cross Site Scripting filter sets the `X-XSS-Protection` to prevent reflected XSS attacks
## Attack
If someone can run JavaScript on your page, they can attack your users and do a lot of bad things. Sometimes people can inject script tags through query strings, and thus attack your users
## The Header
This middleware simply allows the browsers to detect and combat reflective XSS attacks. This will not save you against all attacks, but its a good start. Note in older versions of IE, this causes more security issues so we turn it off.
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCrossSiteScriptingFilters();
//app.UseCrossSiteScriptingFilters(addOldIE: true); if you want older versions of IE to get the header
}
```

21
docs/DnsPrefetch.md Normal file
View File

@@ -0,0 +1,21 @@
DNS Prefetch sets the `X-DNS-Prefetch-Control`
## Attack
When you visit a URL, the browser prefetches dns for a given link. This is a performance improvement, but can expose your users privacy by having them visit sites, they have never visited.
## The Header
The `X-DNS-Prefetch-Control` header tells browsers whether they should do DNS prefetching.
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseDnsPrefetch(allow: false);
}
```

23
docs/Frameguard.md Normal file
View File

@@ -0,0 +1,23 @@
Frameguard mitigates clickjacking attacks by setting the `X-Frame-Options` header.
## Attack
Attackers get you to click on something you do not wish to click on. Often they do this by iframing other website, buttons but with a different context.
For example lets say your button says you are only going to vote democrat, hackers may make a website that says you will only vote republican and iFrame your button in their website.
## The Header
The `X-Frame-Options` header tells browsers to prevent your webpage from being put in an iframe. This can also control which domains are allowed to load iframes from your site.
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseFrameGuard(new FrameGuardOptions("http://amazon.com"));
}
```

30
docs/Hpkp.md Normal file
View File

@@ -0,0 +1,30 @@
the HTTP Public Key Pinning module helps you set the `Public-Key-Pins` header to prevent person-in-the-middle attacks.
## Attack
If hackers can intercept secure requests to your website, they can gain credit card information, or passwords of your customers.
## The Header
The `Public-Key-Pins` header gives the browsers a hash of your public keys. This verifies to the browser if they are actually talking to your website
## Code
You can set the max age of the cache in seconds. You provide base64 encoded keys, and
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseHpkp(maxAge: 5184000,
keys: new List<PublicKeyPin>{
new PublicKeyPin("cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=", HpKpCrypto.sha256),
new PublicKeyPin("M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=", HpKpCrypto.sha256)
},
includeSubDomains: true,
reportUri: "/report",
reportOnly: false);
}
```

25
docs/Hsts.md Normal file
View File

@@ -0,0 +1,25 @@
Set the `Strict-Transport-Security` header which tells the browser to use https
## Attack
Most websites only want to server over https, but often a http request is made and a user is redirected to https. This middleware caches the knowledge to use https, so future http requests cannot be man in the middle attacked
## The Header
the `Strict-Transport-Security` header controls the browsers behavior to default to https
## Code
Apart from setting a max age, you can include your subdomains. Ontop of all, you can include a preload header, which is required if you submit your [site to google](https://hstspreload.org/). Submitting your site to google will mean that the hsts header will be cached in the browsers ahead of time.
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseHsts(maxAge: 5000, includeSubDomains: true, preload: false);
}
```

21
docs/IENoOpen.md Normal file
View File

@@ -0,0 +1,21 @@
Prevent IE from opening websites in the context of your site
## Attack
In old versions of IE, IE would open html files in the context of your site. Lets say you uploaded a html file as your image for your profile picture in a social media site. Old versions of IE would actually render the html out!
## The Header
The `X-Download-Options` header can be set to noopen.
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIENoOpen();
}
```

21
docs/MimeSniff.md Normal file
View File

@@ -0,0 +1,21 @@
the Dont Sniff Mimetype middleware, noSniff, helps prevent browsers from trying to guess ("sniff") the MIME type.
## Attack
Some browsers will detect what the mime type of a file is, even if the webserver says something else. Lets say someone uploads a script file to your website as their profile. Even though the webserver could say the mime type is one thing, the browser could interpret it as javascript and execute it!
## The Header
The `X-Content-Type-Options` header can be set to `nosniff` to prevent mime sniffing.
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseNoMimeSniff();
}
```

16
docs/Readme.md Normal file
View File

@@ -0,0 +1,16 @@
## Index
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.
* [DnsPrefetch](DnsPrefetch.md) controls browser DNS prefetching
* [Cross Site Scripting](CrossSiteScripting.md) prevent reflective xss attacks
* [Content Security Policy](ContentSecurityPolicy.md)
* [FrameGuard](Frameguard.md) prevent clickjacking
* [HpKp](Hpkp.md) for http public key pinning
* [Hsts](Hsts.md) for telling the browsers to always use https
* [IENoOpen](IENoOpen.md) Prevents IE from opening websites in the context of your browsers
* [MimeSniff](MimeSniff.md) Prevents the browsers from sniffing the mime type of a file
* [ReferrerPolicy](ReferrerPolicy.md) Remove the referral header on link clicks

21
docs/ReferrerPolicy.md Normal file
View File

@@ -0,0 +1,21 @@
the Referrer Policy middleware can control the behavior of the `Referer header` by setting the `Referrer-Policy` header.
## Attack
The referrer header is usually set by the browsers to tell websites where users are coming from. This causes privacy issues for your users by telling other sites where they are coming from.
## The Header
The `Referrer-Header` header can be set to `no-referrer` to prevent such behaviors. The header can also be set to `same-origin` to track users between your own site
## Code
```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseReferrerPolicy(ReferrerPolicy.NoReferrer);
}
```

View File

@@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>HardHat.Example</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>HardHat.Example</PackageId>
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>
@@ -20,19 +19,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.0.2" />
<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" />
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
namespace HardHat.Example
{
@@ -36,7 +37,6 @@ namespace HardHat.Example
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
@@ -58,13 +58,13 @@ namespace HardHat.Example
.WithMediaSource(CSPConstants.Schemes.MediaStream)
.BuildPolicy()
);
app.UseStaticFiles();
// use public key pinning
app.UseHpkp(maxAge: 5184000, keys: new List<PublicKeyPin>{
new PublicKeyPin("cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=", HpKpCrypto.sha256),
new PublicKeyPin("M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=", HpKpCrypto.sha256)
}, includeSubDomains: true, reportUri: "/report", reportOnly: false);
new ContentSecurityPolicyBuilder()
.WithFontSource(CSPConstants.Self)
.WithImageSource("https://example.com")
.WithSandBox(SandboxOption.AllowForms);
app.UseStaticFiles();
app.UseMvc(routes =>
{

View File

@@ -25,10 +25,11 @@ namespace HardHat.UnitTests
FormAction = new HashSet<string>() { "http://*.example.com" },
FrameAncestors = 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]

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">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<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="Moq" Version="4.7.25" />
<PackageReference Include="xunit" Version="2.2.0" />

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
using HardHat.Builders;
using Xunit;
namespace HardHat.UnitTests
{
public class HpKpBuilderTests
{
[Fact]
public void ThrowsExceptions()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
HpKpHeaderBuilder.Build(0, null);
});
Assert.Throws<ArgumentNullException>(() =>
{
HpKpHeaderBuilder.Build(2, null);
});
Assert.Throws<ArgumentException>(() =>
{
HpKpHeaderBuilder.Build(2, new List<PublicKeyPin>());
});
var results = HpKpHeaderBuilder.Build(2, new List<PublicKeyPin>()
{
new PublicKeyPin("yo", HpKpCrypto.sha256),
new PublicKeyPin("dawg", HpKpCrypto.sha256)
}, true, "/awesome");
Assert.Equal<string>("pin-sha256=\"yo\"; pin-sha256=\"dawg\"; max-age=2; includeSubDomains; report-uri=\"/awesome\"", results);
}
}
}

View File

@@ -91,6 +91,10 @@ namespace HardHat.Builders
stringBuilder.Append(Constants.CSPDirectives.PluginTypes);
stringBuilder.Append($" {string.Join(" ", policy.PluginTypes)}; ");
}
if(policy.UpgradeInsecureRequests)
{
stringBuilder.Append($"{Constants.CSPDirectives.UpgradeInsecureRequests}; ");
}
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

@@ -0,0 +1,42 @@
using HardHat;
using System;
using System.Collections.Generic;
using System.Text;
namespace HardHat.Builders
{
internal static class HpKpHeaderBuilder
{
internal static string Build(ulong maxAge, ICollection<PublicKeyPin> keys, bool includeSubDomains = false, string reportUri = "")
{
if (maxAge < 1)
{
throw new ArgumentOutOfRangeException(nameof(maxAge));
}
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
if (keys.Count < 2)
{
throw new ArgumentException(" The current specification requires including a second pin for a backup key which isn't yet used in production. This allows for changing the server's public key without breaking accessibility for clients that have already noted the pins. This is important for example when the former key gets compromised.", nameof(keys));
}
var builder = new StringBuilder();
foreach(var key in keys)
{
// pin-sha256="base64==";
builder.Append($"pin-{Enum.GetName(typeof(HpKpCrypto), key.cryptoType)}=\"{key.fingerprint}\"; ");
}
builder.Append($"max-age={maxAge}");
if(includeSubDomains)
{
builder.Append($"; includeSubDomains");
}
if(!string.IsNullOrWhiteSpace(reportUri))
{
builder.Append($"; report-uri=\"{reportUri}\"");
}
return builder.ToString();
}
}
}

View File

@@ -18,11 +18,11 @@ namespace HardHat
/// <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.
/// </summary>
public const string UnsafeInline = "unsafe-inline";
public const string UnsafeInline = "'unsafe-inline'";
/// <summary>
/// Allows the use of eval() and similar methods for creating code from strings. You must include the single quotes.
/// </summary>
public const string UnsafeEval = "unsafe-eval";
public const string UnsafeEval = "'unsafe-eval'";
/// <summary>
/// Refers to the empty set; that is, no URLs match. The single quotes are required.
/// </summary>
@@ -30,7 +30,7 @@ namespace HardHat
/// <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.
/// </summary>
public const string StrictDynamic = "strict-dynamic";
public const string StrictDynamic = "'strict-dynamic'";
/// <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.
/// </summary>

View File

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

View File

@@ -63,8 +63,11 @@ namespace HardHat
public string ReportUri = string.Empty;
/// <summary>
/// Reports violations that would have occurred. Does not actively enforce the Content Policy
/// </summary>
public bool OnlySendReport { get; set; } = false;
public bool UpgradeInsecureRequests { get; set; } = false;
}
}

View File

@@ -183,7 +183,7 @@ namespace HardHat
/// <summary>
/// Defines valid MIME types for plugins invoked via <object> and <embed>. To load an <applet> you must specify application/x-java-applet.
/// </summary>
/// <param name="mimeTypes"></param>
/// <param name="mimeTypes">valid mime types</param>
/// <returns></returns>
public ContentSecurityPolicyBuilder WithPluginTypes(params string[] mimeTypes)
{
@@ -194,7 +194,11 @@ namespace HardHat
Policy.PluginTypes.UnionWith(mimeTypes);
return this;
}
/// <summary>
/// sandbox directive enables a sandbox for the requested resource similar to the <iframe> sandbox attribute. It applies restrictions to a page's actions including preventing popups, preventing the execution of plugins and scripts, and enforcing a same-origin policy.
/// </summary>
/// <param name="sandboxOption"></param>
/// <returns></returns>
public ContentSecurityPolicyBuilder WithSandBox(BaseSandboxOption sandboxOption)
{
Policy.Sandbox = sandboxOption ?? throw new ArgumentNullException(nameof(sandboxOption));

View File

@@ -1,5 +1,8 @@
using HardHat;
using HardHat.Middlewares;
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Builder
{
public static class Extensions
@@ -28,6 +31,19 @@ namespace Microsoft.AspNetCore.Builder
/// <returns></returns>
public static IApplicationBuilder UseHsts(this IApplicationBuilder app, ulong maxAge, bool includeSubDomains = false, bool preload = false) => app.UseMiddleware<Hsts>(maxAge, includeSubDomains, preload);
/// <summary>
/// The Strict-Transport-Security HTTP header tells browsers to stick with HTTPS and never visit the insecure HTTP version.
/// </summary>
/// <param name="app"></param>
/// <param name="maxAge"></param>
/// <param name="includeSubDomains"></param>
/// <param name="preload"></param>
/// <returns></returns>
public static IApplicationBuilder UseHsts(this IApplicationBuilder app, TimeSpan maxAge, bool includeSubDomains = false, bool preload = false)
{
app.UseMiddleware<Hsts>(Convert.ToUInt64(maxAge.TotalSeconds), includeSubDomains, preload);
return app;
}
/// <summary>
/// This middleware sets the X-Download-Options to prevent Internet Explorer from executing downloads in your sites context.
/// </summary>
/// <param name="app"></param>
@@ -62,6 +78,29 @@ namespace Microsoft.AspNetCore.Builder
/// <returns></returns>
public static IApplicationBuilder UseContentSecurityPolicy(this IApplicationBuilder app, ContentSecurityPolicy policy) => app.UseMiddleware<ContentSecurityPolicyMiddleware>(policy);
/// <summary>
/// To ensure the authenticity of a server's public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. HPKP can circumvent this threat for the HTTPS protocol by telling the client which public key belongs to a certain web server.
/// </summary>
/// <param name="app"></param>
/// <param name="maxAge">The time, in seconds, that the browser should remember that this site is only to be accessed using one of the defined keys.</param>
/// <param name="keys"></param>
/// <param name="includeSubDomains">If this optional parameter is specified, this rule applies to all of the site's subdomains as well.</param>
/// <param name="reportUri">If this optional parameter is specified, pin validation failures are reported to the given URL.</param>
/// <param name="reportOnly">Instead of using a Public-Key-Pins header you can also use a Public-Key-Pins-Report-Only header. This header only sends reports to the report-uri specified in the header and does still allow browsers to connect to the webserver even if the pinning is violated.</param>
/// <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);
/// <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>
/// change or remove the server header.
/// </summary>

View File

@@ -1,20 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>.Net core Middleware, Add various headers to help secure your site</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>
<TargetFramework>netstandard1.6</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>HardHat</AssemblyName>
<PackageId>HardHat</PackageId>
<PackageTags>xss;clickjack;clickjacking;security;.net core;Middleware;core</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>
<PackageProjectUrl>https://github.com/TerribleDev/HardHat</PackageProjectUrl>
<PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<SourceLinkServerType>GitHub</SourceLinkServerType>
</PropertyGroup>
<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>
<PropertyGroup>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);IncludePDBsInPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="IncludePDBsInPackage" Condition="'$(IncludeBuildOutput)' != 'false'">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\$(AssemblyName).pdb" PackagePath="lib\$(TargetFramework)" />
</ItemGroup>
</Target>
</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);
}
}
}

View File

@@ -0,0 +1,49 @@
using HardHat.Builders;
using HardHat;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace HardHat.Middlewares
{
public class Hpkp
{
private readonly bool reportOnly;
private readonly RequestDelegate _next;
private readonly string _header;
public Hpkp(RequestDelegate next, ulong maxAge, ICollection<PublicKeyPin> keys, bool includeSubDomains = false, string reportUri = "", bool reportOnly = false)
{
if(maxAge < 1)
{
throw new ArgumentOutOfRangeException(nameof(maxAge));
}
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
if (keys.Count < 2)
{
throw new ArgumentException(" The current specification requires including a second pin for a backup key which isn't yet used in production. This allows for changing the server's public key without breaking accessibility for clients that have already noted the pins. This is important for example when the former key gets compromised.", nameof(keys));
}
_header = HpKpHeaderBuilder.Build(maxAge, keys, includeSubDomains, reportUri);
_next = next;
this.reportOnly = reportOnly;
}
public Task Invoke(HttpContext context)
{
if(reportOnly)
{
context.Response.Headers[Constants.HpKpHeaderReportOnly] = _header;
}
else
{
context.Response.Headers[Constants.HpKpHeader] = _header;
}
return _next.Invoke(context);
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace HardHat
{
public class PublicKeyPin
{
internal readonly HpKpCrypto cryptoType;
internal readonly string fingerprint;
/// <summary>
///
/// </summary>
/// <param name="fingerprint"> the Base64 encoded Subject Public Key Information (SPKI) fingerprint. It is possible to specify multiple pins for different public keys.</param>
/// <param name="cryptoType"></param>
public PublicKeyPin(string fingerprint, HpKpCrypto cryptoType)
{
if(string.IsNullOrWhiteSpace(fingerprint))
{
throw new ArgumentNullException(nameof(fingerprint));
}
this.fingerprint = fingerprint;
this.cryptoType = cryptoType;
}
}
public enum HpKpCrypto
{
sha256
}
}