Add custom security header support to security header middleware

pull/16330/head
Engincan VESKE 3 years ago
parent eccbfbbb8f
commit 2a0dab9a2e

@ -20,27 +20,38 @@ public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
/*X-Content-Type-Options header tells the browser to not try and “guess” what a mimetype of a resource might be, and to just take what mimetype the server has returned as fact.*/
AddHeaderIfNotExists(context, "X-Content-Type-Options", "nosniff");
AddHeader(context, "X-Content-Type-Options", "nosniff");
/*X-XSS-Protection is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks*/
AddHeaderIfNotExists(context, "X-XSS-Protection", "1; mode=block");
AddHeader(context, "X-XSS-Protection", "1; mode=block");
/*The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame>, <iframe> or <object>. SAMEORIGIN makes it being displayed in a frame on the same origin as the page itself. The spec leaves it up to browser vendors to decide whether this option applies to the top level, the parent, or the whole chain*/
AddHeaderIfNotExists(context, "X-Frame-Options", "SAMEORIGIN");
AddHeader(context, "X-Frame-Options", "SAMEORIGIN");
if (Options.Value.UseContentSecurityPolicyHeader)
{
AddHeaderIfNotExists(context, "Content-Security-Policy",
AddHeader(context, "Content-Security-Policy",
Options.Value.ContentSecurityPolicyValue.IsNullOrEmpty()
? "object-src 'none'; form-action 'self'; frame-ancestors 'none'"
: Options.Value.ContentSecurityPolicyValue);
}
foreach (var (key, value) in Options.Value.Headers)
{
AddHeader(context, key, value, true);
}
await next.Invoke(context);
}
protected virtual void AddHeaderIfNotExists(HttpContext context, string key, string value)
protected virtual void AddHeader(HttpContext context, string key, string value, bool overrideIfExists = false)
{
if (overrideIfExists && context.Response.Headers.TryGetValue(key, out _))
{
context.Response.Headers[key] = value;
return;
}
context.Response.Headers.AddIfNotContains(new KeyValuePair<string, StringValues>(key, value));
}
}

@ -1,3 +1,5 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Security;
public class AbpSecurityHeadersOptions
@ -5,4 +7,11 @@ public class AbpSecurityHeadersOptions
public bool UseContentSecurityPolicyHeader { get; set; }
public string ContentSecurityPolicyValue { get; set; }
public Dictionary<string, string> Headers { get; }
public AbpSecurityHeadersOptions()
{
Headers = new Dictionary<string, string>();
}
}

@ -15,6 +15,7 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase
services.Configure<AbpSecurityHeadersOptions>(options =>
{
options.UseContentSecurityPolicyHeader = true;
options.Headers["Referrer-Policy"] = "no-referrer";
});
base.ConfigureServices(context, services);
@ -30,4 +31,12 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase
responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff");
responseMessage.Headers.ShouldContain(x => x.Key == "Content-Security-Policy" & x.Value.First().ToString() == "object-src 'none'; form-action 'self'; frame-ancestors 'none'");
}
[Fact]
public async Task SecurityHeaders_Custom_Headers_Should_Be_Added()
{
var responseMessage = await GetResponseAsync("/SecurityHeadersTest/Get");
responseMessage.Headers.ShouldNotBeEmpty();
responseMessage.Headers.ShouldContain(x => x.Key == "Referrer-Policy" && x.Value.First().ToString() == "no-referrer");
}
}

Loading…
Cancel
Save