Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying different properties for endpoints #172

Merged
merged 8 commits into from
Aug 31, 2024

Conversation

andrewlock
Copy link
Owner

Initial attempt at having different policies for multiple endpoints.

I'm not entirely sure about this at the moment, I feel like it's a bit confusing having to call UseSecurityHeaders() multiple times (to make sure you have the "default" headers set).

Another option would be to use an IStartupFilter to auto-add the default middleware to the start of the pipeline using the services, but then that could be confusing, as it would differ from the "simple" approach...

Would be interested in feedback/suggestions...

Fixes #55
Fixes #87

The Readme looks something like this:

Applying different headers to different endpoints

In some situations, you may need to apply different security headers to different endpoints. For example, you may want to have a very restrictive Content-Security-Policy by default, but then have a more relaxed on specific endpoints that require it. This is supported, but requires more configuration.

1. Configure your policies using AddSecurityHeaderPolicies()

You can configure named and default policies by calling AddSecurityHeaderPolicies() on IServiceCollection. You can configure the default policy to use, as well as any named policies. For example, the following configures the default policy (used when UseSecurityHeaders() is called without any arguments), and a named policy:

var builder = WebApplication.CreateBuilder();

// 👇 Call AddSecurityHeaderPolicies()
builder.Services.AddSecurityHeaderPolicies()
    .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders()) // 👈 Configure the default policy
    .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue")); // 👈 Configure named policies

2. Add the default middleware early to the pipeline

The security headers middleware can only add headers to all requests if it is early in the middleware pipeline, so it's important to add the headders middleware at the start of your middleware pipeline. However, if you want to have endpoint-specific policies, then you also need to place the middleware after the call to UseRouting(), as that is the point at which the endpoint that will be executed is selected.

var builder = WebApplication.CreateBuilder();

builder.Services.AddSecurityHeaderPolicies()
    .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders())
    .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue"));

var app = builder.Build();

// Set the default headers for requests that
// 👇 don't make it to the routing middleware
app.UseSecurityHeaders();

app.UseStaticFiles(); // other middleware
app.UseAuthentication();
app.UseRouting(); 

app.UseSecurityHeaders(); // 👈 Add after the routing middleware 
app.UseAuthorization();

app.MapGet("/", () => "Hello world");
app.Run();

Note that if you pass a policy to any call to UseSecurityHeaders() it will override the "default" policy used at that point.

3. Apply custom policies to endpoints

To apply a non-default policy to an endpoint, use the WithSecurityHeadersPolicy(policy) endpoint extension method, and pass in the name of the policy to apply:

var builder = WebApplication.CreateBuilder();

builder.Services.AddSecurityHeaderPolicies()
    .SetDefaultPolicy(policy => policy.AddDefaultSecurityHeaders())
    .AddPolicy("CustomPolicy", policy => policy.AddCustomHeader("X-Custom", "SomeValue"));

var app = builder.Build();

app.UseSecurityHeaders();

app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting(); 

app.UseSecurityHeaders(); 
app.UseAuthorization();

app.MapGet("/", () => "Hello world")
    .WithSecurityHeadersPolicy("CustomPolicy"); // 👈 Apply a named policy to the endpoint 
app.Run();

If you're using MVC controllers or Razor Pages, you can apply the [SecurityHeadersPolicy(policyName)] attribute to your endpoints:

public class HomeController : ControllerBase
{
    [SecurityHeadersPolicy("CustomHeader")] // 👈 Apply a custom header to the endpoint
    public IActionResult Index()
    {
        return View();
    }
}

Each call to UseSecurityHeaders() will re-evaluate the applicable policies; the headers are applied just before the response is sent. The policy to apply is determined as follows, with the first applicable policy selected.

  1. If an endpoint has been selected, and a named policy is applied, use that.
  2. If a named or policy instance is passed to the SecurityHeadersMiddleware, use that.
  3. If the default policy has been set using SetDefaultPolicy(), use that.
  4. Otherwise, apply the default headers (those added by AddDefaultSecurityHeaders())

@andrewlock andrewlock merged commit b0c49e1 into major_v_bump Aug 31, 2024
3 checks passed
@andrewlock andrewlock deleted the multiple-policies branch August 31, 2024 14:22
andrewlock added a commit that referenced this pull request Sep 6, 2024
* nit: Remove dead code

* Remove unused package references

* Remove ICustomHeaderService and make header service static

* Add support for adding multiple named policies, and referencing them in the middleware

* Add support for specifying a different header policy for a given endpoint

* Allow configuring the default policy in the services

* Add ReadMe

* Add support for MVC attributes with named policies
andrewlock added a commit that referenced this pull request Sep 15, 2024
* nit: Remove dead code

* Remove unused package references

* Remove ICustomHeaderService and make header service static

* Add support for adding multiple named policies, and referencing them in the middleware

* Add support for specifying a different header policy for a given endpoint

* Allow configuring the default policy in the services

* Add ReadMe

* Add support for MVC attributes with named policies
andrewlock added a commit that referenced this pull request Sep 25, 2024
* nit: Remove dead code

* Remove unused package references

* Remove ICustomHeaderService and make header service static

* Add support for adding multiple named policies, and referencing them in the middleware

* Add support for specifying a different header policy for a given endpoint

* Allow configuring the default policy in the services

* Add ReadMe

* Add support for MVC attributes with named policies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant