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