102 changed files with 6 additions and 4217 deletions
@ -1,197 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using NWebsec.Core.HttpHeaders.Configuration.Validation; |
|||
using NWebsec.Middleware; |
|||
using NWebsec.Middleware.Middleware; |
|||
|
|||
// ReSharper disable once CheckNamespace
|
|||
namespace Microsoft.AspNetCore.Builder |
|||
{ |
|||
public static class ApplicationBuilderExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Adds a middleware to the pipeline that validates redirects.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseRedirectValidation(this IApplicationBuilder app) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
|
|||
var options = new RedirectValidationOptions(); |
|||
return app.UseMiddleware<RedirectValidationMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that validates redirects.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseRedirectValidation(this IApplicationBuilder app, Action<IFluentRedirectValidationOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new RedirectValidationOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<RedirectValidationMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the Strict-Transport-Security header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseHsts(this IApplicationBuilder app, Action<IFluentHstsOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new HstsOptions(); |
|||
configurer(options); |
|||
new HstsConfigurationValidator().Validate(options); |
|||
return app.UseMiddleware<HstsMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the Public-Key-Pins header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseHpkp(this IApplicationBuilder app, Action<IFluentHpkpOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new HpkpOptions(); |
|||
configurer(options); |
|||
new HpkpConfigurationValidator().ValidateNumberOfPins(options.Config); |
|||
return app.UseMiddleware<HpkpMiddleware>(options, false); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the Public-Key-Pins-Report-Only header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseHpkpReportOnly(this IApplicationBuilder app, Action<IFluentHpkpOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new HpkpOptions(); |
|||
configurer(options); |
|||
new HpkpConfigurationValidator().ValidateNumberOfPins(options.Config); |
|||
return app.UseMiddleware<HpkpMiddleware>(options, true); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the X-Content-Type-Options header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseXContentTypeOptions(this IApplicationBuilder app) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
|
|||
return app.UseMiddleware<XContentTypeOptionsMiddleware>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the X-Download-Options header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseXDownloadOptions(this IApplicationBuilder app) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
|
|||
return app.UseMiddleware<XDownloadOptionsMiddleware>(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the X-Frame-Options header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseXfo(this IApplicationBuilder app, Action<IFluentXFrameOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new XFrameOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<XfoMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the X-Robots-Tag header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseXRobotsTag(this IApplicationBuilder app, Action<IFluentXRobotsTagOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new XRobotsTagOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<XRobotsTagMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the X-Xss-Protection header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseXXssProtection(this IApplicationBuilder app, Action<IFluentXXssProtectionOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new XXssProtectionOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<XXssMiddleware>(options); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the Content-Security-Policy header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseCsp(this IApplicationBuilder app, Action<IFluentCspOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new CspOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<CspMiddleware>(options, false); //Last param indicates it's not reportOnly.
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a middleware to the ASP.NET pipeline that sets the Content-Security-Policy-Report-Only header.
|
|||
/// </summary>
|
|||
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
|
|||
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
|
|||
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
|
|||
public static IApplicationBuilder UseCspReportOnly(this IApplicationBuilder app, Action<IFluentCspOptions> configurer) |
|||
{ |
|||
if (app == null) throw new ArgumentNullException(nameof(app)); |
|||
if (configurer == null) throw new ArgumentNullException(nameof(configurer)); |
|||
|
|||
var options = new CspOptions(); |
|||
configurer(options); |
|||
return app.UseMiddleware<CspMiddleware>(options, true); //Last param indicates it's reportOnly.
|
|||
} |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using NWebsec.Core; |
|||
|
|||
namespace NWebsec.Middleware.Core |
|||
{ |
|||
//TODO Get rid of these?
|
|||
internal class OwinEnvironment |
|||
{ |
|||
private readonly IDictionary<string, object> _environment; |
|||
|
|||
internal OwinEnvironment(IDictionary<string, object> env) |
|||
{ |
|||
_environment = env; |
|||
RequestHeaders = new RequestHeaders((IDictionary<string, string[]>)_environment[OwinKeys.RequestHeaders]); |
|||
ResponseHeaders = new ResponseHeaders((IDictionary<string, string[]>)_environment[OwinKeys.ResponseHeaders]); |
|||
} |
|||
|
|||
internal string RequestScheme => (string)_environment[OwinKeys.RequestScheme]; |
|||
|
|||
internal string RequestPathBase => (string)_environment[OwinKeys.RequestPathBase]; |
|||
|
|||
internal string RequestPath => (string)_environment[OwinKeys.RequestPath]; |
|||
|
|||
internal int ResponseStatusCode |
|||
{ |
|||
get { return (int)_environment[OwinKeys.ResponseStatusCode]; } |
|||
set { _environment[OwinKeys.ResponseStatusCode] = value; } |
|||
} |
|||
|
|||
internal RequestHeaders RequestHeaders { get; private set; } |
|||
|
|||
internal ResponseHeaders ResponseHeaders { get; private set; } |
|||
|
|||
internal NWebsecContext NWebsecContext |
|||
{ |
|||
get |
|||
{ |
|||
if (!_environment.ContainsKey(NWebsecContext.ContextKey)) |
|||
{ |
|||
_environment[NWebsecContext.ContextKey] = new NWebsecContext(); |
|||
} |
|||
|
|||
return _environment[NWebsecContext.ContextKey] as NWebsecContext; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Middleware.Core |
|||
{ |
|||
internal static class OwinKeys |
|||
{ |
|||
//Request
|
|||
internal static string RequestBody = "owin.RequestBody"; |
|||
internal static string RequestHeaders = "owin.RequestHeaders"; |
|||
internal static string RequestMethod = "owin.RequestMethod"; |
|||
internal static string RequestPath = "owin.RequestPath"; |
|||
internal static string RequestPathBase = "owin.RequestPathBase"; |
|||
internal static string RequestProtocol = "owin.RequestProtocol"; |
|||
internal static string RequestQueryString = "owin.RequestQueryString"; |
|||
internal static string RequestScheme = "owin.RequestScheme"; |
|||
|
|||
//Response
|
|||
internal static string ResponseBody = "owin.ResponseBody"; |
|||
internal static string ResponseHeaders = "owin.ResponseHeaders"; |
|||
internal static string ResponseStatusCode = "owin.ResponseStatusCode"; |
|||
internal static string ResponseReasonPhrase = "owin.ResponseReasonPhrase"; |
|||
internal static string ResponseProtocol = "owin.ResponseProtocol"; |
|||
|
|||
//Other
|
|||
internal static string CallCancelled = "owin.CallCancelled"; |
|||
internal static string Version = "owin.Version"; |
|||
} |
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace NWebsec.Middleware.Core |
|||
{ |
|||
internal class RequestHeaders |
|||
{ |
|||
private readonly IDictionary<string, string[]> _headers; |
|||
|
|||
internal RequestHeaders(IDictionary<string, string[]> headers) |
|||
{ |
|||
_headers = headers; |
|||
} |
|||
|
|||
public string Host |
|||
{ |
|||
get |
|||
{ |
|||
try |
|||
{ |
|||
return _headers.ContainsKey("Host") ? _headers["Host"].Single() : null; |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
throw new Exception("Multiple Host headers detected: " + String.Join(" ", _headers["Host"])); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of a header
|
|||
/// </summary>
|
|||
/// <param name="headername"></param>
|
|||
/// <returns>The header's values as a comma separated list, null if the header is not set.</returns>
|
|||
public string GetHeaderValue(string headername) |
|||
{ |
|||
string[] values; |
|||
return _headers.TryGetValue(headername, out values) ? String.Join(",", values) : null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace NWebsec.Middleware.Core |
|||
{ |
|||
internal class ResponseHeaders |
|||
{ |
|||
private readonly IDictionary<string, string[]> _headers; |
|||
|
|||
internal ResponseHeaders(IDictionary<string, string[]> headers) |
|||
{ |
|||
_headers = headers; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the value of the Location header if present. Otherwise returns null.
|
|||
/// </summary>
|
|||
public string Location |
|||
{ |
|||
get |
|||
{ |
|||
try |
|||
{ |
|||
return _headers.ContainsKey("Location") ? _headers["Location"].Single() : null; |
|||
} |
|||
catch (Exception) |
|||
{ |
|||
throw new Exception("Multiple Location headers detected: " + String.Join(" ", _headers["Location"])); |
|||
} |
|||
} |
|||
set { _headers["Location"] = new[] { value }; } |
|||
} |
|||
|
|||
internal void SetHeader(string name, string value) |
|||
{ |
|||
_headers[name] = new[] { value }; |
|||
} |
|||
|
|||
internal void RemoveHeader(string name) |
|||
{ |
|||
_headers.Remove(name); |
|||
} |
|||
} |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class CspDirective : ICspDirectiveConfiguration |
|||
{ |
|||
public CspDirective() |
|||
{ |
|||
Enabled = true; |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool Enabled { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool NoneSrc { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool SelfSrc { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool UnsafeInlineSrc { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool UnsafeEvalSrc { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public string Nonce { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public IEnumerable<string> CustomSources { get; set; } |
|||
} |
|||
} |
|||
@ -1,114 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
using NWebsec.Core.HttpHeaders.Csp; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public static class CspDirectiveExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the "none" source for the CSP directive. This source cannot be combined with other sources on a CSP directive.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
|
|||
/// <param name="directive">The CSP directive configuration object.</param>
|
|||
/// <exception cref="InvalidOperationException">Thrown when sources have already been configured for the directive.</exception>
|
|||
public static void None<T>(this T directive) where T : class, ICspDirectiveBasicConfiguration |
|||
{ |
|||
if (directive == null) throw new ArgumentNullException("directive"); |
|||
|
|||
ValidateBeforeSettingNoneSource(directive); |
|||
directive.NoneSrc = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the "self" source for the CSP directive.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
|
|||
/// <param name="directive">The CSP directive configuration object.</param>
|
|||
/// <returns>The CSP directive configuration object.</returns>
|
|||
public static T Self<T>(this T directive) where T : class, ICspDirectiveBasicConfiguration |
|||
{ |
|||
if (directive == null) throw new ArgumentNullException("directive"); |
|||
|
|||
directive.SelfSrc = true; |
|||
return directive; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets custom sources for the CSP directive.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
|
|||
/// <param name="directive">The CSP directive configuration object.</param>
|
|||
/// <param name="sources">One or more custom sources.</param>
|
|||
/// <returns>The CSP directive configuration object.</returns>
|
|||
public static T CustomSources<T>(this T directive, params string[] sources) where T : class, ICspDirectiveBasicConfiguration |
|||
{ |
|||
if (directive == null) throw new ArgumentNullException("directive"); |
|||
if (sources.Length == 0) throw new ArgumentException("You must supply at least one source.", "sources"); |
|||
|
|||
try |
|||
{ |
|||
directive.CustomSources = sources.Select(s => CspUriSource.Parse(s).ToString()).ToArray(); |
|||
} |
|||
catch (InvalidCspSourceException e) |
|||
{ |
|||
throw new ArgumentException("Invalid source. Details: " + e.Message, "sources", e); |
|||
} |
|||
|
|||
return directive; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the "unsafe-inline" source for the CSP directive.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
|
|||
/// <param name="directive">The CSP directive configuration object.</param>
|
|||
/// <returns>The CSP directive configuration object.</returns>
|
|||
public static T UnsafeInline<T>(this T directive) where T : class, ICspDirectiveUnsafeInlineConfiguration |
|||
{ |
|||
if (directive == null) throw new ArgumentNullException("directive"); |
|||
|
|||
directive.UnsafeInlineSrc = true; |
|||
return directive; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the "unsafe-eval" source for the CSP directive.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
|
|||
/// <param name="directive">The CSP directive configuration object.</param>
|
|||
/// <returns>The CSP directive configuration object.</returns>
|
|||
public static T UnsafeEval<T>(this T directive) where T : class, ICspDirectiveConfiguration |
|||
{ |
|||
if (directive == null) throw new ArgumentNullException("directive"); |
|||
|
|||
directive.UnsafeEvalSrc = true; |
|||
return directive; |
|||
} |
|||
|
|||
private static void ValidateBeforeSettingNoneSource(ICspDirectiveBasicConfiguration directive) |
|||
{ |
|||
if (directive.SelfSrc || (directive.CustomSources != null && directive.CustomSources.Any())) |
|||
{ |
|||
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); |
|||
} |
|||
|
|||
var unsafeInline = directive as ICspDirectiveUnsafeInlineConfiguration; |
|||
|
|||
if (unsafeInline != null && unsafeInline.UnsafeInlineSrc) |
|||
{ |
|||
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); |
|||
} |
|||
|
|||
var unsafeEval = directive as ICspDirectiveConfiguration; |
|||
|
|||
if (unsafeEval != null && unsafeEval.UnsafeEvalSrc) |
|||
{ |
|||
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,161 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class CspOptions : ICspConfiguration, IFluentCspOptions |
|||
{ |
|||
public bool Enabled { get; set; } = true; |
|||
|
|||
public ICspDirectiveConfiguration DefaultSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration ScriptSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration ObjectSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration StyleSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration ImgSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration MediaSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration FrameSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration FontSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration ConnectSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration BaseUriDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration ChildSrcDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration FormActionDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspDirectiveConfiguration FrameAncestorsDirective { get; set; } = new CspDirective(); |
|||
|
|||
public ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; } = new FluentCspPluginTypesDirective(); |
|||
|
|||
public ICspSandboxDirectiveConfiguration SandboxDirective { get; set; } = new FluentCspSandboxDirective(); |
|||
|
|||
public ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; } = new CspUpgradeDirectiveConfiguration(); |
|||
|
|||
public ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; } = new CspReportUriDirective(); |
|||
|
|||
public IFluentCspOptions DefaultSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(DefaultSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ScriptSources(Action<ICspDirectiveConfiguration> configurer) |
|||
{ |
|||
configurer(ScriptSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ObjectSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(ObjectSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions StyleSources(Action<ICspDirectiveUnsafeInlineConfiguration> configurer) |
|||
{ |
|||
configurer(StyleSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ImageSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(ImgSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions MediaSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(MediaSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions FrameSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(FrameSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions FontSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(FontSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ConnectSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(ConnectSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions BaseUris(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(BaseUriDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ChildSources(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(ChildSrcDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions FormActions(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(FormActionDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions FrameAncestors(Action<ICspDirectiveBasicConfiguration> configurer) |
|||
{ |
|||
configurer(FrameAncestorsDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions PluginTypes(Action<IFluentCspPluginTypesDirective> configurer) |
|||
{ |
|||
configurer((IFluentCspPluginTypesDirective)PluginTypesDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions Sandbox() |
|||
{ |
|||
SandboxDirective.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions Sandbox(Action<IFluentCspSandboxDirective> configurer) |
|||
{ |
|||
SandboxDirective.Enabled = true; |
|||
configurer((IFluentCspSandboxDirective)SandboxDirective); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions UpgradeInsecureRequests(int httpsPort = 443) |
|||
{ |
|||
if (httpsPort < 1 || httpsPort > 65535) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(httpsPort),"The port number must be in the range 1-65535."); |
|||
} |
|||
|
|||
UpgradeInsecureRequestsDirective.Enabled = true; |
|||
UpgradeInsecureRequestsDirective.HttpsPort = httpsPort; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentCspOptions ReportUris(Action<IFluentCspReportUriDirective> configurer) |
|||
{ |
|||
configurer((IFluentCspReportUriDirective)ReportUriDirective); |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,40 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
using NWebsec.Core.HttpHeaders.Csp; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class CspReportUriDirective : ICspReportUriDirectiveConfiguration, IFluentCspReportUriDirective |
|||
{ |
|||
internal CspReportUriDirective() |
|||
{ |
|||
Enabled = true; |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public bool EnableBuiltinHandler { get; set; } |
|||
public IEnumerable<string> ReportUris { get; set; } |
|||
|
|||
public void Uris(params string[] reportUris) |
|||
{ |
|||
if (reportUris.Length == 0) throw new ArgumentException("You must supply at least one report URI.", "reportUris"); |
|||
|
|||
var reportUriList = new List<string>(); |
|||
|
|||
foreach (var reportUri in reportUris) |
|||
{ |
|||
Uri uri; |
|||
if (!Uri.TryCreate(reportUri, UriKind.RelativeOrAbsolute, out uri)) |
|||
{ |
|||
throw new ArgumentException("Could not parse reportUri: " + reportUri); |
|||
} |
|||
|
|||
reportUriList.Add(CspUriSource.EncodeUri(uri)); |
|||
} |
|||
ReportUris = reportUriList.ToArray(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.Exceptions |
|||
{ |
|||
public class RedirectValidationException : Exception |
|||
{ |
|||
public RedirectValidationException(string message) : base(message) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace NWebsec.Core.Extensions |
|||
{ |
|||
public static class HttpContextExtensions |
|||
{ |
|||
public static NWebsecContext GetNWebsecContext(this HttpContext context) |
|||
{ |
|||
if (!context.Items.ContainsKey(NWebsecContext.ContextKey)) |
|||
{ |
|||
context.Items[NWebsecContext.ContextKey] = new NWebsecContext(); |
|||
} |
|||
|
|||
return context.Items[NWebsecContext.ContextKey] as NWebsecContext; |
|||
} |
|||
} |
|||
} |
|||
@ -1,53 +0,0 @@ |
|||
//This code was lended from http://bit.ly/ifluentinterface, and is not covered by the regular NWebsec lisence.
|
|||
|
|||
using System; |
|||
using System.ComponentModel; |
|||
|
|||
namespace NWebsec.Core.Fluent |
|||
{ |
|||
/// <summary>
|
|||
/// Interface that is used to build fluent interfaces and hides methods declared by <see cref="object" /> from
|
|||
/// IntelliSense.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Code that consumes implementations of this interface should expect one of two things:
|
|||
/// <list type="number">
|
|||
/// <item>
|
|||
/// When referencing the interface from within the same solution (project reference), you will still see the
|
|||
/// methods this interface is meant to hide.
|
|||
/// </item>
|
|||
/// <item>
|
|||
/// When referencing the interface through the compiled output assembly (external reference), the standard
|
|||
/// Object methods will be hidden as intended.
|
|||
/// </item>
|
|||
/// </list>
|
|||
/// See http://bit.ly/ifluentinterface for more information.
|
|||
/// </remarks>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public interface IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Redeclaration that hides the <see cref="object.GetType()" /> method from IntelliSense.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
Type GetType(); |
|||
|
|||
/// <summary>
|
|||
/// Redeclaration that hides the <see cref="object.GetHashCode()" /> method from IntelliSense.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
int GetHashCode(); |
|||
|
|||
/// <summary>
|
|||
/// Redeclaration that hides the <see cref="object.ToString()" /> method from IntelliSense.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
string ToString(); |
|||
|
|||
/// <summary>
|
|||
/// Redeclaration that hides the <see cref="object.Equals(object)" /> method from IntelliSense.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool Equals(object obj); |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
using NWebsec.Core.HttpHeaders.Configuration.Validation; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class FluentCspPluginTypesDirective : CspPluginTypesDirectiveConfiguration, IFluentCspPluginTypesDirective |
|||
{ |
|||
public FluentCspPluginTypesDirective() |
|||
{ |
|||
Enabled = true; |
|||
} |
|||
|
|||
|
|||
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
|
|||
public void MediaTypes(params string[] mediaTypes) |
|||
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
|
|||
{ |
|||
if (mediaTypes == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(mediaTypes)); |
|||
} |
|||
|
|||
if (mediaTypes.Length == 0) |
|||
{ |
|||
throw new ArgumentException("One or more parameter values expected.", nameof(mediaTypes)); |
|||
} |
|||
var validator = new Rfc2045MediaTypeValidator(); |
|||
var types = mediaTypes.Distinct().ToArray(); |
|||
|
|||
foreach (var mediaType in types) |
|||
{ |
|||
try |
|||
{ |
|||
validator.Validate(mediaType); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ArgumentException("Invalid argument. Details: " + e.Message, e); |
|||
} |
|||
} |
|||
|
|||
base.MediaTypes = types; |
|||
} |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
class FluentCspSandboxDirective : CspSandboxDirectiveConfiguration, IFluentCspSandboxDirective |
|||
{ |
|||
public new IFluentCspSandboxDirective AllowForms() |
|||
{ |
|||
base.AllowForms = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentCspSandboxDirective AllowPointerLock() |
|||
{ |
|||
base.AllowPointerLock = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentCspSandboxDirective AllowPopups() |
|||
{ |
|||
base.AllowPopups = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentCspSandboxDirective AllowSameOrigin() |
|||
{ |
|||
base.AllowSameOrigin = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentCspSandboxDirective AllowScripts() |
|||
{ |
|||
base.AllowScripts = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentCspSandboxDirective AllowTopNavigation() |
|||
{ |
|||
base.AllowTopNavigation = true; |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace NWebsec.Middleware.Helpers |
|||
{ |
|||
//Tested indirectly by CSP Middleware
|
|||
internal class CspUpgradeHelper |
|||
{ |
|||
internal static bool UaSupportsUpgradeInsecureRequests(HttpContext env) |
|||
{ |
|||
var upgradeHeader = env.Request.Headers["Upgrade-Insecure-Requests"]; |
|||
|
|||
return upgradeHeader.Any(h => h.Equals("1", StringComparison.Ordinal)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.HttpHeaders; |
|||
|
|||
namespace NWebsec.Core.Helpers |
|||
{ |
|||
public class HeaderResultHandler : IHeaderResultHandler |
|||
{ |
|||
public void HandleHeaderResult(HttpResponse response, HeaderResult result) |
|||
{ |
|||
if (result == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
switch (result.Action) |
|||
{ |
|||
case HeaderResult.ResponseAction.Set: |
|||
response.Headers[result.Name] = result.Value; |
|||
return; |
|||
case HeaderResult.ResponseAction.Remove: |
|||
response.Headers.Remove(result.Name); |
|||
return; |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.HttpHeaders; |
|||
|
|||
namespace NWebsec.Core.Helpers |
|||
{ |
|||
public interface IHeaderResultHandler |
|||
{ |
|||
void HandleHeaderResult(HttpResponse response, HeaderResult result); |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.Helpers.X509 |
|||
{ |
|||
internal class TlvTripletHeader |
|||
{ |
|||
public byte Tag { get; set; } |
|||
public int Length { get; set; } |
|||
public byte[] RawData { get; set; } |
|||
} |
|||
} |
|||
@ -1,252 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Security.Cryptography; |
|||
using System.Security.Cryptography.X509Certificates; |
|||
|
|||
namespace NWebsec.Core.Helpers.X509 |
|||
{ |
|||
public class X509Helper |
|||
{ |
|||
private const byte AsnInteger = 0x02; |
|||
private const byte AsnBitString = 0x03; |
|||
private const byte AsnSequence = 0x30; |
|||
private const byte AsnOptional = 0xA0; |
|||
|
|||
private static readonly byte[] AsnTags = { AsnInteger, AsnBitString, AsnSequence, AsnOptional }; |
|||
|
|||
//TODO cleanup. Perhaps a test or two.
|
|||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands"), SecuritySafeCritical]
|
|||
public X509Certificate2 GetCertByThumbprint(string thumbprint, StoreLocation storeLocation, StoreName storeName) |
|||
{ |
|||
X509Store certStore = null; |
|||
X509Certificate2Collection certs = null; |
|||
try |
|||
{ |
|||
certStore = new X509Store(storeName, storeLocation); |
|||
certStore.Open(OpenFlags.ReadOnly); |
|||
certs = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); |
|||
|
|||
if (certs.Count > 1) |
|||
{ |
|||
var message = string.Format("Something went horribly wrong, found more than one cert with thumbprint {0} in store location {1}, storename {2}", thumbprint, storeLocation, storeName); |
|||
throw new Exception(message); |
|||
} |
|||
|
|||
if (certs.Count == 0) |
|||
{ |
|||
var message = string.Format("No certificate with thumbprint {0} in store location {1}, storename {2}", thumbprint, storeLocation, storeName); |
|||
throw new ArgumentException(message); |
|||
} |
|||
|
|||
//Returns new cert, all existing certs will be cleaned up
|
|||
return certs[0]; |
|||
} |
|||
catch |
|||
{ |
|||
if (certs != null) |
|||
{ |
|||
foreach (var cert in certs) |
|||
{ |
|||
CleanupCert(cert); |
|||
} |
|||
} |
|||
if (certStore != null) |
|||
{ |
|||
foreach (var cert in certStore.Certificates) |
|||
{ |
|||
CleanupCert(cert); |
|||
} |
|||
#if DNX451
|
|||
certStore.Close(); |
|||
#elif NET451
|
|||
certStore.Close(); |
|||
#else
|
|||
certStore.Dispose(); |
|||
#endif
|
|||
} |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a string suitable for inclusion in an HPKP header, including hash algoritm.
|
|||
/// </summary>
|
|||
/// <param name="cert"></param>
|
|||
/// <returns></returns>
|
|||
public string GetSubjectPublicKeyInfoPinValue(X509Certificate2 cert) |
|||
{ |
|||
var spki = GetRawSubjectPublicKeyInfo(cert); |
|||
using (var sha256 = SHA256.Create()) |
|||
{ |
|||
var hash = Convert.ToBase64String(sha256.ComputeHash(spki)); |
|||
return string.Format("sha256=\"" + hash + "\""); |
|||
} |
|||
} |
|||
|
|||
private static byte[] GetRawSubjectPublicKeyInfo(X509Certificate2 cert) |
|||
{ |
|||
if (cert.Version != 3) |
|||
{ |
|||
throw new ArgumentException("Only X.509 certificate version 3 is supported. This cert was version " + cert.Version); |
|||
} |
|||
|
|||
var rawCert = cert.RawData; |
|||
|
|||
using (var ms = new MemoryStream(rawCert)) |
|||
{ |
|||
//Get outer cert sequence header
|
|||
var tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence, for start of certificate."); |
|||
//Console.WriteLine("Got certficate sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
|
|||
//Get tbs cert sequence header
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence, for start of tbc cert."); |
|||
//Console.WriteLine("Got tbs certficate sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
|
|||
//Get cert version integer header
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
|
|||
while (tlv.Tag == AsnOptional) |
|||
{ |
|||
//Console.WriteLine("Got optional TLV, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
} |
|||
|
|||
if (tlv.Tag != AsnInteger) throw new Exception("Expected ASN integer cert version."); |
|||
//Console.WriteLine("Got the certficate version, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
|
|||
|
|||
var version = ms.ReadByte(); |
|||
if (version == -1) throw new Exception("Could not read version byte"); |
|||
//Console.WriteLine("Cert version: " + version);
|
|||
|
|||
//Get serial number
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnInteger) throw new Exception("Expected ASN integer serial number."); |
|||
//Console.WriteLine("Got the cert serial number, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
|
|||
var serialNumber = new byte[tlv.Length]; |
|||
|
|||
var read = ms.Read(serialNumber, 0, serialNumber.Length); |
|||
|
|||
if (read < serialNumber.Length) throw new Exception("Expected reading " + tlv.Length + " serial number bytes, got " + read); |
|||
|
|||
//Skip signature sequence
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence signature."); |
|||
//Console.WriteLine("Got the cert signature sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
ms.Seek(tlv.Length, SeekOrigin.Current); |
|||
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
|
|||
|
|||
//Skip issuer sequence
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence issuer."); |
|||
//Console.WriteLine("Got the cert issuer sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
ms.Seek(tlv.Length, SeekOrigin.Current); |
|||
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
|
|||
|
|||
//Skip validity sequence
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence validity."); |
|||
//Console.WriteLine("Got the cert validity sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
ms.Seek(tlv.Length, SeekOrigin.Current); |
|||
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
|
|||
|
|||
//Skip subject sequence
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence subject."); |
|||
//Console.WriteLine("Got the cert subject sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
ms.Seek(tlv.Length, SeekOrigin.Current); |
|||
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
|
|||
|
|||
//Skip subject sequence
|
|||
tlv = ReadTlvTripletHeader(ms); |
|||
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence SPKI."); |
|||
//Console.WriteLine("Got the cert SPKI sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
|
|||
|
|||
//New array for both tlv bits and content bits.
|
|||
var spkiChunk = new byte[tlv.RawData.Length + tlv.Length]; |
|||
|
|||
Array.Copy(tlv.RawData, spkiChunk, tlv.RawData.Length); |
|||
|
|||
read = ms.Read(spkiChunk, tlv.RawData.Length, tlv.Length); |
|||
|
|||
if (read > tlv.Length) throw new Exception("Got " + read + " SPKI bytes, expected " + spkiChunk.Length); |
|||
|
|||
return spkiChunk; |
|||
} |
|||
} |
|||
|
|||
private static TlvTripletHeader ReadTlvTripletHeader(MemoryStream ms) |
|||
{ |
|||
var firstBytes = new byte[2]; |
|||
var read = ms.Read(firstBytes, 0, firstBytes.Length); |
|||
|
|||
if (read < 1) |
|||
{ |
|||
throw new Exception("No data read!"); |
|||
} |
|||
|
|||
if (!AsnTags.Any(t => t == firstBytes[0])) throw new Exception("Unexptected ASN.1 tag byte: " + BitConverter.ToString(firstBytes, 0, 1)); |
|||
|
|||
if (read < 2) |
|||
{ |
|||
throw new Exception("No length byte read!"); |
|||
} |
|||
|
|||
if (firstBytes[1] < 0x80) |
|||
{ |
|||
return new TlvTripletHeader() { Tag = firstBytes[0], Length = firstBytes[1], RawData = firstBytes }; |
|||
} |
|||
|
|||
//Handle multi-byte length.
|
|||
var numberOfLengthBytes = firstBytes[1] - 0x80; |
|||
|
|||
if (numberOfLengthBytes < 1) throw new Exception("Invalid length byte. Indicated multibyte length, with length 0."); |
|||
if (numberOfLengthBytes > 4) throw new NotSupportedException("Leading length byte indicates more than 4 length bytes, which is not supported. Indicated length bytes: " + numberOfLengthBytes); |
|||
|
|||
//Get the bytes
|
|||
var lengthBytes = new byte[numberOfLengthBytes]; |
|||
var bytesRead = ms.Read(lengthBytes, 0, lengthBytes.Length); |
|||
|
|||
if (bytesRead != lengthBytes.Length) throw new Exception(string.Format("Expected {0} length bytes, got {1}", lengthBytes.Length, bytesRead)); |
|||
|
|||
//Got the bytes, make an int.
|
|||
var length = 0; |
|||
//Console.WriteLine("Adding length bytes: " + BitConverter.ToString(lengthBytes));
|
|||
foreach (var lengthByte in lengthBytes) |
|||
{ |
|||
|
|||
//Shift existing bytes so they become more significant. Avoid platform dependent bit fiddling.
|
|||
//Console.WriteLine("Length tweak starting: " + BitConverter.ToString(BitConverter.GetBytes(length)));
|
|||
|
|||
length = length * 256; |
|||
//Console.WriteLine("Shifted length to make room for next byte: " + BitConverter.ToString(BitConverter.GetBytes(length)));
|
|||
|
|||
length += lengthByte; |
|||
//Console.WriteLine("Added next byte: " + BitConverter.ToString(BitConverter.GetBytes(length)));
|
|||
} |
|||
|
|||
var rawbytes = new byte[firstBytes.Length + lengthBytes.Length]; |
|||
Array.Copy(firstBytes, rawbytes, firstBytes.Length); |
|||
Array.Copy(lengthBytes, 0, rawbytes, firstBytes.Length, lengthBytes.Length); |
|||
|
|||
return new TlvTripletHeader { Tag = firstBytes[0], Length = length, RawData = rawbytes }; |
|||
} |
|||
|
|||
private void CleanupCert(X509Certificate2 cert) |
|||
{ |
|||
#if NET451
|
|||
cert.Reset(); |
|||
#elif DNX451
|
|||
cert.Reset(); |
|||
#else
|
|||
cert.Dispose(); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
@ -1,124 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Cryptography.X509Certificates; |
|||
using NWebsec.Core.Helpers.X509; |
|||
using NWebsec.Core.HttpHeaders.Configuration.Validation; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class HpkpOptions : IFluentHpkpOptions |
|||
{ |
|||
private readonly List<string> _pins; |
|||
private readonly HpkpConfigurationValidator _validator; |
|||
|
|||
internal HpkpOptionsConfiguration Config { get; set; } |
|||
|
|||
public HpkpOptions() |
|||
{ |
|||
_pins = new List<string>(); |
|||
Config = new HpkpOptionsConfiguration { Pins = _pins }; |
|||
_validator = new HpkpConfigurationValidator(); |
|||
} |
|||
|
|||
// ReSharper disable once CSharpWarnings::CS0109
|
|||
public IFluentHpkpOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0) |
|||
{ |
|||
if (days < 0) throw new ArgumentOutOfRangeException("days", "Value must be equal to or larger than 0."); |
|||
if (hours < 0) throw new ArgumentOutOfRangeException("hours", "Value must be equal to or larger than 0."); |
|||
if (minutes < 0) throw new ArgumentOutOfRangeException("minutes", "Value must be equal to or larger than 0."); |
|||
if (seconds < 0) throw new ArgumentOutOfRangeException("seconds", "Value must be equal to or larger than 0."); |
|||
|
|||
Config.MaxAge = new TimeSpan(days, hours, minutes, seconds); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions IncludeSubdomains() |
|||
{ |
|||
Config.IncludeSubdomains = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions ReportUri(string reportUri) |
|||
{ |
|||
try |
|||
{ |
|||
_validator.ValidateReportUri(reportUri); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ArgumentException(e.Message, "reportUri"); |
|||
} |
|||
|
|||
Config.ReportUri = reportUri; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions AllResponses() |
|||
{ |
|||
Config.HttpsOnly = false; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions Sha256Pins(params string[] pins) |
|||
{ |
|||
foreach (var pin in pins) |
|||
{ |
|||
try |
|||
{ |
|||
_validator.ValidateRawPin(pin); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ArgumentException(e.Message, "pins"); |
|||
} |
|||
|
|||
var formattedPin = "sha256=\"" + pin + "\""; |
|||
if (!_pins.Contains(formattedPin)) |
|||
{ |
|||
_pins.Add(formattedPin); |
|||
} |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions PinCertificate(string thumbprint, StoreLocation storeLocation = StoreLocation.LocalMachine, |
|||
StoreName storeName = StoreName.My) |
|||
{ |
|||
|
|||
try |
|||
{ |
|||
_validator.ValidateThumbprint(thumbprint); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ArgumentException(e.Message, thumbprint); |
|||
} |
|||
|
|||
var helper = new X509Helper(); |
|||
var cert = helper.GetCertByThumbprint(thumbprint, storeLocation, storeName); |
|||
var pin = helper.GetSubjectPublicKeyInfoPinValue(cert); |
|||
|
|||
#if DNX451
|
|||
cert.Reset(); |
|||
#elif NET451
|
|||
cert.Reset(); |
|||
#else
|
|||
cert.Dispose(); |
|||
#endif
|
|||
if (!_pins.Contains(pin)) |
|||
{ |
|||
_pins.Add(pin); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public IFluentHpkpOptions HttpsOnly() |
|||
{ |
|||
Config.HttpsOnly = true; |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class HpkpOptionsConfiguration : IHpkpConfiguration |
|||
{ |
|||
internal HpkpOptionsConfiguration() |
|||
{ |
|||
MaxAge = TimeSpan.Zero; |
|||
HttpsOnly = true; |
|||
Pins = new string[0]; |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public IEnumerable<string> Pins { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public TimeSpan MaxAge { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IncludeSubdomains { get; set; } |
|||
|
|||
public string ReportUri { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool HttpsOnly { get; set; } |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class HstsOptions : HstsOptionsConfiguration, IFluentHstsOptions |
|||
{ |
|||
|
|||
// ReSharper disable once CSharpWarnings::CS0109
|
|||
public new IFluentHstsOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0) |
|||
{ |
|||
if (days < 0) throw new ArgumentOutOfRangeException(nameof(days), "Value must be equal to or larger than 0."); |
|||
if (hours < 0) throw new ArgumentOutOfRangeException(nameof(hours), "Value must be equal to or larger than 0."); |
|||
if (minutes < 0) throw new ArgumentOutOfRangeException(nameof(minutes), "Value must be equal to or larger than 0."); |
|||
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds), "Value must be equal to or larger than 0."); |
|||
|
|||
base.MaxAge = new TimeSpan(days, hours, minutes, seconds); |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentHstsOptions IncludeSubdomains() |
|||
{ |
|||
base.IncludeSubdomains = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentHstsOptions Preload() |
|||
{ |
|||
base.Preload = true; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentHstsOptions UpgradeInsecureRequests() |
|||
{ |
|||
base.UpgradeInsecureRequests = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentHstsOptions AllResponses() |
|||
{ |
|||
base.HttpsOnly = false; |
|||
return this; |
|||
} |
|||
|
|||
public new IFluentHstsOptions HttpsOnly() |
|||
{ |
|||
base.HttpsOnly = true; |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.ComponentModel; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class HstsOptionsConfiguration : IHstsConfiguration |
|||
{ |
|||
internal HstsOptionsConfiguration() |
|||
{ |
|||
MaxAge = TimeSpan.Zero; |
|||
HttpsOnly = true; |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public TimeSpan MaxAge { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool IncludeSubdomains { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool Preload { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool HttpsOnly { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool UpgradeInsecureRequests { get; set; } |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspConfiguration : ICspConfiguration |
|||
{ |
|||
public CspConfiguration(bool initializeDirectives=true) |
|||
{ |
|||
if (!initializeDirectives) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
DefaultSrcDirective = new CspDirectiveConfiguration(); |
|||
ScriptSrcDirective = new CspDirectiveConfiguration(); |
|||
ObjectSrcDirective = new CspDirectiveConfiguration(); |
|||
StyleSrcDirective = new CspDirectiveConfiguration(); |
|||
ImgSrcDirective = new CspDirectiveConfiguration(); |
|||
MediaSrcDirective = new CspDirectiveConfiguration(); |
|||
FrameSrcDirective = new CspDirectiveConfiguration(); |
|||
FontSrcDirective = new CspDirectiveConfiguration(); |
|||
ConnectSrcDirective = new CspDirectiveConfiguration(); |
|||
BaseUriDirective = new CspDirectiveConfiguration(); |
|||
ChildSrcDirective = new CspDirectiveConfiguration(); |
|||
FormActionDirective = new CspDirectiveConfiguration(); |
|||
FrameAncestorsDirective = new CspDirectiveConfiguration(); |
|||
PluginTypesDirective = new CspPluginTypesDirectiveConfiguration(); |
|||
SandboxDirective = new CspSandboxDirectiveConfiguration(); |
|||
UpgradeInsecureRequestsDirective = new CspUpgradeDirectiveConfiguration(); |
|||
ReportUriDirective = new CspReportUriDirectiveConfiguration(); |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public ICspDirectiveConfiguration DefaultSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration ScriptSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration ObjectSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration StyleSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration ImgSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration MediaSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration FrameSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration FontSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration ConnectSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration BaseUriDirective { get; set; } |
|||
public ICspDirectiveConfiguration ChildSrcDirective { get; set; } |
|||
public ICspDirectiveConfiguration FormActionDirective { get; set; } |
|||
public ICspDirectiveConfiguration FrameAncestorsDirective { get; set; } |
|||
public ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; } |
|||
public ICspSandboxDirectiveConfiguration SandboxDirective { get; set; } |
|||
public ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; } |
|||
public ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; } |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspDirectiveConfiguration : ICspDirectiveConfiguration |
|||
{ |
|||
private static readonly string[] EmptySources = new string[0]; |
|||
|
|||
public CspDirectiveConfiguration() |
|||
{ |
|||
Enabled = true; |
|||
CustomSources = EmptySources; |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public bool NoneSrc { get; set; } |
|||
public bool SelfSrc { get; set; } |
|||
public bool UnsafeInlineSrc { get; set; } |
|||
public bool UnsafeEvalSrc { get; set; } |
|||
public IEnumerable<string> CustomSources { get; set; } |
|||
public string Nonce { get; set; } |
|||
|
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspHeaderConfiguration : ICspHeaderConfiguration |
|||
{ |
|||
public bool Enabled { get; set; } |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspPluginTypesDirectiveConfiguration : ICspPluginTypesDirectiveConfiguration |
|||
{ |
|||
private static readonly string[] EmptySources = new string[0]; |
|||
|
|||
public bool Enabled { get; set; } |
|||
public IEnumerable<string> MediaTypes { get; set; } |
|||
|
|||
public CspPluginTypesDirectiveConfiguration() |
|||
{ |
|||
Enabled = true; |
|||
MediaTypes = EmptySources; |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspReportUriDirectiveConfiguration : ICspReportUriDirectiveConfiguration |
|||
{ |
|||
public CspReportUriDirectiveConfiguration() |
|||
{ |
|||
ReportUris = new string[0]; |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
//TODO figure out what to do with this property
|
|||
public bool EnableBuiltinHandler { get; set; } |
|||
public IEnumerable<string> ReportUris { get; set; } |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspSandboxDirectiveConfiguration : ICspSandboxDirectiveConfiguration |
|||
{ |
|||
public bool Enabled { get; set; } |
|||
public bool AllowForms { get; set; } |
|||
public bool AllowPointerLock { get; set; } |
|||
public bool AllowPopups { get; set; } |
|||
public bool AllowSameOrigin { get; set; } |
|||
public bool AllowScripts { get; set; } |
|||
public bool AllowTopNavigation { get; set; } |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class CspUpgradeDirectiveConfiguration : ICspUpgradeDirectiveConfiguration |
|||
{ |
|||
|
|||
public bool Enabled { get; set; } |
|||
public int HttpsPort { get; set; } = 443; |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class HpkpConfiguration : IHpkpConfiguration |
|||
{ |
|||
public IEnumerable<string> Pins { get; set; } |
|||
public TimeSpan MaxAge { get; set; } |
|||
public bool IncludeSubdomains { get; set; } |
|||
public string ReportUri { get; set; } |
|||
public bool HttpsOnly { get; set; } |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class HstsConfiguration : IHstsConfiguration |
|||
{ |
|||
public TimeSpan MaxAge { get; set; } |
|||
public bool IncludeSubdomains { get; set; } |
|||
public bool Preload { get; set; } |
|||
public bool HttpsOnly { get; set; } |
|||
public bool UpgradeInsecureRequests { get; set; } |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface ICspConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
ICspDirectiveConfiguration DefaultSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration ScriptSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration ObjectSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration StyleSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration ImgSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration MediaSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration FrameSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration FontSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration ConnectSrcDirective { get; set; } |
|||
ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; } |
|||
|
|||
//CSP 2
|
|||
ICspDirectiveConfiguration BaseUriDirective { get; set; } |
|||
ICspDirectiveConfiguration ChildSrcDirective { get; set; } |
|||
ICspDirectiveConfiguration FormActionDirective { get; set; } |
|||
ICspDirectiveConfiguration FrameAncestorsDirective { get; set; } |
|||
ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; } |
|||
ICspSandboxDirectiveConfiguration SandboxDirective { get; set; } |
|||
|
|||
//Upgrade insecure requests
|
|||
ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; } |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspDirectiveBasicConfiguration : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool Enabled { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool NoneSrc { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool SelfSrc { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
IEnumerable<string> CustomSources { get; set; } |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.ComponentModel; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspDirectiveConfiguration : ICspDirectiveUnsafeInlineConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool UnsafeEvalSrc { get; set; } |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.ComponentModel; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspDirectiveUnsafeInlineConfiguration : ICspDirectiveBasicConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
bool UnsafeInlineSrc { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
string Nonce { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface ICspHeaderConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP sandbox directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspPluginTypesDirectiveConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool Enabled { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
IEnumerable<string> MediaTypes { get; set; } |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface ICspReportUriDirectiveConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
|
|||
bool EnableBuiltinHandler { get; set; } |
|||
|
|||
IEnumerable<string> ReportUris { get; set; } |
|||
} |
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP sandbox directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspSandboxDirectiveConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool Enabled { get; set; } |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowForms { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowPointerLock { get; set; } |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowPopups { get; set; } |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowSameOrigin { get; set; } |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowScripts { get; set; } |
|||
/// <summary>
|
|||
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
|
|||
/// made.
|
|||
/// </summary>
|
|||
bool AllowTopNavigation { get; set; } |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the properties required for CSP sandbox directive configuration.
|
|||
/// </summary>
|
|||
public interface ICspUpgradeDirectiveConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
|
|||
int HttpsPort { get; set; } |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Security.Cryptography.X509Certificates; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IHpkpCertConfiguration |
|||
{ |
|||
string ThumbPrint { get; set; } |
|||
StoreLocation StoreLocation { get; set; } |
|||
StoreName Storename { get; set; } |
|||
string SpkiPinValue { get; set; } |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IHpkpConfiguration |
|||
{ |
|||
IEnumerable<string> Pins { get; set; } |
|||
TimeSpan MaxAge { get; set; } |
|||
bool IncludeSubdomains { get; set; } |
|||
string ReportUri { get; set; } |
|||
bool HttpsOnly { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IHpkpPinConfiguration |
|||
{ |
|||
string Pin { get; set; } |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IHstsConfiguration |
|||
{ |
|||
TimeSpan MaxAge { get; set; } |
|||
|
|||
bool IncludeSubdomains { get; set; } |
|||
|
|||
bool Preload { get; set; } |
|||
|
|||
bool HttpsOnly { get; set; } |
|||
|
|||
bool UpgradeInsecureRequests { get; set; } |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IRedirectValidationConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// URIs allowed for redirect. Strings in this list should be created with Uri.AbsoluteUri to assure consistency.
|
|||
/// </summary>
|
|||
IEnumerable<string> AllowedUris { get; set; } |
|||
|
|||
ISameHostHttpsRedirectConfiguration SameHostRedirectConfiguration { get; set; } |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface ISameHostHttpsRedirectConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
int[] Ports { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface ISimpleBooleanConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IXFrameOptionsConfiguration |
|||
{ |
|||
XfoPolicy Policy { get; set; } |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IXRobotsTagConfiguration |
|||
{ |
|||
bool Enabled { get; set; } |
|||
|
|||
bool NoIndex { get; set; } |
|||
|
|||
bool NoFollow { get; set; } |
|||
|
|||
bool NoSnippet { get; set; } |
|||
|
|||
bool NoArchive { get; set; } |
|||
|
|||
bool NoOdp { get; set; } |
|||
|
|||
bool NoTranslate { get; set; } |
|||
|
|||
bool NoImageIndex { get; set; } |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public interface IXXssProtectionConfiguration |
|||
{ |
|||
XXssPolicy Policy { get; set; } |
|||
|
|||
bool BlockMode { get; set; } |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
internal class RedirectValidationConfiguration : IRedirectValidationConfiguration |
|||
{ |
|||
public RedirectValidationConfiguration() |
|||
{ |
|||
AllowedUris = new string[0]; |
|||
SameHostRedirectConfiguration = new SameHostHttpsRedirectConfiguration(); |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public IEnumerable<string> AllowedUris { get; set; } |
|||
public ISameHostHttpsRedirectConfiguration SameHostRedirectConfiguration { get; set; } |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class SameHostHttpsRedirectConfiguration : ISameHostHttpsRedirectConfiguration |
|||
{ |
|||
public SameHostHttpsRedirectConfiguration() |
|||
{ |
|||
Ports = new int[0]; |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public int[] Ports { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class SimpleBooleanConfiguration : ISimpleBooleanConfiguration |
|||
{ |
|||
public bool Enabled { get; set; } |
|||
} |
|||
} |
|||
@ -1,55 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration.Validation |
|||
{ |
|||
public class HpkpConfigurationValidator |
|||
{ |
|||
private static readonly string[] ValidSchemes = { "http", "https" }; |
|||
|
|||
public void ValidateNumberOfPins(IHpkpConfiguration hpkpConfig) |
|||
{ |
|||
if (hpkpConfig.MaxAge > TimeSpan.Zero && hpkpConfig.Pins.Count() < 2) |
|||
{ |
|||
throw new Exception("You must supply two or more HPKP pins. One should represent a certificate currently in use, you should also include a backup pin for a cert/key not (yet) in use."); |
|||
} |
|||
} |
|||
|
|||
public void ValidateRawPin(string pin) |
|||
{ |
|||
var bytes = Convert.FromBase64String(pin); |
|||
|
|||
if (bytes.Length != 32) |
|||
{ |
|||
throw new Exception("Expected a 256 bit pin value, it was " + bytes.Length * 8 + " bits: " + pin); |
|||
} |
|||
} |
|||
|
|||
public void ValidateThumbprint(string thumbPrint) |
|||
{ |
|||
if (Regex.IsMatch(thumbPrint, "^([a-fA-F0-9]{2} ?){19}[a-fA-F0-9]{2}$")) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
throw new Exception("Malformed thumbprint, expected 20 HEX octets without any leading or trailing whitespace, was: " + thumbPrint); |
|||
} |
|||
|
|||
public void ValidateReportUri(string reportUri) |
|||
{ |
|||
Uri result; |
|||
if (!Uri.TryCreate(reportUri, UriKind.Absolute, out result)) |
|||
{ |
|||
throw new Exception("Report URIs must be absolute URIs. This is not: " + reportUri); |
|||
} |
|||
|
|||
if (!ValidSchemes.Any(s => s.Equals(result.Scheme))) |
|||
{ |
|||
throw new Exception("Report URIs must have the http or https scheme. Got: " + reportUri); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration.Validation |
|||
{ |
|||
public class HstsConfigurationValidator |
|||
{ |
|||
public void Validate(IHstsConfiguration hstsConfig) |
|||
{ |
|||
if (!hstsConfig.Preload) return; |
|||
|
|||
if (hstsConfig.UpgradeInsecureRequests) |
|||
{ |
|||
throw new Exception("The Preload setting cannot be combined with the UpgradeInsecureRequests setting. Use one or the other."); |
|||
} |
|||
|
|||
if (hstsConfig.MaxAge.TotalSeconds < 10886400 || !hstsConfig.IncludeSubdomains) |
|||
{ |
|||
throw new Exception("HSTS max age must be at least 18 weeks and includesubdomains must be enabled to use the preload directive."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration.Validation |
|||
{ |
|||
public class Rfc2045MediaTypeValidator |
|||
{ |
|||
private static readonly string[] ValidTypes = { "application", "audio", "image","model", "text", "video" }; |
|||
|
|||
public void Validate(string mediaType) |
|||
{ |
|||
if (String.IsNullOrEmpty(mediaType)) throw new ArgumentException("String was null or empty", "mediaType"); |
|||
|
|||
var components = mediaType.Split(new[] { '/' }, 2); |
|||
var type = components[0]; |
|||
|
|||
if (!ValidTypes.Any(t => t.Equals(type, StringComparison.OrdinalIgnoreCase))) |
|||
{ |
|||
var message = String.Format("Media type \"{0}\" did not match any of the expected types: {1}", mediaType, String.Join(", ", ValidTypes)); |
|||
throw new Exception(message); |
|||
} |
|||
|
|||
if (components.Length != 2) |
|||
{ |
|||
throw new Exception("Invalid format for media type. Expected \"type/subtype\" but was: " + mediaType); |
|||
} |
|||
|
|||
var subType = components[1]; |
|||
|
|||
if (!Regex.IsMatch(subType, @"^[\x00-\x7F]*$")) |
|||
{ |
|||
throw new Exception("Subtype contained characters from outside the US-ASCII range, was: " + subType); |
|||
} |
|||
|
|||
if (Regex.IsMatch(subType, @"[\x00-\x20\x7F]+")) |
|||
{ |
|||
throw new Exception("Subtype contained the space character, or an ASCII control character."); |
|||
} |
|||
|
|||
var escapedTspecials = @"[()<>@,;:""\\/[\]?=]+"; |
|||
|
|||
if (Regex.IsMatch(subType, escapedTspecials)) |
|||
{ |
|||
throw new Exception("Subtype contained one of the forbidden tspecial characters: " + Regex.Unescape(escapedTspecials)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration.Validation |
|||
{ |
|||
public class XRobotsTagConfigurationValidator |
|||
{ |
|||
public void Validate(IXRobotsTagConfiguration xRobotsConfig) |
|||
{ |
|||
if (!xRobotsConfig.Enabled) return; |
|||
|
|||
if (xRobotsConfig.NoArchive || |
|||
xRobotsConfig.NoFollow || |
|||
xRobotsConfig.NoImageIndex || |
|||
xRobotsConfig.NoIndex || |
|||
xRobotsConfig.NoOdp || |
|||
xRobotsConfig.NoSnippet || |
|||
xRobotsConfig.NoTranslate) return; |
|||
throw new Exception( |
|||
"One or more directives must be enabled when header is enabled. Enable directives or disable header."); |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class XFrameOptionsConfiguration : IXFrameOptionsConfiguration |
|||
{ |
|||
public XfoPolicy Policy { get; set; } |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class XRobotsTagConfiguration : IXRobotsTagConfiguration |
|||
{ |
|||
public bool Enabled { get; set; } |
|||
public bool NoIndex { get; set; } |
|||
public bool NoFollow { get; set; } |
|||
public bool NoSnippet { get; set; } |
|||
public bool NoArchive { get; set; } |
|||
public bool NoOdp { get; set; } |
|||
public bool NoTranslate { get; set; } |
|||
public bool NoImageIndex { get; set; } |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Configuration |
|||
{ |
|||
public class XXssProtectionConfiguration : IXXssProtectionConfiguration |
|||
{ |
|||
public XXssPolicy Policy { get; set; } |
|||
public bool BlockMode { get; set; } |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Csp |
|||
{ |
|||
internal class CspSourceParseResult |
|||
{ |
|||
public string Scheme { get; set; } |
|||
public string Host { get; set; } |
|||
public string Port { get; set; } |
|||
public string PathAndQuery { get; set; } |
|||
} |
|||
} |
|||
@ -1,172 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Csp |
|||
{ |
|||
public class CspUriSource |
|||
{ |
|||
private const string HostRegex = @"^(\*\.)?([\p{Ll}\p{Lu}0-9\-]+)(\.[\p{Ll}\p{Lu}0-9\-]+)*$"; |
|||
private static readonly string SchemeOnlyRegex = "^[a-zA-Z]*[a-zA-Z0-9" + Regex.Escape("+.-") + "]:$"; |
|||
private static readonly string[] KnownSchemes = { "http", "https", "ws", "wss" }; |
|||
private readonly string _source; |
|||
|
|||
private CspUriSource(string source) |
|||
{ |
|||
_source = source; |
|||
} |
|||
|
|||
// Returns the source as a string encoded according to the CSP spec.
|
|||
public override string ToString() |
|||
{ |
|||
return _source; |
|||
|
|||
} |
|||
|
|||
public static string EncodeUri(Uri uri) |
|||
{ |
|||
|
|||
if (!uri.IsAbsoluteUri) |
|||
{ |
|||
var uriString = uri.IsWellFormedOriginalString() ? uri.ToString() : Uri.EscapeUriString(uri.ToString()); |
|||
return EscapeReservedCspChars(uriString); |
|||
} |
|||
|
|||
var host = uri.Host; |
|||
var encodedHost = EncodeHostname(host); |
|||
|
|||
var needsReplacement = !host.Equals(encodedHost); |
|||
|
|||
var authority = uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped); |
|||
|
|||
if (needsReplacement) |
|||
{ |
|||
authority = authority.Replace(host, encodedHost); |
|||
} |
|||
|
|||
if (uri.PathAndQuery.Equals("/")) |
|||
{ |
|||
return authority; |
|||
} |
|||
|
|||
return authority + EscapeReservedCspChars(uri.PathAndQuery); |
|||
} |
|||
|
|||
public static CspUriSource Parse(string source) |
|||
{ |
|||
if (String.IsNullOrEmpty(source)) throw new ArgumentException("Value was null or empty", "source"); |
|||
|
|||
if (source.Equals("*")) return new CspUriSource(source); |
|||
|
|||
Uri uriResult; //TODO figure out what happened to known schemes.
|
|||
if (Uri.TryCreate(source, UriKind.Absolute, out uriResult) && KnownSchemes.Contains(uriResult.Scheme)) |
|||
{ |
|||
return new CspUriSource(EncodeUri(uriResult)); |
|||
} |
|||
|
|||
//Scheme only source
|
|||
if (Regex.IsMatch(source, SchemeOnlyRegex)) return new CspUriSource(source.ToLower()); |
|||
|
|||
var parseResult = ParseSourceComponents(source); |
|||
var sb = new StringBuilder(); |
|||
|
|||
if (!String.IsNullOrEmpty(parseResult.Scheme)) |
|||
{ |
|||
if (!Regex.IsMatch(parseResult.Scheme, SchemeOnlyRegex)) |
|||
{ |
|||
throw new InvalidCspSourceException("Invalid scheme in CSP source: " + source); |
|||
} |
|||
sb.Append(parseResult.Scheme.ToLower()).Append("//"); |
|||
} |
|||
|
|||
if (String.IsNullOrEmpty(parseResult.Host)) |
|||
{ |
|||
throw new InvalidCspSourceException("Could not parse host in CSP source: " + source); |
|||
} |
|||
|
|||
if (!Regex.IsMatch(parseResult.Host, HostRegex)) |
|||
{ |
|||
throw new InvalidCspSourceException("Invalid host in CSP source: " + source); |
|||
|
|||
} |
|||
|
|||
sb.Append(EncodeHostname(parseResult.Host.ToLower())); |
|||
|
|||
if (!String.IsNullOrEmpty(parseResult.Port)) |
|||
{ |
|||
if (!ValidatePort(parseResult.Port)) |
|||
{ |
|||
throw new InvalidCspSourceException("Invalid port in CSP source: " + source); |
|||
} |
|||
sb.Append(":").Append(parseResult.Port); |
|||
} |
|||
|
|||
if (!String.IsNullOrEmpty(parseResult.PathAndQuery)) |
|||
{ |
|||
sb.Append(EscapeReservedCspChars(Uri.EscapeUriString(parseResult.PathAndQuery))); |
|||
} |
|||
|
|||
return new CspUriSource(sb.ToString()); |
|||
} |
|||
|
|||
private static CspSourceParseResult ParseSourceComponents(string uri) |
|||
{ |
|||
const string regex = @"^((?<scheme>.*?:)\/\/)?" + // match anything up to ://
|
|||
@"(?<host>.*?[^:\/])" + //then match anything up to a : or /
|
|||
@"(:(?<port>(.*?[^\/])))?" + //then match port if exists up to a /
|
|||
@"(?<pathAndQuery>\/.*)?$"; //grab the rest
|
|||
|
|||
var re = new Regex(regex, RegexOptions.ExplicitCapture); |
|||
var result = re.Match(uri); |
|||
|
|||
if (!result.Success) |
|||
{ |
|||
throw new InvalidCspSourceException("Malformed CSP source: " + uri); |
|||
} |
|||
|
|||
return new CspSourceParseResult |
|||
{ |
|||
Scheme = result.Groups["scheme"].Value, |
|||
Host = result.Groups["host"].Value, |
|||
Port = result.Groups["port"].Value, |
|||
PathAndQuery = result.Groups["pathAndQuery"].Value |
|||
}; |
|||
} |
|||
|
|||
private static string EncodeHostname(string hostname) |
|||
{ |
|||
var idn = new IdnMapping(); |
|||
|
|||
return idn.GetAscii(hostname); |
|||
} |
|||
|
|||
private static string EscapeReservedCspChars(string pathAndQuery) |
|||
{ |
|||
char[] encodeChars = { ';', ',' }; |
|||
|
|||
if (pathAndQuery.IndexOfAny(encodeChars) == -1) |
|||
{ |
|||
return pathAndQuery; |
|||
} |
|||
|
|||
var sb = new StringBuilder(pathAndQuery); |
|||
sb.Replace(";", "%3B"); |
|||
sb.Replace(",", "%2C"); |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
private static bool ValidatePort(string port) |
|||
{ |
|||
if (port.Equals("*")) return true; |
|||
|
|||
int portNumber; |
|||
var isInt = Int32.TryParse(port, out portNumber); |
|||
return isInt && portNumber > 0 && portNumber <= 65535; |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders.Csp |
|||
{ |
|||
//[Serializable]
|
|||
public class InvalidCspSourceException : Exception |
|||
{ |
|||
public InvalidCspSourceException(string s) |
|||
: base(s) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public class HeaderConstants |
|||
{ |
|||
public static readonly string XFrameOptionsHeader = "X-Frame-Options"; |
|||
public static readonly string XRobotsTagHeader = "X-Robots-Tag"; |
|||
public static readonly string StrictTransportSecurityHeader = "Strict-Transport-Security"; |
|||
public static readonly string XContentTypeOptionsHeader = "X-Content-Type-Options"; |
|||
public static readonly string XDownloadOptionsHeader = "X-Download-Options"; |
|||
public static readonly string XXssProtectionHeader = "X-XSS-Protection"; |
|||
public static readonly string ContentSecurityPolicyHeader = "Content-Security-Policy"; |
|||
public static readonly string ContentSecurityPolicyReportOnlyHeader = "Content-Security-Policy-Report-Only"; |
|||
public static readonly string HpkpHeader = "Public-Key-Pins"; |
|||
public static readonly string HpkpReportOnlyHeader = "Public-Key-Pins-Report-Only"; |
|||
|
|||
public static readonly string[] CspSourceList = |
|||
{ |
|||
"'none'", |
|||
"'self'", |
|||
"'unsafe-inline'", |
|||
"'unsafe-eval'" |
|||
}; |
|||
|
|||
public static readonly string[] CspDirectives = |
|||
{ |
|||
"default-src", |
|||
"script-src", |
|||
"object-src", |
|||
"style-src", |
|||
"img-src", |
|||
"media-src", |
|||
"frame-src", |
|||
"font-src", |
|||
"connect-src", |
|||
"report-uri" |
|||
}; |
|||
|
|||
public static readonly string[] CspSchemes = |
|||
{ |
|||
"data:", |
|||
"https:", |
|||
"http:" |
|||
}; |
|||
} |
|||
} |
|||
@ -1,373 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public class HeaderGenerator : IHeaderGenerator |
|||
{ |
|||
public HeaderResult CreateXRobotsTagResult(IXRobotsTagConfiguration xRobotsTagConfig, |
|||
IXRobotsTagConfiguration oldXRobotsTagConfig = null) |
|||
{ |
|||
if (oldXRobotsTagConfig != null && oldXRobotsTagConfig.Enabled && xRobotsTagConfig.Enabled == false) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XRobotsTagHeader); |
|||
} |
|||
|
|||
if (xRobotsTagConfig.Enabled == false) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var sb = new StringBuilder(); |
|||
sb.Append(xRobotsTagConfig.NoIndex ? "noindex, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoFollow ? "nofollow, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoSnippet && !xRobotsTagConfig.NoIndex ? "nosnippet, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoArchive && !xRobotsTagConfig.NoIndex ? "noarchive, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoOdp && !xRobotsTagConfig.NoIndex ? "noodp, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoTranslate && !xRobotsTagConfig.NoIndex ? "notranslate, " : String.Empty); |
|||
sb.Append(xRobotsTagConfig.NoImageIndex ? "noimageindex" : String.Empty); |
|||
var value = sb.ToString().TrimEnd(' ', ','); |
|||
|
|||
if (value.Length == 0) return null; |
|||
|
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XRobotsTagHeader, value); |
|||
} |
|||
|
|||
public HeaderResult CreateHstsResult(IHstsConfiguration hstsConfig) |
|||
{ |
|||
if (hstsConfig.MaxAge < TimeSpan.Zero) return null; |
|||
|
|||
if (hstsConfig.Preload && (hstsConfig.MaxAge.TotalSeconds < 10886400 || !hstsConfig.IncludeSubdomains)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var seconds = (int)hstsConfig.MaxAge.TotalSeconds; |
|||
|
|||
var includeSubdomains = (hstsConfig.IncludeSubdomains ? "; includeSubdomains" : ""); |
|||
var preload = (hstsConfig.Preload ? "; preload" : ""); |
|||
var value = string.Format("max-age={0}{1}{2}", seconds, includeSubdomains, preload); |
|||
|
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.StrictTransportSecurityHeader, |
|||
value); |
|||
} |
|||
|
|||
public HeaderResult CreateXContentTypeOptionsResult(ISimpleBooleanConfiguration xContentTypeOptionsConfig, |
|||
ISimpleBooleanConfiguration oldXContentTypeOptionsConfig = null) |
|||
{ |
|||
if (oldXContentTypeOptionsConfig != null && oldXContentTypeOptionsConfig.Enabled && |
|||
!xContentTypeOptionsConfig.Enabled) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XContentTypeOptionsHeader); |
|||
} |
|||
|
|||
return xContentTypeOptionsConfig.Enabled |
|||
? new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XContentTypeOptionsHeader, "nosniff") |
|||
: null; |
|||
} |
|||
|
|||
public HeaderResult CreateXDownloadOptionsResult(ISimpleBooleanConfiguration xDownloadOptionsConfig, |
|||
ISimpleBooleanConfiguration oldXDownloadOptionsConfig = null) |
|||
{ |
|||
if (oldXDownloadOptionsConfig != null && oldXDownloadOptionsConfig.Enabled && |
|||
!xDownloadOptionsConfig.Enabled) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XDownloadOptionsHeader); |
|||
} |
|||
return xDownloadOptionsConfig.Enabled |
|||
? new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XDownloadOptionsHeader, "noopen") |
|||
: null; |
|||
} |
|||
|
|||
public HeaderResult CreateXXssProtectionResult(IXXssProtectionConfiguration xXssProtectionConfig, |
|||
IXXssProtectionConfiguration oldXXssProtectionConfig = null) |
|||
{ |
|||
if (oldXXssProtectionConfig != null && oldXXssProtectionConfig.Policy != XXssPolicy.Disabled && |
|||
xXssProtectionConfig.Policy == XXssPolicy.Disabled) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XXssProtectionHeader); |
|||
} |
|||
|
|||
string value; |
|||
switch (xXssProtectionConfig.Policy) |
|||
{ |
|||
case XXssPolicy.Disabled: |
|||
return null; |
|||
|
|||
case XXssPolicy.FilterDisabled: |
|||
value = "0"; |
|||
break; |
|||
|
|||
case XXssPolicy.FilterEnabled: |
|||
value = (xXssProtectionConfig.BlockMode ? "1; mode=block" : "1"); |
|||
break; |
|||
|
|||
default: |
|||
throw new NotImplementedException("Somebody apparently forgot to implement support for: " + |
|||
xXssProtectionConfig.Policy); |
|||
} |
|||
|
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XXssProtectionHeader, value); |
|||
} |
|||
|
|||
public HeaderResult CreateXfoResult(IXFrameOptionsConfiguration xfoConfig, |
|||
IXFrameOptionsConfiguration oldXfoConfig = null) |
|||
{ |
|||
if (oldXfoConfig != null && oldXfoConfig.Policy != XfoPolicy.Disabled && |
|||
xfoConfig.Policy == XfoPolicy.Disabled) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XFrameOptionsHeader); |
|||
} |
|||
|
|||
switch (xfoConfig.Policy) |
|||
{ |
|||
case XfoPolicy.Disabled: |
|||
return null; |
|||
|
|||
case XfoPolicy.Deny: |
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XFrameOptionsHeader, "Deny"); |
|||
|
|||
case XfoPolicy.SameOrigin: |
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XFrameOptionsHeader, |
|||
"SameOrigin"); |
|||
|
|||
default: |
|||
throw new NotImplementedException("Apparently someone forgot to implement support for: " + |
|||
xfoConfig.Policy); |
|||
} |
|||
} |
|||
|
|||
public HeaderResult CreateHpkpResult(IHpkpConfiguration hpkpConfig, bool reportOnly) |
|||
{ |
|||
if (hpkpConfig.MaxAge < TimeSpan.Zero || hpkpConfig.Pins == null || !hpkpConfig.Pins.Any()) return null; |
|||
|
|||
var headerName = reportOnly ? HeaderConstants.HpkpReportOnlyHeader : HeaderConstants.HpkpHeader; |
|||
|
|||
var seconds = (int)hpkpConfig.MaxAge.TotalSeconds; |
|||
//Unpinning. Save a few bytes by ignoring other directives.
|
|||
if (seconds == 0) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, headerName, "max-age=" + seconds); |
|||
} |
|||
|
|||
var sb = new StringBuilder(); |
|||
sb.Append("max-age=").Append(seconds).Append(";"); |
|||
|
|||
if (hpkpConfig.IncludeSubdomains) |
|||
{ |
|||
sb.Append("includeSubdomains;"); |
|||
} |
|||
|
|||
foreach (var pin in hpkpConfig.Pins) |
|||
{ |
|||
sb.Append("pin-").Append(pin).Append(";"); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(hpkpConfig.ReportUri)) |
|||
{ |
|||
sb.Remove(sb.Length - 1, 1); |
|||
} |
|||
else |
|||
{ |
|||
sb.Append("report-uri=\"").Append(hpkpConfig.ReportUri).Append("\""); |
|||
} |
|||
|
|||
var value = sb.ToString(); |
|||
|
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, headerName, value); |
|||
} |
|||
|
|||
public HeaderResult CreateCspResult(ICspConfiguration cspConfig, bool reportOnly, |
|||
string builtinReportHandlerUri = null, ICspConfiguration oldCspConfig = null) |
|||
{ |
|||
var headerValue = cspConfig.Enabled ? CreateCspHeaderValue(cspConfig, builtinReportHandlerUri) : null; |
|||
|
|||
if (oldCspConfig != null && oldCspConfig.Enabled) |
|||
{ |
|||
if (!cspConfig.Enabled || headerValue == null) |
|||
{ |
|||
return new HeaderResult(HeaderResult.ResponseAction.Remove, |
|||
(reportOnly ? HeaderConstants.ContentSecurityPolicyReportOnlyHeader : HeaderConstants.ContentSecurityPolicyHeader)); |
|||
|
|||
} |
|||
} |
|||
|
|||
if (!cspConfig.Enabled || headerValue == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return new HeaderResult(HeaderResult.ResponseAction.Set, |
|||
(reportOnly ? HeaderConstants.ContentSecurityPolicyReportOnlyHeader : HeaderConstants.ContentSecurityPolicyHeader), headerValue); |
|||
} |
|||
|
|||
private string CreateCspHeaderValue(ICspConfiguration config, string builtinReportHandlerUri = null) |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
AppendDirective(sb, "default-src", GetDirectiveList(config.DefaultSrcDirective)); |
|||
AppendDirective(sb, "script-src", GetDirectiveList(config.ScriptSrcDirective)); |
|||
AppendDirective(sb, "object-src", GetDirectiveList(config.ObjectSrcDirective)); |
|||
AppendDirective(sb, "style-src", GetDirectiveList(config.StyleSrcDirective)); |
|||
AppendDirective(sb, "img-src", GetDirectiveList(config.ImgSrcDirective)); |
|||
AppendDirective(sb, "media-src", GetDirectiveList(config.MediaSrcDirective)); |
|||
AppendDirective(sb, "frame-src", GetDirectiveList(config.FrameSrcDirective)); |
|||
AppendDirective(sb, "font-src", GetDirectiveList(config.FontSrcDirective)); |
|||
AppendDirective(sb, "connect-src", GetDirectiveList(config.ConnectSrcDirective)); |
|||
AppendDirective(sb, "base-uri", GetDirectiveList(config.BaseUriDirective)); |
|||
AppendDirective(sb, "child-src", GetDirectiveList(config.ChildSrcDirective)); |
|||
AppendDirective(sb, "form-action", GetDirectiveList(config.FormActionDirective)); |
|||
AppendDirective(sb, "frame-ancestors", GetDirectiveList(config.FrameAncestorsDirective)); |
|||
AppendDirective(sb, "plugin-types", GetPluginTypesDirectiveList(config.PluginTypesDirective)); |
|||
AppendDirective(sb, "sandbox", GetSandboxDirectiveList(config.SandboxDirective)); |
|||
AppendUpgradeDirective(sb, "upgrade-insecure-requests", config.UpgradeInsecureRequestsDirective); |
|||
|
|||
if (sb.Length == 0) return null; |
|||
|
|||
AppendDirective(sb, "report-uri", |
|||
GetReportUriList(config.ReportUriDirective, builtinReportHandlerUri)); |
|||
|
|||
//Get rid of trailing ;
|
|||
sb.Length--; |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
private void AppendDirective(StringBuilder sb, string directiveName, List<string> sources) |
|||
{ |
|||
if (sources == null) return; |
|||
|
|||
sb.Append(directiveName); |
|||
|
|||
foreach (var source in sources) |
|||
{ |
|||
sb.Append(' ').Append(source); |
|||
} |
|||
|
|||
sb.Append(';'); |
|||
} |
|||
|
|||
private void AppendUpgradeDirective(StringBuilder sb, string directiveName, ICspUpgradeDirectiveConfiguration config) |
|||
{ |
|||
if (!config.Enabled) return; |
|||
|
|||
sb.Append(directiveName); |
|||
sb.Append(';'); |
|||
} |
|||
|
|||
private List<string> GetDirectiveList(ICspDirectiveConfiguration directive) |
|||
{ |
|||
if (directive == null || !directive.Enabled) |
|||
return null; |
|||
|
|||
var sources = new List<string>(); |
|||
|
|||
if (directive.NoneSrc) |
|||
{ |
|||
sources.Add("'none'"); |
|||
} |
|||
|
|||
if (directive.SelfSrc) |
|||
{ |
|||
sources.Add("'self'"); |
|||
} |
|||
|
|||
if (directive.UnsafeInlineSrc) |
|||
{ |
|||
sources.Add("'unsafe-inline'"); |
|||
} |
|||
|
|||
if (!String.IsNullOrEmpty(directive.Nonce)) |
|||
{ |
|||
var nonce = $"'nonce-{directive.Nonce}'"; |
|||
sources.Add(nonce); |
|||
} |
|||
|
|||
if (directive.UnsafeEvalSrc) |
|||
{ |
|||
sources.Add("'unsafe-eval'"); |
|||
} |
|||
|
|||
if (directive.CustomSources != null) |
|||
{ |
|||
sources.AddRange(directive.CustomSources); |
|||
} |
|||
|
|||
return sources.Count > 0 ? sources : null; |
|||
} |
|||
|
|||
private List<string> GetPluginTypesDirectiveList(ICspPluginTypesDirectiveConfiguration directive) |
|||
{ |
|||
if (directive == null || !directive.Enabled || !directive.MediaTypes.Any()) |
|||
return null; |
|||
|
|||
//We know there are MediaTypes, so not null.
|
|||
return new List<string>(directive.MediaTypes); |
|||
} |
|||
|
|||
private List<string> GetSandboxDirectiveList(ICspSandboxDirectiveConfiguration directive) |
|||
{ |
|||
if (directive == null || !directive.Enabled) |
|||
return null; |
|||
|
|||
var sources = new List<string>(); |
|||
|
|||
if (directive.AllowForms) |
|||
{ |
|||
sources.Add("allow-forms"); |
|||
} |
|||
|
|||
if (directive.AllowPointerLock) |
|||
{ |
|||
sources.Add("allow-pointer-lock"); |
|||
} |
|||
|
|||
if (directive.AllowPopups) |
|||
{ |
|||
sources.Add("allow-popups"); |
|||
} |
|||
|
|||
if (directive.AllowSameOrigin) |
|||
{ |
|||
sources.Add("allow-same-origin"); |
|||
} |
|||
|
|||
if (directive.AllowScripts) |
|||
{ |
|||
sources.Add("allow-scripts"); |
|||
} |
|||
|
|||
if (directive.AllowTopNavigation) |
|||
{ |
|||
sources.Add("allow-top-navigation"); |
|||
} |
|||
|
|||
return sources; //We want to return empty list and not null
|
|||
} |
|||
|
|||
private List<string> GetReportUriList(ICspReportUriDirectiveConfiguration directive, |
|||
string builtinReportHandlerUri = null) |
|||
{ |
|||
if (directive == null || !directive.Enabled) |
|||
return null; |
|||
|
|||
var reportUris = new List<string>(); |
|||
|
|||
if (directive.EnableBuiltinHandler) |
|||
{ |
|||
reportUris.Add(builtinReportHandlerUri); |
|||
} |
|||
|
|||
if (directive.ReportUris != null) |
|||
{ |
|||
reportUris.AddRange(directive.ReportUris); |
|||
} |
|||
|
|||
return reportUris.Count > 0 ? reportUris : null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public class HeaderResult |
|||
{ |
|||
public enum ResponseAction |
|||
{ |
|||
Set = 0, |
|||
Remove = 1 |
|||
} |
|||
|
|||
public HeaderResult(ResponseAction action, string name, string value = null) |
|||
{ |
|||
Action = action; |
|||
Name = name; |
|||
Value = value; |
|||
} |
|||
|
|||
|
|||
public ResponseAction Action { get; set; } |
|||
public string Name { get; set; } |
|||
public string Value { get; set; } |
|||
} |
|||
} |
|||
@ -1,31 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public interface IHeaderGenerator |
|||
{ |
|||
HeaderResult CreateXRobotsTagResult(IXRobotsTagConfiguration xRobotsTagConfig, |
|||
IXRobotsTagConfiguration oldXRobotsTagConfig = null); |
|||
|
|||
HeaderResult CreateHstsResult(IHstsConfiguration hstsConfig); |
|||
|
|||
HeaderResult CreateXContentTypeOptionsResult(ISimpleBooleanConfiguration xContentTypeOptionsConfig, |
|||
ISimpleBooleanConfiguration oldXContentTypeOptionsConfig = null); |
|||
|
|||
HeaderResult CreateXDownloadOptionsResult(ISimpleBooleanConfiguration xDownloadOptionsConfig, |
|||
ISimpleBooleanConfiguration oldXDownloadOptionsConfig = null); |
|||
|
|||
HeaderResult CreateXXssProtectionResult(IXXssProtectionConfiguration xXssProtectionConfig, |
|||
IXXssProtectionConfiguration oldXXssProtectionConfig = null); |
|||
|
|||
HeaderResult CreateXfoResult(IXFrameOptionsConfiguration xfoConfig, |
|||
IXFrameOptionsConfiguration oldXfoConfig = null); |
|||
|
|||
HeaderResult CreateCspResult(ICspConfiguration cspConfig, bool reportOnly, |
|||
string builtinReportHandlerUri = null, ICspConfiguration oldCspConfig = null); |
|||
|
|||
HeaderResult CreateHpkpResult(IHpkpConfiguration hpkpConfig, bool reportOnly); |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public enum XXssPolicy |
|||
{ |
|||
/// <summary>Specifies that the X-Xss-Protection header should not be set in the HTTP response.</summary>
|
|||
Disabled, |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the X-Xss-Protection header should be set in the HTTP response, explicitly disabling the IE XSS
|
|||
/// filter.
|
|||
/// </summary>
|
|||
FilterDisabled, |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the X-Xss-Protection header should be set in the HTTP response, explicitly enabling the IE XSS
|
|||
/// filter.
|
|||
/// </summary>
|
|||
FilterEnabled |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
namespace NWebsec.Core.HttpHeaders |
|||
{ |
|||
public enum XfoPolicy |
|||
{ |
|||
/// <summary>Specifies that the X-Frame-Options header should not be set in the HTTP response.</summary>
|
|||
Disabled, |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the X-Frame-Options header should be set in the HTTP response, instructing the browser to not
|
|||
/// display the page when it is loaded in an iframe.
|
|||
/// </summary>
|
|||
Deny, |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the X-Frame-Options header should be set in the HTTP response, instructing the browser to
|
|||
/// display the page when it is loaded in an iframe - but only if the iframe is from the same origin as the page.
|
|||
/// </summary>
|
|||
SameOrigin |
|||
} |
|||
} |
|||
@ -1,142 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using NWebsec.Core.Fluent; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for Content-Security-Options.
|
|||
/// </summary>
|
|||
public interface IFluentCspOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Configures the default-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions DefaultSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the script-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ScriptSources(Action<ICspDirectiveConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the object-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ObjectSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the style-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions StyleSources(Action<ICspDirectiveUnsafeInlineConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the image-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ImageSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the media-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions MediaSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the frame-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions FrameSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the font-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions FontSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the connect-src directive (CSP 1.0).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ConnectSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the base-uri directive (CSP 2).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions BaseUris(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the child-src directive (CSP 2).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ChildSources(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the form-action directive (CSP 2).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions FormActions(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the fram-ancestors directive (CSP 2).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions FrameAncestors(Action<ICspDirectiveBasicConfiguration> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Configures the plugin-types directive (CSP 2).
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the media types for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions PluginTypes(Action<IFluentCspPluginTypesDirective> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Enables the sandbox directive (CSP 2) without further ado.
|
|||
/// </summary>
|
|||
/// <remarks>Support for this directive was optional in CSP 1.0, but is mandatory as of CSP 2.</remarks>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions Sandbox(); |
|||
|
|||
/// <summary>
|
|||
/// Configures the sandbox directive (CSP 2) with one or more sources.
|
|||
/// </summary>
|
|||
/// <remarks>Support for this directive was optional in CSP 1.0, but is mandatory as of CSP 2.</remarks>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions Sandbox(Action<IFluentCspSandboxDirective> configurer); |
|||
|
|||
/// <summary>
|
|||
/// Enables the upgrade-insecure-requests directive and redirects conformant UAs to HTTPS.
|
|||
/// </summary>
|
|||
/// <remarks>This directive is not part of CSP 1.0 or CSP 2, but is described in a separate specification.</remarks>
|
|||
/// <param name="httpsPort">The HTTPS port. Defaults to 443.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions UpgradeInsecureRequests(int httpsPort = 443); |
|||
|
|||
/// <summary>
|
|||
/// Configures the report-uri directive (CSP 1.0). Support for absolute URIs was introduced in CSP 2.
|
|||
/// </summary>
|
|||
/// <param name="configurer">An <see cref="Action"/> that configures the report URIs.</param>
|
|||
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
|
|||
IFluentCspOptions ReportUris(Action<IFluentCspReportUriDirective> configurer); |
|||
} |
|||
} |
|||
@ -1,14 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public interface IFluentCspPluginTypesDirective : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the media types for the CSP plugin-types directive.
|
|||
/// </summary>
|
|||
void MediaTypes(params string[] pluginType); |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure report URIs.
|
|||
/// </summary>
|
|||
public interface IFluentCspReportUriDirective : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Sets report URIs for the CSP directive.
|
|||
/// </summary>
|
|||
/// <param name="reportUris">One or more report URIs.</param>
|
|||
void Uris(params string[] reportUris); |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public interface IFluentCspSandboxDirective : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Sets the 'allow-forms' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowForms(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the 'allow-pointer-lock' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowPointerLock(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the 'allow-popups' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowPopups(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the 'allow-same-origin' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowSameOrigin(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the 'allow-scripts' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowScripts(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the 'allow-top-navigation' source for the CSP sandbox directive.
|
|||
/// </summary>
|
|||
IFluentCspSandboxDirective AllowTopNavigation(); |
|||
} |
|||
} |
|||
@ -1,62 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Security.Cryptography.X509Certificates; |
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for Http Strict Transport Security.
|
|||
/// </summary>
|
|||
public interface IFluentHpkpOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Specifies the max age for the HPKP header.
|
|||
/// </summary>
|
|||
/// <param name="days">The number of days added to max age.</param>
|
|||
/// <param name="hours">The number of hours added to max age.</param>
|
|||
/// <param name="minutes">The number of minutes added to max age.</param>
|
|||
/// <param name="seconds">The number of seconds added to max age.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value was supplied in any of the parameters.</exception>
|
|||
IFluentHpkpOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0); |
|||
|
|||
/// <summary>
|
|||
/// Enables the IncludeSubdomains directive in the HPKP header.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHpkpOptions IncludeSubdomains(); |
|||
|
|||
/// <summary>
|
|||
/// Specifies a report URI where the browser can send HPKP violations.
|
|||
/// </summary>
|
|||
/// <param name="reportUri">The report URI, which is an absolute URI with scheme http or https.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHpkpOptions ReportUri(string reportUri); |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the HPKP header should also be set for HTTP responses. The header is always set for HTTPS responses.
|
|||
/// </summary>
|
|||
/// <remarks>The HPKP standard specifies that the header should only be set over secure connections, which is the default behavior.
|
|||
/// This configuration option exists to accomodate websites running behind an SSL terminator.</remarks>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHpkpOptions AllResponses(); |
|||
|
|||
/// <summary>
|
|||
/// Specifies one or more certificate pins to include in the HPKP header. A certificate pin is the Base64 encoded SHA-256 hash value of a certficate's SPKI.
|
|||
/// </summary>
|
|||
/// <param name="pins">One or more certficate pin values.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHpkpOptions Sha256Pins(params string[] pins); |
|||
|
|||
/// <summary>
|
|||
/// Specifies a certificate that should be pinned in the HPKP header.
|
|||
/// </summary>
|
|||
/// <param name="thumbprint">The certificate thumbprint.</param>
|
|||
/// <param name="storeLocation">The <see cref="StoreLocation"/> for the certificate. The default is <see cref="StoreLocation.LocalMachine"/>.</param>
|
|||
/// <param name="storeName">The <see cref="StoreName"/> for the certificate. The default is <see cref="StoreName.My"/>.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHpkpOptions PinCertificate(string thumbprint, StoreLocation storeLocation = StoreLocation.LocalMachine, StoreName storeName = StoreName.My); |
|||
} |
|||
} |
|||
@ -1,59 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for Http Strict Transport Security.
|
|||
/// </summary>
|
|||
public interface IFluentHstsOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Specifies the max age for the HSTS header.
|
|||
/// </summary>
|
|||
/// <param name="days">The number of days added to max age.</param>
|
|||
/// <param name="hours">The number of hours added to max age.</param>
|
|||
/// <param name="minutes">The number of minutes added to max age.</param>
|
|||
/// <param name="seconds">The number of seconds added to max age.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value was supplied in any of the parameters.</exception>
|
|||
IFluentHstsOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0); |
|||
|
|||
/// <summary>
|
|||
/// Enables the IncludeSubdomains directive in the Hsts header.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHstsOptions IncludeSubdomains(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the Preload directive in the HSTS header. MaxAge must be at least 18 weeks, and IncludeSubdomains must be enabled.
|
|||
/// </summary>
|
|||
/// <remarks>Read more about preloaded HSTS sites at <a href="https://www.chromium.org/hsts">www.chromium.org/sts</a></remarks>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHstsOptions Preload(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the HSTS header only when the user agent signals that it supports the upgrade-insecure-requests CSP directive.
|
|||
/// </summary>
|
|||
/// <remarks>This setting is intended to be used in combination with the upgrade-insecure-requests CSP directive.</remarks>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHstsOptions UpgradeInsecureRequests(); |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the HSTS header should also be set for HTTP responses. The header is always set for HTTPS responses.
|
|||
/// </summary>
|
|||
/// <remarks>The HSTS standard specifies that the header should only be set over secure connections, which is the default behavior.
|
|||
/// This configuration option exists to accomodate websites running behind an SSL terminator.</remarks>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentHstsOptions AllResponses(); |
|||
|
|||
/// <summary>
|
|||
/// Specifies that the HSTS header should be set for HTTPS responses only.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
[Obsolete("This method is deprecated as the default has been changed to HTTPS only.", false)] |
|||
IFluentHstsOptions HttpsOnly(); |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for redirect validation.
|
|||
/// </summary>
|
|||
public interface IFluentRedirectValidationOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Configures the allowed redirect destinations. These must be well formed absolute URIs.
|
|||
/// </summary>
|
|||
/// <param name="uris">Allowed redirect destinations.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentRedirectValidationOptions AllowedDestinations(params string[] uris); |
|||
|
|||
/// <summary>
|
|||
/// Allows same host redirects to HTTPS.
|
|||
/// </summary>
|
|||
/// <param name="httpsPorts">Allowed destination port(s) for redirects to HTTPS. The default HTTPS port (443) is assumed if no values are configured.</param>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentRedirectValidationOptions AllowSameHostRedirectsToHttps(params int[] httpsPorts); |
|||
} |
|||
} |
|||
@ -1,22 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for X-Frame-Options.
|
|||
/// </summary>
|
|||
public interface IFluentXFrameOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Enables the Deny directive.
|
|||
/// </summary>
|
|||
void Deny(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the SameOrigin directive.
|
|||
/// </summary>
|
|||
void SameOrigin(); |
|||
} |
|||
} |
|||
@ -1,54 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for X-Robots-Tag.
|
|||
/// </summary>
|
|||
public interface IFluentXRobotsTagOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Enables the noindex directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoIndex(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the nofollow directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoFollow(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the nosnippet directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoSnippet(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the noarchive directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoArchive(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the noodp directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoOdp(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the notranslate directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoTranslate(); |
|||
|
|||
/// <summary>
|
|||
/// Enables the noimageindex directive.
|
|||
/// </summary>
|
|||
/// <returns>The current instance.</returns>
|
|||
IFluentXRobotsTagOptions NoImageIndex(); |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.Fluent; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
/// <summary>
|
|||
/// Fluent interface to configure options for X-Xss-Protection.
|
|||
/// </summary>
|
|||
public interface IFluentXXssProtectionOptions : IFluentInterface |
|||
{ |
|||
/// <summary>
|
|||
/// Configures the header to explicitly disable protection.
|
|||
/// </summary>
|
|||
void Disabled(); |
|||
|
|||
/// <summary>
|
|||
/// Configures the header to explicitly enable protection.
|
|||
/// </summary>
|
|||
void Enabled(); |
|||
|
|||
/// <summary>
|
|||
/// Configures the header to explicitly enable protection with block mode.
|
|||
/// </summary>
|
|||
void EnabledWithBlockMode(); |
|||
} |
|||
} |
|||
@ -1,88 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
using NWebsec.Middleware.Helpers; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class CspMiddleware |
|||
{ |
|||
private readonly ICspConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
private readonly bool _reportOnly; |
|||
private readonly RequestDelegate _next; |
|||
|
|||
public CspMiddleware(RequestDelegate next, ICspConfiguration options, bool reportOnly) |
|||
{ |
|||
_next = next; |
|||
_config = options; |
|||
_reportOnly = reportOnly; |
|||
|
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateCspResult(_config, reportOnly); |
|||
} |
|||
|
|||
public async Task Invoke(HttpContext context) |
|||
{ |
|||
|
|||
if (HandleUpgradeInsecureRequest(context)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
SetCspHeaders(context); |
|||
|
|||
if (_next != null) |
|||
{ |
|||
await _next(context); |
|||
} |
|||
|
|||
} |
|||
|
|||
internal bool HandleUpgradeInsecureRequest(HttpContext context) |
|||
{ |
|||
//Already on https.
|
|||
if (context.Request.IsHttps) return false; |
|||
|
|||
//CSP upgrade-insecure-requests is disabled
|
|||
if (!_config.Enabled || !_config.UpgradeInsecureRequestsDirective.Enabled) return false; |
|||
|
|||
if (!CspUpgradeHelper.UaSupportsUpgradeInsecureRequests(context)) return false; |
|||
|
|||
var upgradeUri = new UriBuilder($"https://{context.Request.Host}") |
|||
{ |
|||
Port = _config.UpgradeInsecureRequestsDirective.HttpsPort, |
|||
Path = context.Request.PathBase + context.Request.Path |
|||
}; |
|||
|
|||
//Redirect
|
|||
context.Response.Headers["Vary"] = "Upgrade-Insecure-Requests"; |
|||
context.Response.Headers["Location"] = upgradeUri.Uri.AbsoluteUri; |
|||
context.Response.StatusCode = 307; |
|||
return true; |
|||
} |
|||
|
|||
internal void SetCspHeaders(HttpContext context) |
|||
{ |
|||
if (_reportOnly) |
|||
{ |
|||
context.GetNWebsecContext().CspReportOnly = _config; |
|||
} |
|||
else |
|||
{ |
|||
context.GetNWebsecContext().Csp = _config; |
|||
} |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
context.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
|
|||
public class HpkpMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly IHpkpConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public HpkpMiddleware(RequestDelegate next, HpkpOptions options, bool reportOnly) |
|||
: base(next) |
|||
{ |
|||
_config = options.Config; |
|||
|
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateHpkpResult(_config, reportOnly); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext context) |
|||
{ |
|||
|
|||
if (_config.HttpsOnly && !context.Request.IsHttps) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
context.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,46 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
using NWebsec.Middleware.Helpers; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
|
|||
public class HstsMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly IHstsConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
private const string Https = "https"; |
|||
|
|||
public HstsMiddleware(RequestDelegate next, HstsOptions options) |
|||
: base(next) |
|||
{ |
|||
_config = options; |
|||
|
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateHstsResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext context) |
|||
{ |
|||
|
|||
if (_config.HttpsOnly && !context.Request.IsHttps) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (_config.UpgradeInsecureRequests && !CspUpgradeHelper.UaSupportsUpgradeInsecureRequests(context)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
context.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class MiddlewareBase |
|||
{ |
|||
private readonly RequestDelegate _next; |
|||
|
|||
public MiddlewareBase(RequestDelegate next) |
|||
{ |
|||
_next = next; |
|||
} |
|||
|
|||
public async Task Invoke(HttpContext context) |
|||
{ |
|||
|
|||
PreInvokeNext(context); |
|||
|
|||
if (_next != null) |
|||
{ |
|||
await _next(context); |
|||
} |
|||
|
|||
PostInvokeNext(context); |
|||
} |
|||
|
|||
internal virtual void PreInvokeNext(HttpContext context) |
|||
{ |
|||
} |
|||
|
|||
internal virtual void PostInvokeNext(HttpContext context) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
|
|||
public class RedirectValidationMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly RedirectValidationOptions _config; |
|||
private readonly RedirectValidator _redirectValidator; |
|||
|
|||
public RedirectValidationMiddleware(RequestDelegate next, RedirectValidationOptions options) |
|||
: base(next) |
|||
{ |
|||
_config = options; |
|||
_redirectValidator = new RedirectValidator(); |
|||
} |
|||
|
|||
internal override void PostInvokeNext(HttpContext context) |
|||
{ |
|||
var statusCode = context.Response.StatusCode; |
|||
|
|||
if (!_redirectValidator.IsRedirectStatusCode(statusCode)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var scheme = context.Request.Scheme; |
|||
var hostandport = context.Request.Host; |
|||
var requestUri = new Uri(scheme + "://" + hostandport); |
|||
|
|||
_redirectValidator.ValidateRedirect(statusCode, context.Response.Headers["Location"], requestUri, _config); |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class XContentTypeOptionsMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly ISimpleBooleanConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public XContentTypeOptionsMiddleware(RequestDelegate next) |
|||
: base(next) |
|||
{ |
|||
_config = new SimpleBooleanConfiguration { Enabled = true }; |
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateXContentTypeOptionsResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext owinEnvironment) |
|||
{ |
|||
owinEnvironment.GetNWebsecContext().XContentTypeOptions = _config; |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class XDownloadOptionsMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly ISimpleBooleanConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public XDownloadOptionsMiddleware(RequestDelegate next) |
|||
: base(next) |
|||
{ |
|||
_config = new SimpleBooleanConfiguration { Enabled = true }; |
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateXDownloadOptionsResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext owinEnvironment) |
|||
{ |
|||
owinEnvironment.GetNWebsecContext().XDownloadOptions = _config; |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
owinEnvironment.Response.Headers[_headerResult.Name]= _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,36 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
|
|||
public class XRobotsTagMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly IXRobotsTagConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public XRobotsTagMiddleware(RequestDelegate next, XRobotsTagOptions options) |
|||
: base(next) |
|||
{ |
|||
_config = options.Config; |
|||
|
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateXRobotsTagResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext owinEnvironment) |
|||
{ |
|||
owinEnvironment.GetNWebsecContext().XRobotsTag = _config; |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class XXssMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly IXXssProtectionConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public XXssMiddleware(RequestDelegate next, XXssProtectionOptions options) |
|||
: base(next) |
|||
{ |
|||
_config = options; |
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateXXssProtectionResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext owinEnvironment) |
|||
{ |
|||
owinEnvironment.GetNWebsecContext().XXssProtection = _config; |
|||
|
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using NWebsec.Core.Extensions; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware.Middleware |
|||
{ |
|||
public class XfoMiddleware : MiddlewareBase |
|||
{ |
|||
private readonly IXFrameOptionsConfiguration _config; |
|||
private readonly HeaderResult _headerResult; |
|||
|
|||
public XfoMiddleware(RequestDelegate next, XFrameOptions options) |
|||
: base(next) |
|||
{ |
|||
_config = options; |
|||
var headerGenerator = new HeaderGenerator(); |
|||
_headerResult = headerGenerator.CreateXfoResult(_config); |
|||
} |
|||
|
|||
internal override void PreInvokeNext(HttpContext owinEnvironment) |
|||
{ |
|||
owinEnvironment.GetNWebsecContext().XFrameOptions = _config; |
|||
if (_headerResult.Action == HeaderResult.ResponseAction.Set) |
|||
{ |
|||
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> |
|||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> |
|||
</PropertyGroup> |
|||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" /> |
|||
<PropertyGroup Label="Globals"> |
|||
<ProjectGuid>38c8e88f-1d01-466f-b47d-6d67f13c1594</ProjectGuid> |
|||
<RootNamespace>NWebsec.Middleware</RootNamespace> |
|||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath> |
|||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> |
|||
</PropertyGroup> |
|||
<PropertyGroup> |
|||
<SchemaVersion>2.0</SchemaVersion> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild> |
|||
</PropertyGroup> |
|||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" /> |
|||
</Project> |
|||
@ -1,28 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Core |
|||
{ |
|||
public class NWebsecContext |
|||
{ |
|||
public static readonly string ContextKey = "nwebsec.Context"; |
|||
|
|||
public ISimpleBooleanConfiguration NoCacheHeaders { get; set; } |
|||
public ISimpleBooleanConfiguration XContentTypeOptions { get; set; } |
|||
public ISimpleBooleanConfiguration XDownloadOptions { get; set; } |
|||
public IXFrameOptionsConfiguration XFrameOptions { get; set; } |
|||
public IXRobotsTagConfiguration XRobotsTag { get; set; } |
|||
public IXXssProtectionConfiguration XXssProtection { get; set; } |
|||
public ICspConfiguration Csp { get; set; } |
|||
public ICspConfiguration CspReportOnly { get; set; } |
|||
|
|||
public ConfigurationOverrides ConfigOverrides { get; set; } |
|||
} |
|||
|
|||
public class ConfigurationOverrides |
|||
{ |
|||
public ICspConfiguration CspOverride { get; set; } |
|||
public ICspConfiguration CspReportOnlyOverride { get; set; } |
|||
} |
|||
} |
|||
@ -1,61 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class RedirectValidationOptions : IRedirectValidationConfiguration, IFluentRedirectValidationOptions |
|||
{ |
|||
public RedirectValidationOptions() |
|||
{ |
|||
Enabled = true; |
|||
AllowedUris = new string[0]; |
|||
SameHostRedirectConfiguration = new SameHostHttpsRedirectConfiguration(); |
|||
} |
|||
|
|||
public bool Enabled { get; set; } |
|||
public IEnumerable<string> AllowedUris { get; set; } |
|||
public ISameHostHttpsRedirectConfiguration SameHostRedirectConfiguration { get; set; } |
|||
|
|||
public IFluentRedirectValidationOptions AllowedDestinations(params string[] uris) |
|||
{ |
|||
if (uris.Length == 0) throw new ArgumentException("You must supply at least one redirect URI."); |
|||
|
|||
var validatedUris = new List<string>(); |
|||
|
|||
foreach (var uri in uris) |
|||
{ |
|||
Uri result; |
|||
if (!Uri.TryCreate(uri, UriKind.Absolute, out result)) |
|||
{ |
|||
throw new ArgumentException("Redirect URIs must be well formed absolute URIs. Offending URI: " + uri); |
|||
} |
|||
validatedUris.Add(result.AbsoluteUri); |
|||
} |
|||
|
|||
AllowedUris = validatedUris.ToArray(); |
|||
return this; |
|||
} |
|||
|
|||
public IFluentRedirectValidationOptions AllowSameHostRedirectsToHttps(params int[] httpsPorts) |
|||
{ |
|||
var invalidPorts = httpsPorts.Where(p => p < 1 || p > 65535).ToArray(); |
|||
|
|||
if (invalidPorts.Length > 0) |
|||
{ |
|||
var ports = String.Join(" ", invalidPorts.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray()); |
|||
var invalidPortNumberMessage = "Invalid ports configured. Port number(s) must be in the range 1-65535. Offending ports: " + ports; |
|||
throw new ArgumentOutOfRangeException(invalidPortNumberMessage); |
|||
} |
|||
|
|||
SameHostRedirectConfiguration.Enabled = true; |
|||
SameHostRedirectConfiguration.Ports = httpsPorts; |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,83 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using NWebsec.Core.Exceptions; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Core |
|||
{ |
|||
public class RedirectValidator |
|||
{ |
|||
public void ValidateRedirect(int statusCode, string locationHeader, Uri requestAuthority, |
|||
IRedirectValidationConfiguration config) |
|||
{ |
|||
if (!config.Enabled) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//Not a redirect
|
|||
if (!IsRedirectStatusCode(statusCode)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//No location header
|
|||
if (String.IsNullOrEmpty(locationHeader)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Uri locationUri; |
|||
if (!Uri.TryCreate(locationHeader, UriKind.RelativeOrAbsolute, out locationUri)) |
|||
{ |
|||
throw new Exception("Unable to parse location header value as URI. Value was: " + locationHeader); |
|||
} |
|||
|
|||
//Relative Uri
|
|||
if (!locationUri.IsAbsoluteUri) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
// Same origin TODO look into URL encoding
|
|||
if (locationUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped).Equals(requestAuthority.GetComponents(UriComponents.SchemeAndServer,UriFormat.SafeUnescaped))) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
//Same host https
|
|||
if (config.SameHostRedirectConfiguration.Enabled && locationUri.Scheme.Equals("https") && requestAuthority.Host.Equals(locationUri.Host)) |
|||
{ |
|||
var sameHostConfig = config.SameHostRedirectConfiguration; |
|||
|
|||
if (sameHostConfig.Ports.Length == 0 && locationUri.IsDefaultPort) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (sameHostConfig.Ports.Contains(locationUri.Port)) |
|||
{ |
|||
return; |
|||
} |
|||
throw new RedirectValidationException("A potentially dangerous redirect was detected. Allow same host redirects to this port number in configuration if the redirect was intended. Offending redirect: " + locationHeader); |
|||
} |
|||
|
|||
// Allowed Uri
|
|||
if (config.AllowedUris.Any(locationUri.AbsoluteUri.StartsWith)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
throw new RedirectValidationException( |
|||
"A potentially dangerous redirect was detected. Add the destination to the whitelist in configuration if the redirect was intended. Offending redirect: " + |
|||
locationHeader); |
|||
} |
|||
|
|||
public bool IsRedirectStatusCode(int statusCode) |
|||
{ |
|||
return statusCode >= 300 && statusCode < 400 && statusCode != 304; |
|||
} |
|||
} |
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class XFrameOptions : IXFrameOptionsConfiguration, IFluentXFrameOptions |
|||
{ |
|||
internal XFrameOptions() |
|||
{ |
|||
Policy = XfoPolicy.Disabled; |
|||
} |
|||
|
|||
public XfoPolicy Policy { get; set; } |
|||
|
|||
public void Deny() |
|||
{ |
|||
Policy = XfoPolicy.Deny; |
|||
} |
|||
|
|||
public void SameOrigin() |
|||
{ |
|||
Policy = XfoPolicy.SameOrigin; |
|||
} |
|||
} |
|||
} |
|||
@ -1,58 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class XRobotsTagOptions : IFluentXRobotsTagOptions |
|||
{ |
|||
internal XRobotsTagOptions() |
|||
{ |
|||
Config = new XRobotsTagConfiguration(); |
|||
} |
|||
|
|||
internal XRobotsTagConfiguration Config { get; private set; } |
|||
|
|||
public IFluentXRobotsTagOptions NoIndex() |
|||
{ |
|||
Config.NoIndex = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoFollow() |
|||
{ |
|||
Config.NoFollow = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoSnippet() |
|||
{ |
|||
Config.NoSnippet = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoArchive() |
|||
{ |
|||
Config.NoArchive = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoOdp() |
|||
{ |
|||
Config.NoOdp = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoTranslate() |
|||
{ |
|||
Config.NoTranslate = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
|
|||
public IFluentXRobotsTagOptions NoImageIndex() |
|||
{ |
|||
Config.NoImageIndex = Config.Enabled = true; |
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
|
|||
|
|||
using System.ComponentModel; |
|||
using NWebsec.Core.HttpHeaders; |
|||
using NWebsec.Core.HttpHeaders.Configuration; |
|||
|
|||
namespace NWebsec.Middleware |
|||
{ |
|||
public class XXssProtectionOptions : IXXssProtectionConfiguration, IFluentXXssProtectionOptions |
|||
{ |
|||
internal XXssProtectionOptions() |
|||
{ |
|||
Policy = XXssPolicy.Disabled; |
|||
} |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public XXssPolicy Policy { get; set; } |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public bool BlockMode { get; set; } |
|||
|
|||
public void Disabled() |
|||
{ |
|||
Policy = XXssPolicy.FilterDisabled; |
|||
} |
|||
|
|||
public void Enabled() |
|||
{ |
|||
Policy = XXssPolicy.FilterEnabled; |
|||
} |
|||
|
|||
public void EnabledWithBlockMode() |
|||
{ |
|||
Policy = XXssPolicy.FilterEnabled; |
|||
BlockMode = true; |
|||
} |
|||
} |
|||
} |
|||
@ -1,39 +0,0 @@ |
|||
{ |
|||
"version": "1.0.0-internal-*", |
|||
|
|||
"description": "NWebsec middleware for ASP.NET 5 applications. NWebsec helps you set important security headers and detect potentially dangerous redirects. See project website for documentation.", |
|||
"authors": [ "André N. Klingsheim" ], |
|||
|
|||
"packOptions": { |
|||
"title": "NWebsec.Middleware (ASP.NET 5)", |
|||
"releaseNotes": "This release includes core functions for the NWebsec security libaries.", |
|||
"owners": [ "André N. Klingsheim" ], |
|||
"copyright": "Copyright © 2014 - 2015", |
|||
"tags": [ "NWebsec Security AspNet AppSec" ], |
|||
"projectUrl": "https://docs.nwebsec.com/en/latest/", |
|||
"licenseUrl": "https://github.com/NWebsec/NWebsec/blob/master/LICENSE", |
|||
"iconUrl": "https://klings.blob.core.windows.net/nwebsecicon/nwebsec_nuget.png", |
|||
}, |
|||
|
|||
"shared": "**/**.cs", |
|||
|
|||
"frameworks": { |
|||
"net451": { }, |
|||
|
|||
"netstandard1.3": { |
|||
"dependencies": { |
|||
"Microsoft.CSharp": "4.0.1-rc2-24027", |
|||
"System.Runtime": "4.1.0-rc2-24027" |
|||
}, |
|||
|
|||
"imports": [ |
|||
"dotnet5.4", |
|||
"portable-net451+win8" |
|||
] |
|||
} |
|||
}, |
|||
|
|||
"dependencies": { |
|||
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final" |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue