diff --git a/src/Constants.cs b/src/Constants.cs
index 47e1f7e..814a154 100644
--- a/src/Constants.cs
+++ b/src/Constants.cs
@@ -9,6 +9,7 @@ public static class Constants
public const string XssProtectionHeaderName = "X-XSS-Protection";
public const string XContentTypeOptionsHeaderName = "X-Content-Type-Options";
+ public const string XContentTypeOptionsValue = "nosniff";
public const string ContentSecurityPolicyHeaderName = "Content-Security-Policy";
diff --git a/src/OwaspHeaders.Core.csproj b/src/OwaspHeaders.Core.csproj
index f91b1aa..2f13e2a 100644
--- a/src/OwaspHeaders.Core.csproj
+++ b/src/OwaspHeaders.Core.csproj
@@ -8,7 +8,7 @@
OwaspHeaders.Core
- 9.5.0
+ 9.6.0
Jamie Taylor
RJJ Software Ltd
MIT
diff --git a/src/SecureHeadersMiddleware.cs b/src/SecureHeadersMiddleware.cs
index 9e8a5f5..e251312 100644
--- a/src/SecureHeadersMiddleware.cs
+++ b/src/SecureHeadersMiddleware.cs
@@ -7,9 +7,19 @@ namespace OwaspHeaders.Core;
/// A middleware for injecting OWASP recommended headers into a
/// HTTP Request
///
-public class SecureHeadersMiddleware(RequestDelegate next, SecureHeadersMiddlewareConfiguration config)
+public class SecureHeadersMiddleware()
{
- private string _calculatedContentSecurityPolicy;
+private string _calculatedContentSecurityPolicy;
+private readonly Dictionary _headers;
+private readonly RequestDelegate _next;
+private readonly SecureHeadersMiddlewareConfiguration _config;
+
+public SecureHeadersMiddleware(RequestDelegate next, SecureHeadersMiddlewareConfiguration config) : this()
+{
+ _config = config;
+ _next = next;
+ _headers = new Dictionary();
+}
///
/// The main task of the middleware. This will be invoked whenever
@@ -19,106 +29,121 @@ public class SecureHeadersMiddleware(RequestDelegate next, SecureHeadersMiddlewa
///
public async Task InvokeAsync(HttpContext httpContext)
{
- if (config == null)
+ if (_config == null)
{
- throw new ArgumentException($"Expected an instance of the {nameof(SecureHeadersMiddlewareConfiguration)} object.");
+ throw new ArgumentException(
+ $"Expected an instance of the {nameof(SecureHeadersMiddlewareConfiguration)} object.");
}
if (!RequestShouldBeIgnored(httpContext.Request.Path))
{
- if (config.UseHsts)
+ if (_headers.Count == 0)
{
- httpContext.TryAddHeader(Constants.StrictTransportSecurityHeaderName,
- config.HstsConfiguration.BuildHeaderValue());
+ GenerateRelevantHeaders();
}
- if (config.UseXFrameOptions)
+ foreach (var (key, value) in _headers)
{
- httpContext.TryAddHeader(Constants.XFrameOptionsHeaderName,
- config.XFrameOptionsConfiguration.BuildHeaderValue());
+ httpContext.TryAddHeader(key, value);
}
+ }
- if (config.UseXssProtection)
- {
- httpContext.TryAddHeader(Constants.XssProtectionHeaderName,
- config.XssConfiguration.BuildHeaderValue());
- }
+ // Call the next middleware in the chain
+ await _next(httpContext);
+}
- if (config.UseXContentTypeOptions)
- {
- httpContext.TryAddHeader(Constants.XContentTypeOptionsHeaderName, "nosniff");
- }
+private void GenerateRelevantHeaders()
+{
+ if (_config.UseHsts)
+ {
+ _headers.TryAdd(Constants.StrictTransportSecurityHeaderName,
+ _config.HstsConfiguration.BuildHeaderValue());
+ }
- if (config.UseContentSecurityPolicyReportOnly)
- {
- if (string.IsNullOrWhiteSpace(_calculatedContentSecurityPolicy))
- {
- _calculatedContentSecurityPolicy =
- config.ContentSecurityPolicyReportOnlyConfiguration.BuildHeaderValue();
- }
-
- httpContext.TryAddHeader(Constants.ContentSecurityPolicyReportOnlyHeaderName,
- _calculatedContentSecurityPolicy);
- }
- else if (config.UseContentSecurityPolicy)
- {
- if (string.IsNullOrWhiteSpace(_calculatedContentSecurityPolicy))
- {
- _calculatedContentSecurityPolicy = config.ContentSecurityPolicyConfiguration.BuildHeaderValue();
- }
+ if (_config.UseXFrameOptions)
+ {
+ _headers.TryAdd(Constants.XFrameOptionsHeaderName,
+ _config.XFrameOptionsConfiguration.BuildHeaderValue());
+ }
- httpContext.TryAddHeader(Constants.ContentSecurityPolicyHeaderName,
- _calculatedContentSecurityPolicy);
- }
+ if (_config.UseXssProtection)
+ {
+ _headers.TryAdd(Constants.XssProtectionHeaderName,
+ _config.XssConfiguration.BuildHeaderValue());
+ }
- if (config.UseXContentSecurityPolicy)
- {
- httpContext.TryAddHeader(Constants.XContentSecurityPolicyHeaderName,
- config.ContentSecurityPolicyConfiguration.BuildHeaderValue());
- }
+ if (_config.UseXContentTypeOptions)
+ {
+ _headers.TryAdd(Constants.XContentTypeOptionsHeaderName, Constants.XContentTypeOptionsValue);
+ }
- if (config.UsePermittedCrossDomainPolicy)
+ if (_config.UseContentSecurityPolicyReportOnly)
+ {
+ if (string.IsNullOrWhiteSpace(_calculatedContentSecurityPolicy))
{
- httpContext.TryAddHeader(Constants.PermittedCrossDomainPoliciesHeaderName,
- config.PermittedCrossDomainPolicyConfiguration.BuildHeaderValue());
+ _calculatedContentSecurityPolicy =
+ _config.ContentSecurityPolicyReportOnlyConfiguration.BuildHeaderValue();
}
- if (config.UseReferrerPolicy)
+ _headers.TryAdd(Constants.ContentSecurityPolicyReportOnlyHeaderName,
+ _calculatedContentSecurityPolicy);
+ }
+ else if (_config.UseContentSecurityPolicy)
+ {
+ if (string.IsNullOrWhiteSpace(_calculatedContentSecurityPolicy))
{
- httpContext.TryAddHeader(Constants.ReferrerPolicyHeaderName,
- config.ReferrerPolicy.BuildHeaderValue());
+ _calculatedContentSecurityPolicy = _config.ContentSecurityPolicyConfiguration.BuildHeaderValue();
}
- if (config.UseExpectCt)
- {
- httpContext.TryAddHeader(Constants.ExpectCtHeaderName,
- config.ExpectCt.BuildHeaderValue());
- }
+ _headers.TryAdd(Constants.ContentSecurityPolicyHeaderName,
+ _calculatedContentSecurityPolicy);
+ }
- if (config.UseCacheControl)
- {
- httpContext.TryAddHeader(Constants.CacheControlHeaderName,
- config.CacheControl.BuildHeaderValue());
- }
+ if (_config.UseXContentSecurityPolicy)
+ {
+ _headers.TryAdd(Constants.XContentSecurityPolicyHeaderName,
+ _config.ContentSecurityPolicyConfiguration.BuildHeaderValue());
+ }
- if (config.UseCrossOriginResourcePolicy)
- {
- httpContext.TryAddHeader(Constants.CrossOriginResourcePolicyHeaderName,
- config.CrossOriginResourcePolicy.BuildHeaderValue());
- }
+ if (_config.UsePermittedCrossDomainPolicy)
+ {
+ _headers.TryAdd(Constants.PermittedCrossDomainPoliciesHeaderName,
+ _config.PermittedCrossDomainPolicyConfiguration.BuildHeaderValue());
}
- // Call the next middleware in the chain
- await next(httpContext);
+ if (_config.UseReferrerPolicy)
+ {
+ _headers.TryAdd(Constants.ReferrerPolicyHeaderName,
+ _config.ReferrerPolicy.BuildHeaderValue());
+ }
+
+ if (_config.UseExpectCt)
+ {
+ _headers.TryAdd(Constants.ExpectCtHeaderName,
+ _config.ExpectCt.BuildHeaderValue());
+ }
+
+ if (_config.UseCacheControl)
+ {
+ _headers.TryAdd(Constants.CacheControlHeaderName,
+ _config.CacheControl.BuildHeaderValue());
+ }
+
+ if (_config.UseCrossOriginResourcePolicy)
+ {
+ _headers.TryAdd(Constants.CrossOriginResourcePolicyHeaderName,
+ _config.CrossOriginResourcePolicy.BuildHeaderValue());
+ }
}
private bool RequestShouldBeIgnored(PathString requestedPath)
{
- if (config.UrlsToIgnore.Count == 0)
+ if (_config.UrlsToIgnore.Count == 0)
{
return false;
}
- return requestedPath.HasValue && config.UrlsToIgnore.Any(url => url.Equals(requestedPath.Value!, StringComparison.InvariantCulture));
+ return requestedPath.HasValue &&
+ _config.UrlsToIgnore.Any(url => url.Equals(requestedPath.Value!, StringComparison.InvariantCulture));
}
}