{"id":18721642,"url":"https://github.com/andrewlock/netescapades.aspnetcore.securityheaders","last_synced_at":"2025-05-13T21:08:34.776Z","repository":{"id":41125080,"uuid":"59352868","full_name":"andrewlock/NetEscapades.AspNetCore.SecurityHeaders","owner":"andrewlock","description":"Small package to allow adding security headers to ASP.NET Core websites","archived":false,"fork":false,"pushed_at":"2025-05-07T20:57:41.000Z","size":713,"stargazers_count":754,"open_issues_count":0,"forks_count":82,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-05-07T21:15:30.218Z","etag":null,"topics":["hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrewlock.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-05-21T09:48:56.000Z","updated_at":"2025-05-07T20:42:51.000Z","dependencies_parsed_at":"2024-06-21T16:50:05.739Z","dependency_job_id":"ba4c94af-d27d-4995-a44d-d3f4f83590bf","html_url":"https://github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders","commit_stats":{"total_commits":185,"total_committers":15,"mean_commits":"12.333333333333334","dds":"0.30810810810810807","last_synced_commit":"e0909205eaf14dd749ff78fec768960e32f909ae"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewlock%2FNetEscapades.AspNetCore.SecurityHeaders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewlock%2FNetEscapades.AspNetCore.SecurityHeaders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewlock%2FNetEscapades.AspNetCore.SecurityHeaders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewlock%2FNetEscapades.AspNetCore.SecurityHeaders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewlock","download_url":"https://codeload.github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254028991,"owners_count":22002283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest"],"created_at":"2024-11-07T13:35:57.015Z","updated_at":"2025-05-13T21:08:29.745Z","avatar_url":"https://github.com/andrewlock.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![](https://raw.githubusercontent.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders/refs/heads/main/icon_32.png) NetEscapades.AspNetCore.SecurityHeaders\n\n![GitHub branch check runs](https://img.shields.io/github/check-runs/andrewlock/NetEscapades.AspNetCore.SecurityHeaders/main)\n[![NuGet](https://img.shields.io/nuget/v/NetEscapades.AspNetCore.SecurityHeaders.svg)](https://www.nuget.org/packages/NetEscapades.AspNetCore.SecurityHeaders/)\n![NuGet Downloads](https://img.shields.io/nuget/dt/NetEscapades.AspNetCore.SecurityHeaders)\n\n\nA small package to allow adding security headers to ASP.NET Core websites\n\n## Installing\n\nInstall using the [NetEscapades.AspNetCore.SecurityHeaders NuGet package](https://www.nuget.org/packages/NetEscapades.AspNetCore.SecurityHeaders) from the Visual Studio Package Manager Console:\n\n```\nPM\u003e Install-Package NetEscapades.AspNetCore.SecurityHeaders\n```\n\nOr using the `dotnet` CLI\n\n```bash\ndotnet add package NetEscapades.AspNetCore.SecurityHeaders --version 1.0.0\n```\n\n## Usage\n\nWhen you install the package, it should be added to your `.csproj`. Alternatively, you can add it directly by adding:\n\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk.Web\"\u003e\n\n  \u003cPropertyGroup\u003e\n    \u003cTargetFramework\u003enetcoreapp5.0\u003c/TargetFramework\u003e\n  \u003c/PropertyGroup\u003e\n\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"NetEscapades.AspNetCore.SecurityHeaders\" Version=\"1.0.0\" /\u003e\n  \u003c/ItemGroup\u003e\n  \n\u003c/Project\u003e\n```\n\nThere are various ways to configure the headers for your application. \n\nIn the simplest scenario, add the middleware to your ASP.NET Core application by configuring it as part of your normal `Startup` pipeline (or `WebApplication` in .NET 6+).\n\n\u003e Note that the order of middleware matters, so to apply the headers to all requests it should be configured first in your pipeline.\n\nTo use the default security headers for your application, add the middleware using:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    app.UseSecurityHeaders();\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\nThis adds the following headers to all responses that pass through the middleware:\n\n* `X-Content-Type-Options: nosniff`\n* `Strict-Transport-Security: max-age=31536000;` - _only applied to HTTPS responses_\n* `X-Frame-Options: Deny`\n* `Referrer-Policy: strict-origin-when-cross-origin`\n* `Content-Security-Policy: object-src 'none'; form-action 'self'; frame-ancestors 'none'`\n* `Cross-Origin-Opener-Policy: same-origin`\n* `Cross-Origin-Embedder-Policy: credentialless`\n* `Cross-Origin-Resource-Policy: same-site`\n\nNote that these policies represent a \"safe\" set of minimum defaults that should be valid for most sites, but are not the most secure they could be. You are advised to think about what features you need, and to restrict them where possible. For example, a stronger [Content Security Policy](#addcontentsecuritypolicy) should be used where possible, as well as a [Permissions Policy](#addpermissionspolicy). \n\n## Customising the security headers added to responses\n\nTo customise the headers returned, you should create an instance of a `HeaderPolicyCollection` and add the required policies to it. There are helper methods for adding a number of security-focused header values to the collection, or you can alternatively add any header by using the `CustomHeader` type. For example, the following would set a number of security headers, and a custom header `X-My-Test-Header`. \n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddFrameOptionsDeny()\n        .AddContentTypeOptionsNoSniff()\n        .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365) // maxage = one year in seconds\n        .AddReferrerPolicyStrictOriginWhenCrossOrigin()\n        .RemoveServerHeader()\n        .AddContentSecurityPolicy(builder =\u003e\n        {\n            builder.AddObjectSrc().None();\n            builder.AddFormAction().Self();\n            builder.AddFrameAncestors().None();\n        })\n        .AddCrossOriginOpenerPolicy(x =\u003e x.SameOrigin());\n        .AddCustomHeader(\"X-My-Test-Header\", \"Header value\");\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\nThe security headers above are also encapsulated in another extension method, so you could rewrite it more tersely using \n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddDefaultSecurityHeaders()\n        .AddCustomHeader(\"X-My-Test-Header\", \"Header value\");\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\nIf you want to use the default security headers, but change one specific header, you can simply add another header to the default collection. For example, the following uses the default headers, but changes the max-age on the `Strict-Transport-Security` header:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddDefaultSecurityHeaders()\n        .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 63072000);\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\nThere is also a convenience overload for `UseSecurityHeaders` that takes an `Action\u003cHeaderPolicyCollection\u003e`, instead of requiring you to instantiate a `HeaderPolicyCollection` yourself:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    app.UseSecurityHeaders(policies =\u003e\n        policies\n            .AddDefaultSecurityHeaders()\n            .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 63072000)\n    );\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\n## Applying different headers to different endpoints\n\nIn 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.\n\n### 1. Configure your policies using `AddSecurityHeaderPolicies()`\n\nYou 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 for all requests that are not customised for an endpoint), and a named policy:\n\n```csharp\nvar builder = WebApplication.CreateBuilder();\n\n// 👇 Call AddSecurityHeaderPolicies()\nbuilder.Services.AddSecurityHeaderPolicies()\n    .SetDefaultPolicy(policy =\u003e policy.AddDefaultSecurityHeaders()) // 👈 Configure the default policy\n    .AddPolicy(\"CustomPolicy\", policy =\u003e policy.AddCustomHeader(\"X-Custom\", \"SomeValue\")); // 👈 Configure named policies\n\n```\n\n### 2. Call `UseSecurityHeaders()` early in the middleware pipeline\n\nThe 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 headers middleware at the start of your middleware pipeline by calling `UseSecurityHeaders()`. For example:\n\n```csharp\nvar builder = WebApplication.CreateBuilder();\n\n// 👇 Configure policies as shown previously\nbuilder.Services.AddSecurityHeaderPolicies()\n    .SetDefaultPolicy(policy =\u003e policy.AddDefaultSecurityHeaders())\n    .AddPolicy(\"CustomPolicy\", policy =\u003e policy.AddCustomHeader(\"X-Custom\", \"SomeValue\"));\n\nvar app = builder.Build();\n\n// Add the middleware to the start of your pipeline\n// 👇\napp.UseSecurityHeaders();\n\napp.UseStaticFiles(); // other middleware\napp.UseAuthentication();\napp.UseRouting(); \n\napp.UseAuthorization();\n\napp.MapGet(\"/\", () =\u003e \"Hello world\");\napp.Run();\n```\n\n### 3. Apply custom policies to endpoints\n\nTo 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:\n\n```csharp\nvar builder = WebApplication.CreateBuilder();\n\nbuilder.Services.AddSecurityHeaderPolicies()\n    .SetDefaultPolicy(policy =\u003e policy.AddDefaultSecurityHeaders())\n    .AddPolicy(\"CustomPolicy\", policy =\u003e policy.AddCustomHeader(\"X-Custom\", \"SomeValue\"));\n\nvar app = builder.Build();\n\napp.UseSecurityHeaders();\n\napp.UseStaticFiles();\napp.UseAuthentication();\napp.UseRouting(); \n\napp.UseEndpointSecurityHeaders(); \napp.UseAuthorization();\n\napp.MapGet(\"/\", () =\u003e \"Hello world\")\n    .WithSecurityHeadersPolicy(\"CustomPolicy\"); // 👈 Apply a named policy to the endpoint \napp.Run();\n```\n\nIf you're using MVC controllers or Razor Pages, you can apply the `[SecurityHeadersPolicy(policyName)]` attribute to your endpoints:\n\n```csharp\npublic class HomeController : ControllerBase\n{\n    [SecurityHeadersPolicy(\"CustomHeader\")] // 👈 Apply a named policy to the endpoint\n    public IActionResult Index()\n    {\n        return View();\n    }\n}\n```\n\nSecurity headers are applied just before the response is sent. If you use the configuration described above, then the policy to apply is determined as follows, with the first applicable policy selected:\n\n1. If an endpoint has been selected, and a named policy is applied, use that.\n2. If a named or policy instance is passed to the `SecurityHeadersMiddleware()`, use that.\n3. If the default policy has been set using `SetDefaultPolicy()`, use that.\n4. Otherwise, apply the default headers (those added by `AddDefaultSecurityHeaders()`)\n\n## Customizing the headers per request\n\nIf you need to use a different set of security headers for certain endpoints in your application, then configuring named policies  [as described above](#applying-different-headers-to-different-endpoints) is the best approach.\n\nHowever, sometimes you need even more customization on a per-request basis. For example, perhaps you have a multi-tenant application, and you need to apply different headers based on a header in the request (or the response) that identifies the tenant. In this situation, you don't know at application startup which set of headers to apply. \n\nTo customize the final `HeaderPolicyCollection` used for a request, you can use the `SetPolicySelector()` method available on `IServiceCollection.AddSecurityHeaderPolicies()`. This method take a `Func\u003c\u003e` argument which is passed a context object, and must return an `IReadOnlyHeaderPolicyCollection`. The `SetPolicySelector()` argument is invoked for every request, just before the final selected policy is applied, and allows you to change the `IReadOnlyHeaderPolicyCollection` to apply.\n\nThe following code shows how to use a dependency injected service in combination with the `SetPolicySelector()` method. This isn't necessary, but rather shows that you can completely customise the applied headers in any way you need.\n\n```csharp\nvar builder = WebApplication.CreateBuilder();\n\n// 👇 a custom service that selects the policy based on tenants\nbuilder.Services.AddScoped\u003cTenantHeaderPolicyCollectionSelector\u003e(); \nbuilder.Services.AddSecurityHeaderPolicies()\n    .AddPolicy(policyName, p =\u003e p.AddCustomHeader(\"Custom-Header\", \"MyValue\"))\n    .SetPolicySelector((PolicySelectorContext ctx) =\u003e\n    {\n        // Use services from the DI container (if you need to)\n        IServiceProvider services = ctx.HttpContext.RequestServices; \n        var selector = services.GetService\u003cTenantHeaderPolicyCollectionSelector\u003e();\n        var tenant = services.GetService\u003cITenant\u003e();\n        return selector.GetPolicy(tenant);\n    });\n```\n\nNote that you should avoid creating a `HeaderPolicyCollection` from scratch on each request. Instead, cache policies for multiple requests where possible. However, if you need to build a new policy based on the policy passed in the context object, you can create a mutable copy by calling `IReadOnlyHeaderPolicyCollection.Copy()`, adding/updating policies as required, and returning the `HeaderPolicyCollection`. \n\n## Applying different headers to JSON API endpoints\n\nAs described in the [OWASP guidance](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers) about security headers, APIs that return only JSON do not require all the security headers added by `AddDefaultSecurityHeaders()`. If your API may return HTML or content other than JSON, it may be safest to use the default security headers nonetheless. If not, you can use  the `AddDefaultApiSecurityHeaders()` method to apply a subset of headers. This method sets the following headers:\n\n* `X-Content-Type-Options: nosniff`\n* `Strict-Transport-Security: max-age=31536000;` - _only applied to HTTPS responses_\n* `X-Frame-Options: Deny`\n* `Content-Security-Policy: default-src: none; frame-ancestors 'none'`\n* `Referrer-Policy: no-referrer`\n* `Permissions-Policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()`\n* `Cross-Origin-Opener-Policy: same-origin`\n* `Cross-Origin-Embedder-Policy: require-corp`\n* `Cross-Origin-Resource-Policy: same-site`\n\nApply it in the same way to your header policy collection:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddDefaultApiSecurityHeaders();\n\n    app.UseSecurityHeaders(policyCollection);\n}\n```\n\nthis is equivalent to \n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddFrameOptionsDeny()\n        .AddContentTypeOptionsNoSniff()\n        .AddStrictTransportSecurityMaxAge()\n        .RemoveServerHeader()\n        .AddContentSecurityPolicy(builder =\u003e\n        {\n            builder.AddDefaultSrc().None();\n            builder.AddFrameAncestors().None();\n        })\n        .AddReferrerPolicyNoReferrer()\n        .AddPermissionsPolicyWithDefaultSecureDirectives();\n\n    app.UseSecurityHeaders(policyCollection);\n}\n```\n\n## RemoveServerHeader\n\nOne point to be aware of is that the `RemoveServerHeader` method will rarely (ever?) be sufficient to remove the `Server` header from your output. If any subsequent middleware in your application pipeline add the header, then this will be able to remove it. However Kestrel will generally add the `Server` header too late in the pipeline to be able to modify it.\n\nLuckily, Kestrel exposes it's own mechanism to allow you to prevent it being added:\n\n```csharp\nvar host = new WebHostBuilder()\n    .UseKestrel(options =\u003e options.AddServerHeader = false)\n    //...\n```\n\nIn `Program.cs`, when constructing your app's `WebHostBuilder`, configure the `KestrelServerOptions` to prevent the `Server` tag being added.\n\n## AddContentSecurityPolicy\n\nThe [`Content-Security-Policy` (CSP) header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is a very powerful header that can protect your website from a wide range of attacks. However, it's also totally possible to create a CSP header that completely breaks your app. \n\nThe CSP has a dizzying array of options, only some of which are implemented in this project. Consequently, I highly recommend reading [this post by Scott Helme](https://scotthelme.co.uk/content-security-policy-an-introduction/), in which he discusses the impact of each \"directive\". I also highly recommend using the \"report only\" version of the header when you start. This won't break your site, but will report instances that it would be broken, by providing reports to a service such as report-uri.com.\n\nSet the header to report-only by using the `AddContentSecurityPolicyReportOnly()` extension. For example:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddContentSecurityPolicyReportOnly(builder =\u003e // report-only\n        {\n            // configure policies\n        });\n}\n```\n\nor by passing `true` to the `AddContentSecurityPolicy` command\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddContentSecurityPolicy(builder =\u003e\n        {\n            // configure policies\n        },\n        asReportOnly: true); // report-only\n}\n```\n\nYou configure your CSP policy when you configure your `HeaderPolicyCollection` in `Startup.Configure`. For example:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddContentSecurityPolicy(builder =\u003e\n        {\n            builder.AddUpgradeInsecureRequests(); // upgrade-insecure-requests\n            builder.AddBlockAllMixedContent(); // block-all-mixed-content\n\n            builder.AddReportUri() // report-uri: https://report-uri.com\n                .To(\"https://report-uri.com\");\n\n            builder.AddDefaultSrc() // default-src 'self' http://testUrl.com\n                .Self()\n                .From(\"http://testUrl.com\");\n\n            builder.AddConnectSrc() // connect-src 'self' http://testUrl.com\n                .Self()\n                .From(\"http://testUrl.com\");\n\n            builder.AddFontSrc() // font-src 'self'\n                .Self();\n\n            builder.AddObjectSrc() // object-src 'none'\n                .None();\n\n            builder.AddFormAction() // form-action 'self'\n                .Self();\n\n            builder.AddImgSrc() // img-src https:\n                .OverHttps();\n\n            builder.AddScriptSrc() // script-src 'self' 'unsafe-inline' 'unsafe-eval' 'report-sample'\n                .Self()\n                .UnsafeInline()\n                .UnsafeEval()\n                .ReportSample();\n\n            builder.AddStyleSrc() // style-src 'self' 'strict-dynamic'\n                .Self()\n                .StrictDynamic();\n\n            builder.AddMediaSrc() // media-src https:\n                .OverHttps();\n\n            builder.AddFrameAncestors() // frame-ancestors 'none'\n                .None();\n\n            builder.AddBaseUri() // base-ri 'self'\n                .Self();\n\n            builder.AddFrameSource() // frame-src http://testUrl.com\n                .From(\"http://testUrl.com\");\n\n            // You can also add arbitrary extra directives: plugin-types application/x-shockwave-flash\"\n            builder.AddCustomDirective(\"plugin-types\", \"application/x-shockwave-flash\");\n\n        })\n        .AddCustomHeader(\"X-My-Test-Header\", \"Header value\");\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\n## AddPermissionsPolicy\n\nThe `permissions-policy` is a header that allows a site to control which features and APIs can be used in the browser. It is similar to CSP but controls features instead of security behaviour.\n\nWith Permissions-Policy, you opt-in to a set of \"policies\" for the browser to enforce on specific features used throughout a website. These policies restrict what APIs the site can access or modify the browser's default behaviour for certain features.\n\nBy adding Permissions-Policy to headers to your website, you can ensure that sensitive APIs like geolocation or the camera cannot be used, even if your site is otherwise compromised, for example by malicious third-party attacks.\n\nFor more information about the permissions, I recommend the following resources:\n\n* Scott Helme's introduction to Permissions-Policy: https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/\n* The list of policy-controlled permissions: https://www.w3.org/TR/permissions-policy-1/\n* MDN documentation: https://developer.mozilla.org/en-US/docs/Web/HTTP/Feature_Policy\n* Google's introduction to Feature-Policy: https://developers.google.com/web/updates/2018/06/feature-policy\n\nYou configure your Permissions-Policy when you configure your `HeaderPolicyCollection` in `Startup.Configure`. For example:\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddPermissionsPolicy(builder =\u003e\n        {\n            builder.AddAccelerometer() // accelerometer 'self' http://testUrl.com\n                .Self()\n                .For(\"http://testUrl.com\");\n\n            builder.AddAutoplay() // autoplay 'self'\n                .Self();\n\n            builder.AddCamera() // camera 'none'\n                .None();\n\n            builder.AddEncryptedMedia() // encrypted-media 'self'\n                .Self();\n\n            builder.AddFullscreen() // fullscreen *:\n                .All();\n\n            builder.AddGeolocation() // geolocation 'none'\n                .None();\n\n            builder.AddGyroscope() // gyroscope 'none'\n                .None();\n\n            builder.AddMagnetometer() // magnetometer 'none'\n                .None();\n\n            builder.AddMicrophone() // microphone 'none'\n                .None();\n\n            builder.AddMidi() // midi 'none'\n                .None();\n\n            builder.AddPayment() // payment 'none'\n                .None();\n\n            builder.AddPictureInPicture() // picture-in-picture 'none'\n                .None();\n\n            builder.AddSpeaker() // speaker 'none'\n                .None();\n\n            builder.AddSyncXHR() // sync-xhr 'none'\n                .None();\n\n            builder.AddUsb() // usb 'none'\n                .None();\n\n            builder.AddVR() // vr 'none'\n                .None();\n\n            // You can also add arbitrary extra directives: plugin-types application/x-shockwave-flash\"\n            builder.AddCustomFeature(\"plugin-types\", \"application/x-shockwave-flash\");\n            // If a new feature policy is added that follows the standard conventions, you can use this overload\n            // iframe 'self' http://testUrl.com\n            builder.AddCustomFeature(\"iframe\") // \n                .Self()\n                .For(\"http://testUrl.com\");\n        });\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\nIf you want to lock down your permissions policy, you can use\n\n```csharp\nvar policyCollection = new HeaderPolicyCollection()\n        .AddPermissionsPolicyWithDefaultSecureDirectives();\n```\n\nThis applies a \"secure\" policy based on the [suggested by OWASP for APIs](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers):\n\n```HTTP\nPermissions-Policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()\n```\n\nAlternatively, if you want to relax some of these directives, you can use the builder version:\n\n```csharp\nvar policyCollection = new HeaderPolicyCollection()\n        .AddPermissionsPolicy(builder =\u003e\n        {\n            // add all the default versions\n            builder.AddDefaultSecureDirectives();\n            \n            // override a directive\n            builder.AddGeolocation().Self(); // geolocation 'self'\n        });\n```\n\n## Using Nonces and generated-hashes with Content-Security-Policy\n\nThe use of a secure Content-Security-Policy can sometimes be problematic when you need to include inline-scripts, styles, or other objects that haven't been allow-listed. You can achieve this in two ways - using a \"nonce\" (or \"number-used-once\"), or specifying the hash of the content to include. \n\nTo help with this you can install the NetEscapades.AspNetCore.SecurityHeaders.TagHelpers package, which provides helpers for generating a nonce per request, which is attached to the HTML element, and included in the CSP header. A similar method helper exists for `\u003cstyle\u003e` and `\u003cscript\u003e` tags, which will take a SHA256 hash of the contents of the HTML element and add it to the CSP allow-list. For _inline styles_, and inline event handlers, there is an helper that supports generating hashes for the contents of such attribute.\n\nTo use a nonce or an auto-generated hash with your ASP.NET Core application, use the following steps.\n\n### 1. Install the NetEscapades.AspNetCore.SecurityHeaders.TagHelpers NuGet package\n\nFor example:\n\n```bash\ndotnet package add Install-Package NetEscapades.AspNetCore.SecurityHeaders.TagHelpers\n```\n\nThis adds the package to your _.csproj_ file:\n\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk.Web\"\u003e\n\n  \u003cPropertyGroup\u003e\n    \u003cTargetFramework\u003enetcoreapp3.1\u003c/TargetFramework\u003e\n  \u003c/PropertyGroup\u003e\n\n  \u003cItemGroup\u003e\n    \u003cPackageReference Include=\"NetEscapades.AspNetCore.SecurityHeaders\" Version=\"1.0.0\" /\u003e\n    \u003cPackageReference Include=\"NetEscapades.AspNetCore.SecurityHeaders.TagHelpers\" Version=\"1.0.0\" /\u003e\n  \u003c/ItemGroup\u003e\n  \n\u003c/Project\u003e\n```\n\n### 2. Configure your CSP to use nonces and/or hashes\n\nConfigure your security headers in the usual way. Use the `WithNonce()` extension method when configuring  `ContentSecurityPolicy` directives to enable allow-listing with a nonce. Use the `WithHashTagHelper()` extension methods on `script-src` and `style-src` directives to allow automatic generation of allow-listed inline-scripts\n\n```csharp\npublic void Configure(IApplicationBuilder app)\n{\n    var policyCollection = new HeaderPolicyCollection()\n        .AddContentSecurityPolicy(builder =\u003e\n        {\n            builder.AddUpgradeInsecureRequests(); \n            builder.AddDefaultSrc() // default-src 'self' http://testUrl.com\n                .Self()\n                .From(\"http://testUrl.com\");\n\n            builder.AddScriptSrc() // script-src 'self' 'unsafe-inline' 'nonce-\u003cbase64-value\u003e'\n                .Self()\n                .UnsafeInline()\n                .WithNonce(); // Allow elements marked with a nonce attribute\n\n            builder.AddStyleSrc() // style-src 'self' 'strict-dynamic' 'sha256-\u003cbase64-value\u003e'\n                .Self()\n                .StrictDynamic()\n                .WithHashTagHelper().UnsafeHashes(); // Allow allowlisted elements based on their SHA256 hash value\n        })\n        .AddCustomHeader(\"X-My-Test-Header\", \"Header value\");\n\n    app.UseSecurityHeaders(policyCollection);\n\n    // other middleware e.g. static files, MVC etc  \n}\n```\n\n### 3. Add a using directive for the TagHelpers\n\nAdd the following to the *_ViewImports.cshtml* file in your application. This makes the tag-helper available in your Razor views. \n\n```csharp\n@addTagHelper *, NetEscapades.AspNetCore.SecurityHeaders.TagHelpers\n```\n\n### 4. Allow-list elements using the TagHelpers\n\nAdd the `NonceTagHelper` to an element by adding the `asp-add-nonce` attribute.\n\n```html\n\u003cscript asp-add-nonce\u003e\n    var body = document.getElementsByTagName('body')[0];\n    var div = document.createElement('div');\n    div.innerText = \"I was added using the NonceHelper\";\n    body.appendChild(div);\n\u003c/script\u003e\n```\n\nThis will use a unique value per-request and attach the required attribute at runtime, to generate markup similar to the following:\n\n```html\n\u003cscript nonce=\"ryPzmoZScSR2xOwV0qTU9mFdFwGPN\u0026#x2B;gy3S2E1/VK1vg=\"\u003e\n    var body = document.getElementsByTagName('body')[0];\n    var blink = document.createElement('div');\n    blink.innerText = \"And I was added using the NonceHelper\";\n    body.appendChild(blink);\n\u003c/script\u003e\n```\n\n\u003e Note that some browsers will hide the value of the `nonce` attribute when viewed from DevTools. View the page source to see the raw nonce value\n\nWhile the CSP policy would look something like the following:\n\n```http\nContent-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-ryPzmoZScSR2xOwV0qTU9mFdFwGPN\u0026#x2B;gy3S2E1/VK1vg='; style-src 'self' 'strict-dynamic'; default-src 'self' http://testUrl.com\n```\n\nTo use an allow-listed hash instead, use the `HashTagHelper`, by adding the `asp-add-content-to-csp` attribute to `\u003cscript\u003e` or `\u003cstyle\u003e` tags. You can optionally add the `csp-hash-type` attribute to choose between SHA256, SHA384, and SHA512:\n\n```html\n\u003cscript asp-add-content-to-csp\u003e\n    var msg = document.getElementById('message');\n    msg.innerText = \"I'm allowed\";\n\u003c/script\u003e\n\n\u003cstyle asp-add-content-to-csp csp-hash-type=\"SHA384\"\u003e\n#message {\n    color: @color;\n}  \n\u003c/style\u003e\n```\n\nAt runtime, these attributes are removed, but the hash values of the contents are added to the `Content-Security-Policy header`.\n\n### 5. Allow-list attributes using the TagHelpers\n\nInline styles, and event handlers don't support nonces, but there is a dedicated tag helper that supports hashing attributes: `AttributeHashTagHelper`. This works similar to the tag helper for elements, but add the `asp-add-csp-for-*` attribute where `*` is the name of the attribute to hash, like this:\n\n```html\n\u003ch3 asp-add-csp-for-style style=\"color: red\"\u003eI will be styled red\u003c/h3\u003e\n```\n\nMultiple occurrences of this attribute is supported using different values for `*` in case you need to hash multiple attributes. You can still set the hash type by setting that on the attribute `asp-add-csp-for-*` directly (SHA256, SHA384, and SHA512 are still the valid options here):\n\n```html\n\u003cbutton asp-add-csp-for-style style=\"color: red\" asp-add-csp-for-onclick=\"SHA384\" onclick=\"alert('Hello!')\"\u003eClick me!\u003c/button\u003e\n```\n\nAt runtime, these attributes are removed, but the hash values of the contents are added to the `Content-Security-Policy header`.\n\n### Using the generated nonce without a TagHelpers\n\nIf you aren't using Razor, or don't want to use the TagHelpers library, you can access the Nonce for a request using an extension method on `HttpContext`:\n\n```csharp\nstring nonce = HttpContext.GetNonce();\n```\n\n# Verifying NuGet provenance attestations\n\nAll releases of the NuGet packages in this repository include provenance attestations, a Software Bill of Materials (SBOM),\nand attestations for the SBOMs. These attestations are generated based on the NuGet packages created in the pipeline. \nHowever, [nuget.org modifies any uploaded packages]((https://andrewlock.net/creating-provenance-attestations-for-nuget-packages-in-github-actions/#and-now-for-the-bad-news)) \nto include a signature file, which changes the SHA of the packages.\n\nTo verify the provenance for a given downloaded NuGet package from nuget.org, you must first reverse the signature file\nmodification, to reconstruct the package for which the attestation was made. You can then use the GitHub CLI to verify\nthe provenance of the package and the associated SBOMs.\n\nTo remove the signature file on Linux or macOS, you can use the `zip` utility:\n\n```bash\nfile=\"path/to/NetEscapades.AspNetCore.SecurityHeaders.1.0.0.nupkg\"\nzip -d $file .signature.p7s\n```\n\nalternatively, use PowerShell and .NET to remove the `.signature.p7s` file: \n\n```powershell\n$file=\"path/to/NetEscapades.AspNetCore.SecurityHeaders.1.0.0.nupkg\"\n[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression')\n$stream = New-Object IO.FileStream($file, [IO.FileMode]::Open)\n$zip    = New-Object IO.Compression.ZipArchive($stream, [IO.Compression.ZipArchiveMode]::Update)\n$zip.Entries | ? { $_.Name -eq \".signature.p7s\" } | % { $_.Delete() }\n$zip.Dispose();\n```\n\nYou can then verify the provenance of the package using [the GitHub CLI](https://cli.github.com/):\n\n```bash\ngh attestation verify --owner andrewlock \"NetEscapades.AspNetCore.SecurityHeaders.1.0.0.nupkg\"\ngh attestation verify --owner andrewlock \"NetEscapades.AspNetCore.SecurityHeaders.TagHelpers.1.0.0.nupkg\"\n```\n\non success, this displays output similar to the following:\n\n```bash\nLoaded digest sha256:bf809ff0ed6a8a31131df4391b169e35ded44d4dfd97cc797123441683a95c9f for file://NetEscapades.AspNetCore.SecurityHeaders.1.0.0.nupkg\nLoaded 2 attestations from GitHub API\n\nThe following policy criteria will be enforced:\n- Predicate type must match:................ https://slsa.dev/provenance/v1\n- Source Repository Owner URI must match:... https://github.com/andrewlock\n- Subject Alternative Name must match regex: (?i)^https://github.com/andrewlock/\n- OIDC Issuer must match:................... https://token.actions.githubusercontent.com\n\n✓ Verification succeeded!\n\nThe following 1 attestation matched the policy criteria\n\n- Attestation #1\n  - Build repo:..... andrewlock/NetEscapades.AspNetCore.SecurityHeaders\n  - Build workflow:. .github/workflows/BuildAndPack.yml@refs/tags/v1.0.0\n  - Signer repo:.... andrewlock/NetEscapades.AspNetCore.SecurityHeaders\n  - Signer workflow: .github/workflows/BuildAndPack.yml@refs/tags/v1.0.0\n```\n\nSBOMs are provided in the GitHub release for the packages using the [CycloneDX standard](https://cyclonedx.org/). \nAs for build provenance, attestations for the SBOMs must be verified against the _.nupkg_ files\nwith the `.signature.p7s` file removed. Assuming you have modified the _.nupkg_ files to remove the signature file, as described above,\nyou can verify the SBOM attestations by specifying the `--predicate-type`:\n\n```bash\ngh attestation verify --owner andrewlock --predicate-type https://cyclonedx.org/bom \"NetEscapades.AspNetCore.SecurityHeaders.1.0.0.nupkg\"\ngh attestation verify --owner andrewlock --predicate-type https://cyclonedx.org/bom \"NetEscapades.AspNetCore.SecurityHeaders.TagHelpers.1.0.0.nupkg\"\n```\n\n## Additional Resources\n\n* [ASP.NET Core Middleware Docs](https://docs.asp.net/en/latest/fundamentals/middleware.html)\n* [How to add default security headers in ASP.NET Core using custom middleware](http://andrewlock.net/adding-default-security-headers-in-asp-net-core/)\n* [Content Security Policy - An Introduction](https://scotthelme.co.uk/content-security-policy-an-introduction/) by Scott Helme\n* [Content Security Policy Reference](https://content-security-policy.com/)\n* [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) by Mozilla Developer Network\n* [OWASP Content Security Policy Cheatsheat](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#csp-sample-policies)\n* [OWASP REST security header guidance](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers)\n* [OWASP HTTP Headers guidance](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#security-headers)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewlock%2Fnetescapades.aspnetcore.securityheaders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewlock%2Fnetescapades.aspnetcore.securityheaders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewlock%2Fnetescapades.aspnetcore.securityheaders/lists"}