├── .editorconfig ├── .gitignore ├── Joonasw.AspNetCore.SecurityHeaders.sln ├── LICENSE.txt ├── README.md ├── azure-pipelines.yml ├── src └── Joonasw.AspNetCore.SecurityHeaders │ ├── AppBuilderExtensions.cs │ ├── Csp │ ├── Builder │ │ ├── CspBaseUriBuilder.cs │ │ ├── CspBuilder.cs │ │ ├── CspChildBuilder.cs │ │ ├── CspConnectionBuilder.cs │ │ ├── CspDefaultBuilder.cs │ │ ├── CspFontsBuilder.cs │ │ ├── CspFormActionBuilder.cs │ │ ├── CspFrameAncestorsBuilder.cs │ │ ├── CspFrameBuilder.cs │ │ ├── CspImageBuilder.cs │ │ ├── CspManifestBuilder.cs │ │ ├── CspMediaBuilder.cs │ │ ├── CspPluginBuilder.cs │ │ ├── CspPrefetchBuilder.cs │ │ ├── CspRequireSriBuilder.cs │ │ ├── CspSandboxBuilder.cs │ │ ├── CspScriptsBuilder.cs │ │ ├── CspStylesBuilder.cs │ │ └── CspWorkerBuilder.cs │ ├── CspMiddleware.cs │ ├── CspNonceService.cs │ ├── CspSendingHeaderContext.cs │ ├── ICspNonceService.cs │ └── Options │ │ ├── CspBaseUriOptions.cs │ │ ├── CspChildSrcOptions.cs │ │ ├── CspConnectSrcOptions.cs │ │ ├── CspDefaultSrcOptions.cs │ │ ├── CspFontSrcOptions.cs │ │ ├── CspFormActionOptions.cs │ │ ├── CspFrameAncestorsOptions.cs │ │ ├── CspFrameSrcOptions.cs │ │ ├── CspImgSrcOptions.cs │ │ ├── CspManifestSrcOptions.cs │ │ ├── CspMediaSrcOptions.cs │ │ ├── CspObjectSrcOptions.cs │ │ ├── CspPluginTypesOptions.cs │ │ ├── CspPrefetchSrcOptions.cs │ │ ├── CspRequireSriOptions.cs │ │ ├── CspSandboxOptions.cs │ │ ├── CspScriptSrcOptions.cs │ │ ├── CspSrcOptionsBase.cs │ │ ├── CspStyleSrcOptions.cs │ │ └── CspWorkerSrcOptions.cs │ ├── CspOptions.cs │ ├── EnumExtensions.cs │ ├── ExpectCT │ ├── ExpectCTMiddleware.cs │ └── ExpectCTOptions.cs │ ├── FeaturePolicy │ ├── Builder │ │ ├── FeaturePolicyBuilder.cs │ │ ├── FeaturePolicyFeatureBuilder.cs │ │ ├── FeaturePolicyOtherFeatureBuilder.cs │ │ └── IFeaturePolicyFeatureBuilder.cs │ ├── FeaturePolicyMiddleware.cs │ └── Options │ │ ├── FeaturePolicyAccelerometerOptions.cs │ │ ├── FeaturePolicyAmbientLightSensorOptions.cs │ │ ├── FeaturePolicyAutoplayOptions.cs │ │ ├── FeaturePolicyCameraOptions.cs │ │ ├── FeaturePolicyEncryptedMediaOptions.cs │ │ ├── FeaturePolicyFullscreenOptions.cs │ │ ├── FeaturePolicyGeolocationOptions.cs │ │ ├── FeaturePolicyGyroscopeOptions.cs │ │ ├── FeaturePolicyMagnetometerOptions.cs │ │ ├── FeaturePolicyMicrophoneOptions.cs │ │ ├── FeaturePolicyMidiOptions.cs │ │ ├── FeaturePolicyNotificationsOptions.cs │ │ ├── FeaturePolicyOptionsBase.cs │ │ ├── FeaturePolicyOtherFeatureOptions.cs │ │ ├── FeaturePolicyPaymentOptions.cs │ │ ├── FeaturePolicyPictureInPictureOptions.cs │ │ ├── FeaturePolicyPushOptions.cs │ │ ├── FeaturePolicySpeakerOptions.cs │ │ ├── FeaturePolicySyncXhrOptions.cs │ │ ├── FeaturePolicyUsbOptions.cs │ │ ├── FeaturePolicyVibrateOptions.cs │ │ └── FeaturePolicyVrOptions.cs │ ├── FeaturePolicyOptions.cs │ ├── Hpkp │ ├── Builder │ │ └── HpkpBuilder.cs │ └── HpkpMiddleware.cs │ ├── HpkpOptions.cs │ ├── Hsts │ ├── HstsMiddleware.cs │ └── HstsOptionsExtensions.cs │ ├── HstsOptions.cs │ ├── Joonasw.AspNetCore.SecurityHeaders.csproj │ ├── ReferrerPolicy │ └── ReferrerPolicyMiddleware.cs │ ├── ReferrerPolicyOptions.cs │ ├── ServiceCollectionExtensions.cs │ ├── TagHelpers │ └── NonceTagHelper.cs │ ├── XContentTypeOptions │ └── XContentTypeOptionsMiddleware.cs │ ├── XContentTypeOptionsOptions.cs │ ├── XFrameOptions │ ├── XFrameOptionsExtensions.cs │ └── XFrameOptionsMiddleware.cs │ ├── XFrameOptionsOptions.cs │ ├── XXssProtection │ └── XXssProtectionMiddleware.cs │ └── XXssProtectionOptions.cs └── test ├── Joonasw.AspNetCore.SecurityHeaders.Samples ├── .bowerrc ├── Controllers │ ├── HomeController.cs │ └── ReportController.cs ├── Joonasw.AspNetCore.SecurityHeaders.Samples.csproj ├── Middleware │ ├── AppBuilderExtensions.cs │ └── EnforceHttpsMiddleware.cs ├── Models │ ├── CspViolationReport.cs │ └── HpkpViolationReport.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ ├── Home │ │ ├── About.cshtml │ │ ├── Contact.cshtml │ │ └── Index.cshtml │ ├── Shared │ │ ├── ApplicationError.cshtml │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── appsettings.json ├── bower.json ├── bundleconfig.json ├── web.config └── wwwroot │ ├── _references.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── images │ ├── banner1.svg │ ├── banner2.svg │ ├── banner3.svg │ └── banner4.svg │ ├── js │ ├── OtherScript.js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── .bower.json │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── jquery-validation-unobtrusive │ ├── .bower.json │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── .bower.json │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── .bower.json │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map └── Joonasw.AspNetCore.SecurityHeaders.Tests ├── ContentTypeOptionsMiddlewareTests.cs ├── CspBuilderTests.cs ├── CspMiddlewareTests.cs ├── CspOptionsTests.cs ├── CspRequireSriBuilderTests.cs ├── CspSandboxBuilderTests.cs ├── CspScriptsBuilderTests.cs ├── CspStylesBuilderTests.cs ├── CspWorkerBuilderTests.cs ├── ExpectCtMiddlewareTests.cs ├── FeaturePolicyBuilderTests.cs ├── FeaturePolicyMiddlewareTests.cs ├── FrameOptionsMiddlewareTests.cs ├── HpkpMiddlewareTests.cs ├── HstsMiddlewareTests.cs ├── Joonasw.AspNetCore.SecurityHeaders.Tests.csproj ├── ReferrerPolicyMiddlewareTests.cs ├── XFrameOptionsTests.cs ├── XXssProtectionTests.cs └── XssProtectionMiddlewareTests.cs /Joonasw.AspNetCore.SecurityHeaders.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26621.2 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C93B021C-E01E-497C-97EB-61598F8F8E2A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders.Samples", "test\Joonasw.AspNetCore.SecurityHeaders.Samples\Joonasw.AspNetCore.SecurityHeaders.Samples.csproj", "{8FF34C33-8DF3-41D6-9A4A-C461C201B389}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders", "src\Joonasw.AspNetCore.SecurityHeaders\Joonasw.AspNetCore.SecurityHeaders.csproj", "{E3185634-6192-4C1D-8BB1-65DE700901BB}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{568F7557-6509-4F00-BD44-3CB7804D87FC}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Joonasw.AspNetCore.SecurityHeaders.Tests", "test\Joonasw.AspNetCore.SecurityHeaders.Tests\Joonasw.AspNetCore.SecurityHeaders.Tests.csproj", "{B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20FDC4F1-21F4-4D9D-8E0A-A063491C5AB7}" 17 | ProjectSection(SolutionItems) = preProject 18 | .editorconfig = .editorconfig 19 | LICENSE.txt = LICENSE.txt 20 | EndProjectSection 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|Any CPU = Release|Any CPU 28 | Release|x64 = Release|x64 29 | Release|x86 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x64.ActiveCfg = Debug|Any CPU 35 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x64.Build.0 = Debug|Any CPU 36 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x86.ActiveCfg = Debug|Any CPU 37 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Debug|x86.Build.0 = Debug|Any CPU 38 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x64.ActiveCfg = Release|Any CPU 41 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x64.Build.0 = Release|Any CPU 42 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x86.ActiveCfg = Release|Any CPU 43 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389}.Release|x86.Build.0 = Release|Any CPU 44 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x64.ActiveCfg = Debug|Any CPU 47 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x64.Build.0 = Debug|Any CPU 48 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x86.ActiveCfg = Debug|Any CPU 49 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Debug|x86.Build.0 = Debug|Any CPU 50 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x64.ActiveCfg = Release|Any CPU 53 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x64.Build.0 = Release|Any CPU 54 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x86.ActiveCfg = Release|Any CPU 55 | {E3185634-6192-4C1D-8BB1-65DE700901BB}.Release|x86.Build.0 = Release|Any CPU 56 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x64.ActiveCfg = Debug|Any CPU 59 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x64.Build.0 = Debug|Any CPU 60 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x86.ActiveCfg = Debug|Any CPU 61 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Debug|x86.Build.0 = Debug|Any CPU 62 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x64.ActiveCfg = Release|Any CPU 65 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x64.Build.0 = Release|Any CPU 66 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x86.ActiveCfg = Release|Any CPU 67 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F}.Release|x86.Build.0 = Release|Any CPU 68 | EndGlobalSection 69 | GlobalSection(SolutionProperties) = preSolution 70 | HideSolutionNode = FALSE 71 | EndGlobalSection 72 | GlobalSection(NestedProjects) = preSolution 73 | {8FF34C33-8DF3-41D6-9A4A-C461C201B389} = {568F7557-6509-4F00-BD44-3CB7804D87FC} 74 | {E3185634-6192-4C1D-8BB1-65DE700901BB} = {C93B021C-E01E-497C-97EB-61598F8F8E2A} 75 | {B3C0CA83-9CE4-4B9F-8447-BA9D8E8C9F5F} = {568F7557-6509-4F00-BD44-3CB7804D87FC} 76 | EndGlobalSection 77 | GlobalSection(ExtensibilityGlobals) = postSolution 78 | SolutionGuid = {B16EDA54-6190-47A7-B948-8E6C55972B9A} 79 | EndGlobalSection 80 | EndGlobal 81 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Joonas Westlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Add CSP, HSTS or HPKP headers to an ASP.NET Core app 2 | 3 | This library allows you to add Content Security Policy, Strict Transport Security and Public Key Pin headers via middleware. 4 | 5 | You can get the library from NuGet: [https://www.nuget.org/packages/Joonasw.AspNetCore.SecurityHeaders](https://www.nuget.org/packages/Joonasw.AspNetCore.SecurityHeaders) 6 | 7 | ## Example configuration 8 | 9 | ```cs 10 | // Enable Strict Transport Security with a 30-day caching period 11 | // Do not include subdomains 12 | // Do not allow preload 13 | app.UseStrictTransportSecurity(new HstsOptions(TimeSpan.FromDays(30), includeSubDomains: false, preload: false)); 14 | 15 | // Use certificate pinning with: 16 | // - 30-day caching period 17 | // - One pin in SHA-256 form 18 | // - Report-Only = Invalid certificate should not be reported, but: 19 | // - Report problems to /hpkp-report 20 | app.UseHpkp(hpkp => 21 | { 22 | hpkp.UseMaxAgeSeconds(30 * 24 * 60 * 60) 23 | .AddSha256Pin("nrmpk4ZI3wbRBmUZIT5aKAgP0LlKHRgfA2Snjzeg9iY=") 24 | .SetReportOnly() 25 | .ReportViolationsTo("/hpkp-report"); 26 | }); 27 | 28 | // Content Security Policy 29 | app.UseCsp(csp => 30 | { 31 | // If nothing is mentioned for a resource class, allow from this domain 32 | csp.ByDefaultAllow 33 | .FromSelf(); 34 | 35 | // Allow JavaScript from: 36 | csp.AllowScripts 37 | .FromSelf() //This domain 38 | .From("localhost:1591") //These two domains 39 | .From("ajax.aspnetcdn.com"); 40 | 41 | // CSS allowed from: 42 | csp.AllowStyles 43 | .FromSelf() 44 | .From("ajax.aspnetcdn.com"); 45 | 46 | csp.AllowImages 47 | .FromSelf(); 48 | 49 | // HTML5 audio and video elemented sources can be from: 50 | csp.AllowAudioAndVideo 51 | .FromNowhere(); 52 | 53 | // Contained iframes can be sourced from: 54 | csp.AllowFrames 55 | .FromNowhere(); //Nowhere, no iframes allowed 56 | 57 | // Allow AJAX, WebSocket and EventSource connections to: 58 | csp.AllowConnections 59 | .To("ws://localhost:1591") 60 | .To("http://localhost:1591") 61 | .ToSelf(); 62 | 63 | // Allow fonts to be downloaded from: 64 | csp.AllowFonts 65 | .FromSelf() 66 | .From("ajax.aspnetcdn.com"); 67 | 68 | // Allow object, embed, and applet sources from: 69 | csp.AllowPlugins 70 | .FromNowhere(); 71 | 72 | // Allow other sites to put this in an iframe? 73 | csp.AllowFraming 74 | .FromNowhere(); // Block framing on other sites, equivalent to X-Frame-Options: DENY 75 | 76 | // Do not block violations, only report 77 | // This is a good idea while testing your CSP 78 | // Remove it when you know everything will work 79 | csp.SetReportOnly(); 80 | // Where should the violation reports be sent to? 81 | csp.ReportViolationsTo("/csp-report"); 82 | 83 | // Do not include the CSP header for requests to the /api endpoints 84 | csp.OnSendingHeader = context => 85 | { 86 | context.ShouldNotSend = context.HttpContext.Request.Path.StartsWithSegments("/api"); 87 | return Task.CompletedTask; 88 | }; 89 | }); 90 | ``` 91 | 92 | Content Security Policy can be quite daunting. Here is a nice page to find out what the options do: [https://content-security-policy.com/](https://content-security-policy.com/.) 93 | 94 | For violation reports, I recommend using Scott Helme's Report URI service at [https://report-uri.io/](https://report-uri.io/). 95 | 96 | ## Nonces 97 | 98 | CSP allows you to also specify a nonce value, which makes it easier to have inline script and style elements like this on a page: 99 | 100 | ```html 101 | 102 | 105 | 110 | 111 | ``` 112 | 113 | To allow them without nonces, you might have to use the unsafe-inline option. 114 | 115 | Instead of doing that, we can add the following service in `Startup`: 116 | 117 | ```cs 118 | public void ConfigureServices(IServiceCollection services) 119 | { 120 | // ... other service registrations 121 | 122 | // Add services necessary for nonces in CSP, 32-byte nonces 123 | services.AddCsp(nonceByteAmount: 32); 124 | } 125 | ``` 126 | 127 | Then you need to modify your CSP definition to include the nonce: 128 | 129 | ```cs 130 | csp.AllowScripts 131 | .FromSelf() 132 | .From("localhost:1591") 133 | .From("ajax.aspnetcdn.com") 134 | .AddNonce(); //<---- 135 | 136 | csp.AllowStyles 137 | .FromSelf() 138 | .From("ajax.aspnetcdn.com") 139 | .AddNonce(); //<----- 140 | ``` 141 | 142 | Then to use the nonce tag helper, we need to import it in *_ViewImports.cshtml*: 143 | 144 | ```c# 145 | @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders 146 | ``` 147 | 148 | Then we just need to use it in the Razor view: 149 | 150 | ```html 151 | 152 | 155 | 160 | 161 | ``` 162 | 163 | Now a unique nonce is generated every request and inserted into the CSP header + the elements you want. 164 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | name: Azure Pipelines 3 | vmImage: windows-2022 4 | 5 | variables: 6 | buildConfiguration: "Release" 7 | 8 | steps: 9 | - task: DotNetCoreCLI@2 10 | displayName: "dotnet build" 11 | inputs: 12 | projects: "**/*.csproj" 13 | arguments: "--configuration $(buildConfiguration)" 14 | 15 | - task: DotNetCoreCLI@2 16 | displayName: "dotnet test" 17 | inputs: 18 | command: test 19 | projects: "**/*Tests/*.csproj" 20 | arguments: "--configuration $(buildConfiguration)" 21 | 22 | - task: PublishBuildArtifacts@1 23 | displayName: "Publish Artifact: Build results" 24 | inputs: 25 | PathtoPublish: "src/Joonasw.AspNetCore.SecurityHeaders/bin/$(buildConfiguration)" 26 | ArtifactName: "Build results" 27 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspBaseUriBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | using System; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | public class CspBaseUriBuilder 7 | { 8 | private readonly CspBaseUriOptions _options = new CspBaseUriOptions(); 9 | 10 | /// 11 | /// Block <base> element usage. 12 | /// 13 | public void FromNowhere() 14 | { 15 | _options.AllowNone = true; 16 | } 17 | 18 | /// 19 | /// Allow <base> element value to be the 20 | /// current host. 21 | /// 22 | /// The builder for call chaining 23 | public CspBaseUriBuilder FromSelf() 24 | { 25 | _options.AllowSelf = true; 26 | return this; 27 | } 28 | 29 | /// 30 | /// Allow <base> element value to be the given 31 | /// URI. 32 | /// 33 | /// The URI to allow. 34 | /// The builder for call chaining 35 | public CspBaseUriBuilder From(string uri) 36 | { 37 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 38 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 39 | 40 | _options.AllowedSources.Add(uri); 41 | return this; 42 | } 43 | 44 | /// 45 | /// Allow <base> element values from anywhere, except 46 | /// data:, blob:, and filesystem: schemes. 47 | /// 48 | /// The builder for call chaining 49 | public CspBaseUriBuilder FromAnywhere() 50 | { 51 | _options.AllowAny = true; 52 | return this; 53 | } 54 | 55 | /// 56 | /// Require <base> element values to 57 | /// use only HTTPS. 58 | /// 59 | /// The builder for call chaining 60 | public CspBaseUriBuilder OnlyOverHttps() 61 | { 62 | _options.AllowOnlyHttps = true; 63 | return this; 64 | } 65 | 66 | public CspBaseUriOptions BuildOptions() 67 | { 68 | return _options; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspChildBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to embedded content. 9 | /// 10 | public class CspChildBuilder 11 | { 12 | private readonly CspChildSrcOptions _options = new CspChildSrcOptions(); 13 | 14 | /// 15 | /// Block all embedded content. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow embedded content from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspChildBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow embedded content from given . 34 | /// 35 | /// The URI to allow. 36 | /// The builder for call chaining 37 | public CspChildBuilder From(string uri) 38 | { 39 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 40 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 41 | 42 | _options.AllowedSources.Add(uri); 43 | return this; 44 | } 45 | 46 | /// 47 | /// Allow embedded content from anywhere, except 48 | /// data:, blob:, and filesystem: schemes. 49 | /// 50 | /// The builder for call chaining 51 | public CspChildBuilder FromAnywhere() 52 | { 53 | _options.AllowAny = true; 54 | return this; 55 | } 56 | 57 | /// 58 | /// Allow embedded content only over secure connections. 59 | /// 60 | /// The builder for call chaining 61 | public CspChildBuilder OnlyOverHttps() 62 | { 63 | _options.AllowOnlyHttps = true; 64 | return this; 65 | } 66 | 67 | public CspChildSrcOptions BuildOptions() 68 | { 69 | return _options; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspConnectionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 4 | { 5 | /// 6 | /// Builder for Content Security Policy 7 | /// rules related to AJAX, WebSockets and EventSource. 8 | /// 9 | public class CspConnectionBuilder 10 | { 11 | private readonly CspConnectSrcOptions _options = new CspConnectSrcOptions(); 12 | 13 | /// 14 | /// Allow AJAX, WebSockets and EventSource 15 | /// to the current domain. 16 | /// 17 | /// The builder for call chaining 18 | public CspConnectionBuilder ToSelf() 19 | { 20 | _options.AllowSelf = true; 21 | return this; 22 | } 23 | 24 | /// 25 | /// Allow AJAX, WebSockets and EventSource 26 | /// to the given . 27 | /// 28 | /// The URI to allow. 29 | /// The builder for call chaining 30 | public CspConnectionBuilder To(string uri) 31 | { 32 | _options.AllowedSources.Add(uri); 33 | return this; 34 | } 35 | 36 | /// 37 | /// Allow AJAX, WebSockets and EventSource 38 | /// to anywhere. 39 | /// 40 | /// The builder for call chaining 41 | public CspConnectionBuilder ToAnywhere() 42 | { 43 | _options.AllowAny = true; 44 | return this; 45 | } 46 | 47 | /// 48 | /// Block all usage of AJAX, WebSockets and 49 | /// EventSource. 50 | /// 51 | /// The builder for call chaining 52 | public void ToNowhere() 53 | { 54 | _options.AllowNone = true; 55 | } 56 | 57 | /// 58 | /// Allow AJAX, WebSockets and EventSource 59 | /// only over secure connections. 60 | /// 61 | /// The builder for call chaining 62 | public CspConnectionBuilder OnlyOverHttps() 63 | { 64 | _options.AllowOnlyHttps = true; 65 | return this; 66 | } 67 | 68 | public CspConnectSrcOptions BuildOptions() 69 | { 70 | return _options; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspDefaultBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for default fallback 8 | /// Content Security Policy rules. 9 | /// 10 | public class CspDefaultBuilder 11 | { 12 | private readonly CspDefaultSrcOptions _options = new CspDefaultSrcOptions(); 13 | 14 | /// 15 | /// Block the resource. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow resources from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspDefaultBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow resources from the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspDefaultBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow resources from any source, except 49 | /// data:, blob:, and filesystem: schemes. 50 | /// 51 | /// The builder for call chaining 52 | public CspDefaultBuilder FromAnywhere() 53 | { 54 | _options.AllowAny = true; 55 | return this; 56 | } 57 | 58 | /// 59 | /// Allow resources only over secure connections. 60 | /// 61 | /// The builder for call chaining 62 | public CspDefaultBuilder OnlyOverHttps() 63 | { 64 | _options.AllowOnlyHttps = true; 65 | return this; 66 | } 67 | 68 | public CspDefaultSrcOptions BuildOptions() 69 | { 70 | return _options; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFontsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to fonts. 9 | /// 10 | public class CspFontsBuilder 11 | { 12 | private readonly CspFontSrcOptions _options = new CspFontSrcOptions(); 13 | 14 | /// 15 | /// Block all fonts. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow fonts from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspFontsBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow fonts from the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspFontsBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow fonts from anywhere, except 49 | /// data:, blob:, and filesystem: schemes. 50 | /// 51 | /// The builder for call chaining 52 | public CspFontsBuilder FromAnywhere() 53 | { 54 | _options.AllowAny = true; 55 | return this; 56 | } 57 | 58 | /// 59 | /// Allow fonts only over secure 60 | /// connections. 61 | /// 62 | /// The builder for call chaining 63 | public CspFontsBuilder OnlyOverHttps() 64 | { 65 | _options.AllowOnlyHttps = true; 66 | return this; 67 | } 68 | 69 | public CspFontSrcOptions BuildOptions() 70 | { 71 | return _options; 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFormActionBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 4 | { 5 | /// 6 | /// Builder for Content Security Policy 7 | /// rules related to form action targets. 8 | /// 9 | public class CspFormActionBuilder 10 | { 11 | private readonly CspFormActionOptions _options = new CspFormActionOptions(); 12 | 13 | /// 14 | /// Allow form submissions to current domain. 15 | /// 16 | /// The builder for call chaining 17 | public CspFormActionBuilder ToSelf() 18 | { 19 | _options.AllowSelf = true; 20 | return this; 21 | } 22 | 23 | /// 24 | /// Allow form submissions to the given 25 | /// . 26 | /// 27 | /// 28 | /// The builder for call chaining 29 | public CspFormActionBuilder To(string uri) 30 | { 31 | _options.AllowedSources.Add(uri); 32 | return this; 33 | } 34 | 35 | /// 36 | /// Allow form submissions anywhere. 37 | /// 38 | /// The builder for call chaining 39 | public CspFormActionBuilder ToAnywhere() 40 | { 41 | _options.AllowAny = true; 42 | return this; 43 | } 44 | 45 | /// 46 | /// Block form submissions. 47 | /// 48 | /// The builder for call chaining 49 | public void ToNowhere() 50 | { 51 | _options.AllowNone = true; 52 | } 53 | 54 | /// 55 | /// Allow form submissions only over secure 56 | /// connections. 57 | /// 58 | /// The builder for call chaining 59 | public CspFormActionBuilder OnlyOverHttps() 60 | { 61 | _options.AllowOnlyHttps = true; 62 | return this; 63 | } 64 | 65 | public CspFormActionOptions BuildOptions() 66 | { 67 | return _options; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFrameAncestorsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to embedding this app. 9 | /// 10 | public class CspFrameAncestorsBuilder 11 | { 12 | private readonly CspFrameAncestorsOptions _options = new CspFrameAncestorsOptions(); 13 | 14 | /// 15 | /// Deny embedding this app. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow embedding this app in itself. 24 | /// 25 | /// The builder for call chaining 26 | public CspFrameAncestorsBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow embedding this app in the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspFrameAncestorsBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow embedding this app anywhere. 49 | /// 50 | /// The builder for call chaining 51 | public CspFrameAncestorsBuilder FromAnywhere() 52 | { 53 | _options.AllowAny = true; 54 | return this; 55 | } 56 | 57 | /// 58 | /// Allow embedding this app only 59 | /// over secure connections. 60 | /// 61 | /// The builder for call chaining 62 | public CspFrameAncestorsBuilder OnlyOverHttps() 63 | { 64 | _options.AllowOnlyHttps = true; 65 | return this; 66 | } 67 | 68 | public CspFrameAncestorsOptions BuildOptions() 69 | { 70 | return _options; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspFrameBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy rules 8 | /// related to <frame> and <iframe> sources. 9 | /// 10 | public class CspFrameBuilder 11 | { 12 | private readonly CspFrameSrcOptions _options = new CspFrameSrcOptions(); 13 | 14 | /// 15 | /// Block all <frame> 16 | /// and <iframe> sources. 17 | /// 18 | public void FromNowhere() 19 | { 20 | _options.AllowNone = true; 21 | } 22 | 23 | /// 24 | /// Allow <frame> and <iframe> sources 25 | /// from current domain. 26 | /// 27 | /// The builder for call chaining 28 | public CspFrameBuilder FromSelf() 29 | { 30 | _options.AllowSelf = true; 31 | return this; 32 | } 33 | 34 | /// 35 | /// Allow <frame> and <iframe> sources 36 | /// from the given . 37 | /// 38 | /// The URI to allow. 39 | /// The builder for call chaining 40 | public CspFrameBuilder From(string uri) 41 | { 42 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 43 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 44 | 45 | _options.AllowedSources.Add(uri); 46 | return this; 47 | } 48 | 49 | /// 50 | /// Allow <frame> and <iframe> sources 51 | /// from anywhere, except data:, blob:, 52 | /// and filesystem: schemes. 53 | /// 54 | /// The builder for call chaining 55 | public CspFrameBuilder FromAnywhere() 56 | { 57 | _options.AllowAny = true; 58 | return this; 59 | } 60 | 61 | /// 62 | /// Allow <frame> and <iframe> sources 63 | /// only over secure connections. 64 | /// 65 | /// The builder for call chaining 66 | public CspFrameBuilder OnlyOverHttps() 67 | { 68 | _options.AllowOnlyHttps = true; 69 | return this; 70 | } 71 | 72 | public CspFrameSrcOptions BuildOptions() 73 | { 74 | return _options; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspImageBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to images. 9 | /// 10 | public class CspImageBuilder 11 | { 12 | private readonly CspImgSrcOptions _options = new CspImgSrcOptions(); 13 | 14 | /// 15 | /// Block all images. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow images from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspImageBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow images from the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspImageBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow images from anywhere, except 49 | /// data:, blob:, and filesystem: schemes. 50 | /// 51 | /// The builder for call chaining 52 | public CspImageBuilder FromAnywhere() 53 | { 54 | _options.AllowAny = true; 55 | return this; 56 | } 57 | 58 | /// 59 | /// Allow images over secure connections. 60 | /// 61 | /// The builder for call chaining 62 | public CspImageBuilder OnlyOverHttps() 63 | { 64 | _options.AllowOnlyHttps = true; 65 | return this; 66 | } 67 | 68 | /// 69 | /// Allow data: scheme embedding of images. 70 | /// 71 | /// The builder for call chaining 72 | public CspImageBuilder DataScheme() 73 | { 74 | _options.AllowDataScheme = true; 75 | return this; 76 | } 77 | 78 | public CspImgSrcOptions BuildOptions() 79 | { 80 | return _options; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspManifestBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 4 | { 5 | /// 6 | /// Builder for controlling where web manifests can be loaded from. 7 | /// 8 | public class CspManifestBuilder 9 | { 10 | private readonly CspManifestSrcOptions _options = new CspManifestSrcOptions(); 11 | 12 | /// 13 | /// Allow manifests to be loaded from the current domain. 14 | /// 15 | /// The builder for call chaining 16 | public CspManifestBuilder FromSelf() 17 | { 18 | _options.AllowSelf = true; 19 | return this; 20 | } 21 | 22 | /// 23 | /// Allow manifests to load from the given . 24 | /// 25 | /// The URI to allow. 26 | /// The builder for call chaining 27 | public CspManifestBuilder From(string uri) 28 | { 29 | _options.AllowedSources.Add(uri); 30 | return this; 31 | } 32 | 33 | /// 34 | /// Allow manifests to load from anywhere. 35 | /// 36 | /// The builder for call chaining 37 | public CspManifestBuilder FromAnywhere() 38 | { 39 | _options.AllowAny = true; 40 | return this; 41 | } 42 | 43 | /// 44 | /// Block all loading of manifests. 45 | /// 46 | /// The builder for call chaining 47 | public void FromNowhere() 48 | { 49 | _options.AllowNone = true; 50 | } 51 | 52 | /// 53 | /// Allow manifests to load from secure connections. 54 | /// 55 | /// The builder for call chaining 56 | public CspManifestBuilder OnlyOverHttps() 57 | { 58 | _options.AllowOnlyHttps = true; 59 | return this; 60 | } 61 | 62 | public CspManifestSrcOptions BuildOptions() 63 | { 64 | return _options; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspMediaBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy rules 8 | /// related to <audio> and <video> sources. 9 | /// 10 | public class CspMediaBuilder 11 | { 12 | private readonly CspMediaSrcOptions _options = new CspMediaSrcOptions(); 13 | 14 | /// 15 | /// Block all <audio> 16 | /// and <video> sources. 17 | /// 18 | public void FromNowhere() 19 | { 20 | _options.AllowNone = true; 21 | } 22 | 23 | /// 24 | /// Allow <audio> and <video> sources 25 | /// from current domain. 26 | /// 27 | /// The builder for call chaining 28 | public CspMediaBuilder FromSelf() 29 | { 30 | _options.AllowSelf = true; 31 | return this; 32 | } 33 | 34 | /// 35 | /// Allow <audio> and <video> sources 36 | /// from the given . 37 | /// 38 | /// The URI to allow. 39 | /// The builder for call chaining 40 | public CspMediaBuilder From(string uri) 41 | { 42 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 43 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 44 | 45 | _options.AllowedSources.Add(uri); 46 | return this; 47 | } 48 | 49 | /// 50 | /// Allow <audio> and <video> sources 51 | /// from anywhere, except data:, blob:, 52 | /// and filesystem: schemes. 53 | /// 54 | /// The builder for call chaining 55 | public CspMediaBuilder FromAnywhere() 56 | { 57 | _options.AllowAny = true; 58 | return this; 59 | } 60 | 61 | /// 62 | /// Allow <audio> and <video> sources 63 | /// only over secure connections. 64 | /// 65 | /// The builder for call chaining 66 | public CspMediaBuilder OnlyOverHttps() 67 | { 68 | _options.AllowOnlyHttps = true; 69 | return this; 70 | } 71 | 72 | public CspMediaSrcOptions BuildOptions() 73 | { 74 | return _options; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspPluginBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy rules 8 | /// related to plugins in e.g. <object>, 9 | /// <embed>, and <applet> elements. 10 | /// 11 | public class CspPluginBuilder 12 | { 13 | private readonly CspPluginTypesOptions _pluginOptions = new CspPluginTypesOptions(); 14 | private readonly CspObjectSrcOptions _objectOptions = new CspObjectSrcOptions(); 15 | 16 | /// 17 | /// Block all <object>, <embed>, 18 | /// and <applet> sources. 19 | /// 20 | public void FromNowhere() 21 | { 22 | _objectOptions.AllowNone = true; 23 | } 24 | 25 | /// 26 | /// Allow <object>, <embed>, and <applet> 27 | /// sources from current domain. 28 | /// 29 | /// The builder for call chaining 30 | public CspPluginBuilder FromSelf() 31 | { 32 | _objectOptions.AllowSelf = true; 33 | return this; 34 | } 35 | 36 | /// 37 | /// Allow <object>, <embed>, and <applet> 38 | /// sources from the given . 39 | /// 40 | /// The URI to allow. 41 | /// The builder for call chaining 42 | public CspPluginBuilder From(string uri) 43 | { 44 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 45 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 46 | 47 | _objectOptions.AllowedSources.Add(uri); 48 | return this; 49 | } 50 | 51 | /// 52 | /// Allow <object>, <embed>, and <applet> 53 | /// sources from anywhere. 54 | /// 55 | /// The builder for call chaining 56 | public CspPluginBuilder FromAnywhere() 57 | { 58 | _objectOptions.AllowAny = true; 59 | return this; 60 | } 61 | 62 | /// 63 | /// Add valid MIME type for plugins invoked via 64 | /// <object>, <embed>, or <applet>. 65 | /// In order to use <applet>, you must add 66 | /// application/x-java-applet. 67 | /// 68 | /// The MIME type to allow. 69 | /// The builder for call chaining 70 | public CspPluginBuilder WithMimeType(string mimeType) 71 | { 72 | _pluginOptions.AllowedMediaTypes.Add(mimeType); 73 | return this; 74 | } 75 | 76 | /// 77 | /// Allow <object>, <embed>, and <applet> 78 | /// sources only over secure connections. 79 | /// 80 | /// The builder for call chaining 81 | public CspPluginBuilder OnlyOverHttps() 82 | { 83 | _objectOptions.AllowOnlyHttps = true; 84 | return this; 85 | } 86 | 87 | public Tuple BuildOptions() 88 | { 89 | return Tuple.Create(_objectOptions, _pluginOptions); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspPrefetchBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy rules 8 | /// related to pre-fetching and pre-rendering content 9 | /// 10 | public class CspPrefetchBuilder 11 | { 12 | private readonly CspPrefetchSrcOptions _options = new CspPrefetchSrcOptions(); 13 | 14 | /// 15 | /// Block all pre-fetching/pre-rendering 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow pre-fetching/pre-rendering 24 | /// from current domain. 25 | /// 26 | /// The builder for call chaining 27 | public CspPrefetchBuilder FromSelf() 28 | { 29 | _options.AllowSelf = true; 30 | return this; 31 | } 32 | 33 | /// 34 | /// Allow pre-fetching/pre-rendering 35 | /// from the given . 36 | /// 37 | /// The URI to allow. 38 | /// The builder for call chaining 39 | public CspPrefetchBuilder From(string uri) 40 | { 41 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 42 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 43 | 44 | _options.AllowedSources.Add(uri); 45 | return this; 46 | } 47 | 48 | /// 49 | /// Allow pre-fetching/pre-rendering 50 | /// from anywhere, except data:, blob:, 51 | /// and filesystem: schemes. 52 | /// 53 | /// The builder for call chaining 54 | public CspPrefetchBuilder FromAnywhere() 55 | { 56 | _options.AllowAny = true; 57 | return this; 58 | } 59 | 60 | /// 61 | /// Allow pre-fetching/pre-rendering 62 | /// only over secure connections. 63 | /// 64 | /// The builder for call chaining 65 | public CspPrefetchBuilder OnlyOverHttps() 66 | { 67 | _options.AllowOnlyHttps = true; 68 | return this; 69 | } 70 | 71 | public CspPrefetchSrcOptions BuildOptions() 72 | { 73 | return _options; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspRequireSriBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 4 | { 5 | public class CspRequireSriBuilder 6 | { 7 | private readonly CspRequireSriOptions _options = new CspRequireSriOptions(); 8 | 9 | /// 10 | /// Require subresource integrity attributes for scripts loaded on this page 11 | /// 12 | public CspRequireSriBuilder ForScripts() 13 | { 14 | _options.ForScripts = true; 15 | return this; 16 | } 17 | 18 | /// 19 | /// Require subresource integrity attributes for styles loaded on this page 20 | /// 21 | public CspRequireSriBuilder ForStyles() 22 | { 23 | _options.ForStyles = true; 24 | return this; 25 | } 26 | 27 | public CspRequireSriOptions BuildOptions() 28 | { 29 | return _options; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspSandboxBuilder.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 4 | { 5 | /// 6 | /// Builder for Content Security Policy rules 7 | /// related to sandbox exceptions. 8 | /// 9 | public class CspSandboxBuilder 10 | { 11 | private readonly CspSandboxOptions _options = new CspSandboxOptions(); 12 | 13 | /// 14 | /// Allow submission of forms. 15 | /// 16 | /// The builder for call chaining 17 | public CspSandboxBuilder AllowForms() 18 | { 19 | _options.AllowForms = true; 20 | return this; 21 | } 22 | 23 | /// 24 | /// Allow execution of JavaScript. 25 | /// 26 | /// The builder for call chaining 27 | public CspSandboxBuilder AllowScripts() 28 | { 29 | _options.AllowScripts = true; 30 | return this; 31 | } 32 | 33 | /// 34 | /// Allow modal windows. 35 | /// 36 | /// The builder for call chaining 37 | public CspSandboxBuilder AllowModals() 38 | { 39 | _options.AllowModals = true; 40 | return this; 41 | } 42 | 43 | /// 44 | /// Allow to disable the ability 45 | /// to lock the screen orientation. 46 | /// 47 | /// The builder for call chaining 48 | public CspSandboxBuilder AllowOrientationLock() 49 | { 50 | _options.AllowOrientationLock = true; 51 | return this; 52 | } 53 | 54 | /// 55 | /// Allow to use the Pointer Lock API. 56 | /// 57 | /// The builder for call chaining 58 | public CspSandboxBuilder AllowPointerLock() 59 | { 60 | _options.AllowPointerLock = true; 61 | return this; 62 | } 63 | 64 | /// 65 | /// Allow pop-ups, e.g. ones created with 66 | /// window.open(). 67 | /// 68 | /// The builder for call chaining 69 | public CspSandboxBuilder AllowPopups() 70 | { 71 | _options.AllowPopups = true; 72 | return this; 73 | } 74 | 75 | /// 76 | /// Allow pop-ups opened to run outside the sandbox. 77 | /// 78 | /// The builder for call chaining 79 | public CspSandboxBuilder AllowPopupsToEscapeSandbox() 80 | { 81 | _options.AllowPopupsToEscapeSandbox = true; 82 | return this; 83 | } 84 | 85 | /// 86 | /// Allow embedders to have control over 87 | /// whether an iframe can start a presentation session. 88 | /// 89 | /// The builder for call chaining 90 | public CspSandboxBuilder AllowPresentation() 91 | { 92 | _options.AllowPresentation = true; 93 | return this; 94 | } 95 | 96 | /// 97 | /// Allow the content to be treated as being from its 98 | /// normal origin. If this exception is not enabled, the 99 | /// embedded content is treated as being from a unique origin. 100 | /// 101 | /// The builder for call chaining 102 | public CspSandboxBuilder AllowSameOrigin() 103 | { 104 | _options.AllowSameOrigin = true; 105 | return this; 106 | } 107 | 108 | /// 109 | /// Allow the embedded browsing context to navigate 110 | /// (load) content to the top-level browsing context. 111 | /// 112 | /// The builder for call chaining 113 | public CspSandboxBuilder AllowTopNavigation() 114 | { 115 | _options.AllowTopNavigation = true; 116 | return this; 117 | } 118 | 119 | public CspSandboxOptions BuildOptions() 120 | { 121 | return _options; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspScriptsBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to JavaScript. 9 | /// 10 | public class CspScriptsBuilder 11 | { 12 | private readonly CspScriptSrcOptions _options = new CspScriptSrcOptions(); 13 | 14 | /// 15 | /// Block all JavaScript. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow JavaScript from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspScriptsBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow JavaScript from the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspScriptsBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow JavaScript with the given 49 | /// . 50 | /// 51 | /// The hash to allow. 52 | /// The builder for call chaining 53 | public CspScriptsBuilder WithHash(string hash) 54 | { 55 | if (hash == null) throw new ArgumentNullException(nameof(hash)); 56 | if (hash.Length == 0) throw new ArgumentException("Hash can't be empty", nameof(hash)); 57 | 58 | _options.AllowedHashes.Add(hash); 59 | return this; 60 | } 61 | 62 | /// 63 | /// Allow inline scripts. 64 | /// If you have an XSS vulnerability, this will allow them to execute. 65 | /// Cannot be enabled together with AddNonce. 66 | /// 67 | /// The builder for call chaining 68 | public CspScriptsBuilder AllowUnsafeInline() 69 | { 70 | _options.AllowUnsafeInline = true; 71 | return this; 72 | } 73 | 74 | /// 75 | /// Allow usage of eval(). Do not enable unless you really 76 | /// need it. 77 | /// 78 | /// The builder for call chaining 79 | public CspScriptsBuilder AllowUnsafeEval() 80 | { 81 | _options.AllowUnsafeEval = true; 82 | return this; 83 | } 84 | 85 | /// 86 | /// Generates a random nonce for each request and 87 | /// adds it to the CSP header. Allows inline scripts that 88 | /// have the nonce attribute set to the random value on 89 | /// the script element. 90 | /// Note that you must call AddCsp() on the service collection 91 | /// in ConfigureServices() to add the necessary dependencies. 92 | /// 93 | /// The builder for call chaining 94 | public CspScriptsBuilder AddNonce() 95 | { 96 | _options.AddNonce = true; 97 | return this; 98 | } 99 | 100 | /// 101 | /// Allow JavaScript from anywhere, except 102 | /// data:, blob:, and filesystem: schemes. 103 | /// 104 | /// The builder for call chaining 105 | public CspScriptsBuilder FromAnywhere() 106 | { 107 | _options.AllowAny = true; 108 | return this; 109 | } 110 | 111 | /// 112 | /// Allow JavaScript only over secure connections. 113 | /// 114 | /// The builder for call chaining 115 | public CspScriptsBuilder OnlyOverHttps() 116 | { 117 | _options.AllowOnlyHttps = true; 118 | return this; 119 | } 120 | 121 | /// 122 | /// Allow scripts that have been loaded with 123 | /// a trusted hash/nonce to load additional 124 | /// scripts. 125 | /// This enabled a "strict" mode 126 | /// for scripts, requiring a hash or nonce 127 | /// on all of them. 128 | /// 129 | /// The builder for call chaining 130 | public CspScriptsBuilder WithStrictDynamic() 131 | { 132 | _options.StrictDynamic = true; 133 | return this; 134 | } 135 | 136 | public CspScriptSrcOptions BuildOptions() 137 | { 138 | return _options; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspStylesBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy 8 | /// rules related to CSS. 9 | /// 10 | public class CspStylesBuilder 11 | { 12 | private readonly CspStyleSrcOptions _options = new CspStyleSrcOptions(); 13 | 14 | /// 15 | /// Block all CSS. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow CSS from current domain. 24 | /// 25 | /// The builder for call chaining 26 | public CspStylesBuilder FromSelf() 27 | { 28 | _options.AllowSelf = true; 29 | return this; 30 | } 31 | 32 | /// 33 | /// Allow CSS from the given 34 | /// . 35 | /// 36 | /// The URI to allow. 37 | /// The builder for call chaining 38 | public CspStylesBuilder From(string uri) 39 | { 40 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 41 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 42 | 43 | _options.AllowedSources.Add(uri); 44 | return this; 45 | } 46 | 47 | /// 48 | /// Allow CSS with the given 49 | /// . 50 | /// 51 | /// The hash to allow. 52 | /// The builder for call chaining 53 | public CspStylesBuilder WithHash(string hash) 54 | { 55 | if (hash == null) throw new ArgumentNullException(nameof(hash)); 56 | if (hash.Length == 0) throw new ArgumentException("Hash can't be empty", nameof(hash)); 57 | 58 | _options.AllowedHashes.Add(hash); 59 | return this; 60 | } 61 | 62 | /// 63 | /// Allow CSS from anywhere, except 64 | /// data:, blob:, and filesystem: schemes. 65 | /// 66 | /// The builder for call chaining 67 | public CspStylesBuilder FromAnywhere() 68 | { 69 | _options.AllowAny = true; 70 | return this; 71 | } 72 | 73 | /// 74 | /// Allow inline CSS. 75 | /// Cannot be enabled together with AddNonce. 76 | /// 77 | /// The builder for call chaining 78 | public CspStylesBuilder AllowUnsafeInline() 79 | { 80 | _options.AllowUnsafeInline = true; 81 | return this; 82 | } 83 | 84 | /// 85 | /// Generates a random nonce for each request and 86 | /// adds it to the CSP header. Allows inline styles that 87 | /// have the nonce attribute set to the random value on 88 | /// the script element. 89 | /// Note that you must call AddCsp() on the service collection 90 | /// in ConfigureServices() to add the necessary dependencies. 91 | /// 92 | /// The builder for call chaining 93 | public CspStylesBuilder AddNonce() 94 | { 95 | _options.AddNonce = true; 96 | return this; 97 | } 98 | 99 | /// 100 | /// Allow CSS only over secure connections. 101 | /// 102 | /// The builder for call chaining 103 | public CspStylesBuilder OnlyOverHttps() 104 | { 105 | _options.AllowOnlyHttps = true; 106 | return this; 107 | } 108 | 109 | public CspStyleSrcOptions BuildOptions() 110 | { 111 | return _options; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Builder/CspWorkerBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Builder 5 | { 6 | /// 7 | /// Builder for Content Security Policy rules 8 | /// related to Worker, SharedWorker and ServiceWorker script sources. 9 | /// 10 | public class CspWorkerBuilder 11 | { 12 | private readonly CspWorkerSrcOptions _options = new CspWorkerSrcOptions(); 13 | 14 | /// 15 | /// Block all Worker, SharedWorker or ServiceWorker sources. 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | /// Allow Worker, SharedWorker or ServiceWorker sources 24 | /// from current domain. 25 | /// 26 | /// The builder for call chaining 27 | public CspWorkerBuilder FromSelf() 28 | { 29 | _options.AllowSelf = true; 30 | return this; 31 | } 32 | 33 | /// 34 | /// Allow Worker, SharedWorker or ServiceWorker sources 35 | /// from the given . 36 | /// 37 | /// The URI to allow. 38 | /// The builder for call chaining 39 | public CspWorkerBuilder From(string uri) 40 | { 41 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 42 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 43 | 44 | _options.AllowedSources.Add(uri); 45 | return this; 46 | } 47 | 48 | /// 49 | /// Allow Worker, SharedWorker or ServiceWorker sources 50 | /// from anywhere, except data:, blob:, 51 | /// and filesystem: schemes. 52 | /// 53 | /// The builder for call chaining 54 | public CspWorkerBuilder FromAnywhere() 55 | { 56 | _options.AllowAny = true; 57 | return this; 58 | } 59 | 60 | /// 61 | /// Allow Worker, SharedWorker or ServiceWorker sources 62 | /// only over secure connections. 63 | /// 64 | /// The builder for call chaining 65 | public CspWorkerBuilder OnlyOverHttps() 66 | { 67 | _options.AllowOnlyHttps = true; 68 | return this; 69 | } 70 | 71 | public CspWorkerSrcOptions BuildOptions() 72 | { 73 | return _options; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp 8 | { 9 | public class CspMiddleware 10 | { 11 | private const string CspHeaderName = "Content-Security-Policy"; 12 | private const string CspReportOnlyHeaderName = "Content-Security-Policy-Report-Only"; 13 | private readonly RequestDelegate _next; 14 | private readonly CspOptions _options; 15 | private readonly string _headerName; 16 | private readonly string _headerValue; 17 | 18 | public CspMiddleware(RequestDelegate next, IOptions options) 19 | { 20 | if (options == null) 21 | { 22 | throw new ArgumentNullException(nameof(options)); 23 | } 24 | 25 | _next = next; 26 | _options = options.Value; 27 | // If a nonce is needed to be generated, we can't cache the header value 28 | if (_options.IsNonceNeeded) 29 | { 30 | _headerName = null; 31 | _headerValue = null; 32 | } 33 | else 34 | { 35 | //If nonces are not needed, we can cache them immediately 36 | var (headerName, headerValue) = _options.ToString(null); 37 | _headerName = headerName; 38 | _headerValue = headerValue; 39 | } 40 | } 41 | 42 | public async Task Invoke(HttpContext context) 43 | { 44 | // Check if a CSP header has already been added to the response 45 | // This can happen for example if a middleware re-executes the pipeline 46 | if (!ContainsCspHeader(context.Response)) 47 | { 48 | var sendingHeaderContext = new CspSendingHeaderContext(context); 49 | //Call the per-request check if CSP should be sent 50 | await _options.OnSendingHeader(sendingHeaderContext); 51 | 52 | if (!sendingHeaderContext.ShouldNotSend) 53 | { 54 | string headerName; 55 | string headerValue; 56 | if (_options.IsNonceNeeded) 57 | { 58 | var nonceService = (ICspNonceService)context.RequestServices.GetService(typeof(ICspNonceService)); 59 | (headerName, headerValue) = _options.ToString(nonceService); 60 | } 61 | else 62 | { 63 | headerName = _headerName; 64 | headerValue = _headerValue; 65 | } 66 | context.Response.Headers.Add(headerName, headerValue); 67 | } 68 | } 69 | 70 | await _next.Invoke(context); 71 | } 72 | 73 | private bool ContainsCspHeader(HttpResponse response) 74 | { 75 | return response.Headers.Any(h => h.Key.Equals(CspHeaderName, StringComparison.OrdinalIgnoreCase) 76 | || h.Key.Equals(CspReportOnlyHeaderName, StringComparison.OrdinalIgnoreCase)); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspNonceService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp 5 | { 6 | public class CspNonceService : ICspNonceService 7 | { 8 | private readonly string _nonce; 9 | 10 | public CspNonceService(int nonceByteAmount = 32) 11 | { 12 | byte[] nonceBytes = new byte[nonceByteAmount]; 13 | using(var rng = RandomNumberGenerator.Create()) 14 | { 15 | rng.GetBytes(nonceBytes); 16 | } 17 | 18 | _nonce = Convert.ToBase64String(nonceBytes); 19 | } 20 | 21 | public string GetNonce() 22 | { 23 | return _nonce; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/CspSendingHeaderContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp 4 | { 5 | public class CspSendingHeaderContext 6 | { 7 | public HttpContext HttpContext { get; } 8 | 9 | /// 10 | /// true iff the CSP header should not be sent, false otherwise. 11 | /// 12 | public bool ShouldNotSend { get; set; } 13 | 14 | public CspSendingHeaderContext(HttpContext httpContext) 15 | { 16 | HttpContext = httpContext; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/ICspNonceService.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp 2 | { 3 | public interface ICspNonceService 4 | { 5 | /// 6 | /// Gets the generated nonce. 7 | /// 8 | /// Nonce generated when the service was initialized. 9 | /// Must be attached to CSP header and any inline style or script 10 | /// you want to use. 11 | string GetNonce(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspBaseUriOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspBaseUriOptions : CspSrcOptionsBase 4 | { 5 | public CspBaseUriOptions() 6 | : base("base-uri") 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspChildSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspChildSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspChildSrcOptions() 6 | : base("child-src") 7 | { 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspConnectSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspConnectSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspConnectSrcOptions() 6 | : base("connect-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspDefaultSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspDefaultSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspDefaultSrcOptions() 6 | : base("default-src") 7 | { 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFontSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspFontSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspFontSrcOptions() 6 | : base("font-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFormActionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspFormActionOptions : CspSrcOptionsBase 4 | { 5 | public CspFormActionOptions() 6 | : base("form-action") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFrameAncestorsOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 4 | { 5 | public class CspFrameAncestorsOptions 6 | { 7 | /// 8 | /// Collection of sources that can embed this app. 9 | /// 10 | public ICollection AllowedSources { get; set; } 11 | /// 12 | /// Allow embedding from the same domain as the app. 13 | /// Equivalent to X-Frame-Options: SAMEORIGIN. 14 | /// 15 | public bool AllowSelf { get; set; } 16 | /// 17 | /// Block iframe embedding. 18 | /// Equivalent to X-Frame-Options: DENY. 19 | /// 20 | public bool AllowNone { get; set; } 21 | /// 22 | /// Allow loading only through secure channels. 23 | /// 24 | public bool AllowOnlyHttps { get; set; } 25 | /// 26 | /// Allows any source. 27 | /// 28 | public bool AllowAny { get; set; } 29 | 30 | public CspFrameAncestorsOptions() 31 | { 32 | AllowedSources = new List(); 33 | } 34 | 35 | public override string ToString() 36 | { 37 | ICollection parts = new List(); 38 | 39 | if (AllowNone) 40 | { 41 | parts.Add("'none'"); 42 | } 43 | else 44 | { 45 | if (AllowAny) 46 | { 47 | parts.Add("*"); 48 | } 49 | if (AllowSelf) 50 | { 51 | parts.Add("'self'"); 52 | } 53 | if (AllowOnlyHttps) 54 | { 55 | parts.Add("https:"); 56 | } 57 | 58 | foreach (string allowedSource in AllowedSources) 59 | { 60 | parts.Add(allowedSource); 61 | } 62 | } 63 | 64 | if(parts.Count == 0) 65 | { 66 | return string.Empty; 67 | } 68 | 69 | return "frame-ancestors " + string.Join(" ", parts); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspFrameSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspFrameSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspFrameSrcOptions() 6 | : base("frame-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspImgSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspImgSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspImgSrcOptions() 6 | : base("img-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspManifestSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspManifestSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspManifestSrcOptions() 6 | : base ("manifest-src") 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspMediaSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspMediaSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspMediaSrcOptions() 6 | : base("media-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspObjectSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspObjectSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspObjectSrcOptions() 6 | : base("object-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspPluginTypesOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 4 | { 5 | public class CspPluginTypesOptions 6 | { 7 | /// 8 | /// Collection of MIME types allowed for loading resources 9 | /// in <object> and <embed> tags. 10 | /// 11 | public ICollection AllowedMediaTypes { get; } 12 | 13 | public CspPluginTypesOptions() 14 | { 15 | AllowedMediaTypes = new List(); 16 | } 17 | 18 | public override string ToString() 19 | { 20 | if (AllowedMediaTypes.Count == 0) 21 | { 22 | return string.Empty; 23 | } 24 | return "plugin-types " + string.Join(" ", AllowedMediaTypes); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspPrefetchSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspPrefetchSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspPrefetchSrcOptions() 6 | : base("prefetch-src") 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspRequireSriOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 4 | { 5 | public class CspRequireSriOptions 6 | { 7 | /// 8 | /// If true subresource integrity attributes will be required for scripts loaded from this page 9 | /// 10 | public bool ForScripts { get; set; } 11 | 12 | /// 13 | /// If true subresource integrity attributes will be required for stylesheets loaded from this page 14 | /// 15 | public bool ForStyles { get; set; } 16 | 17 | /// 18 | public override string ToString() 19 | { 20 | if (!ForScripts && !ForStyles) 21 | { 22 | return string.Empty; 23 | } 24 | 25 | var requiredOn = new List(2); 26 | if (ForScripts) 27 | { 28 | requiredOn.Add("script"); 29 | } 30 | 31 | if (ForStyles) 32 | { 33 | requiredOn.Add("style"); 34 | } 35 | 36 | return $"require-sri-for {string.Join(" ", requiredOn)}"; 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspSandboxOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 4 | { 5 | public class CspSandboxOptions 6 | { 7 | /// 8 | /// Allow page to submit forms. 9 | /// 10 | public bool AllowForms { get; set; } 11 | /// 12 | /// Allow content as being from its normal origin. 13 | /// 14 | public bool AllowSameOrigin { get; set; } 15 | /// 16 | /// Allow content to run scripts, but not create pop-ups. 17 | /// 18 | public bool AllowScripts { get; set; } 19 | /// 20 | /// Allow popups with e.g. window.open(). 21 | /// 22 | public bool AllowPopups { get; set; } 23 | /// 24 | /// Allow modal windows. 25 | /// 26 | public bool AllowModals { get; set; } 27 | /// 28 | /// Allow the page to disable the ability to lock the screen orientation. 29 | /// 30 | public bool AllowOrientationLock { get; set; } 31 | /// 32 | /// Allow the page to use the Pointer Lock API. 33 | /// 34 | public bool AllowPointerLock { get; set; } 35 | /// 36 | /// Allow embedders to have control over whether an iframe can 37 | /// start a presentation session. 38 | /// 39 | public bool AllowPresentation { get; set; } 40 | /// 41 | /// Allow a sandboxed document to open new windows without 42 | /// forcing the sandboxing flags upon them. This will allow, 43 | /// for example, a third-party advertisement to be safely 44 | /// sandboxed without forcing the same restrictions upon a landing page. 45 | /// 46 | public bool AllowPopupsToEscapeSandbox { get; set; } 47 | /// 48 | /// Allows the embedded browsing context to navigate (load) 49 | /// content to the top-level browsing context. 50 | /// 51 | public bool AllowTopNavigation { get; set; } 52 | 53 | public override string ToString() 54 | { 55 | ICollection parts = new List(); 56 | 57 | if (AllowForms) 58 | { 59 | parts.Add("allow-forms"); 60 | } 61 | if (AllowSameOrigin) 62 | { 63 | parts.Add("allow-same-origin"); 64 | } 65 | if (AllowScripts) 66 | { 67 | parts.Add("allow-scripts"); 68 | } 69 | if (AllowPopups) 70 | { 71 | parts.Add("allow-popups"); 72 | } 73 | if (AllowModals) 74 | { 75 | parts.Add("allow-modals"); 76 | } 77 | if (AllowOrientationLock) 78 | { 79 | parts.Add("allow-orientation-lock"); 80 | } 81 | if (AllowPointerLock) 82 | { 83 | parts.Add("allow-pointer-lock"); 84 | } 85 | if (AllowPresentation) 86 | { 87 | parts.Add("allow-presentation"); 88 | } 89 | if (AllowPopupsToEscapeSandbox) 90 | { 91 | parts.Add("allow-popups-to-escape-sandbox"); 92 | } 93 | if (AllowTopNavigation) 94 | { 95 | parts.Add("allow-top-navigation"); 96 | } 97 | 98 | if (parts.Count == 0) 99 | { 100 | return string.Empty; 101 | } 102 | return "sandbox " + string.Join(" ", parts); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspScriptSrcOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 5 | { 6 | public class CspScriptSrcOptions : CspSrcOptionsBase 7 | { 8 | public bool AddNonce { get; set; } 9 | 10 | public bool AllowUnsafeEval { get; set; } 11 | 12 | public bool AllowUnsafeInline { get; set; } 13 | 14 | /// 15 | /// Allow scripts that have been loaded with 16 | /// a trusted hash/nonce to load additional 17 | /// scripts. 18 | /// This enabled a "strict" mode 19 | /// for scripts, requiring a hash or nonce 20 | /// on all of them. 21 | /// 22 | public bool StrictDynamic { get; set; } 23 | 24 | /// 25 | /// Collection of hashes that can be loaded. 26 | /// 27 | public ICollection AllowedHashes { get; set; } 28 | 29 | public CspScriptSrcOptions() 30 | : base("script-src") 31 | { 32 | AllowedHashes = new List(); 33 | } 34 | 35 | protected override ICollection GetParts(ICspNonceService nonceService) 36 | { 37 | ICollection parts = base.GetParts(nonceService); 38 | 39 | foreach (string allowedHash in AllowedHashes) 40 | { 41 | parts.Add($"'{allowedHash}'"); 42 | } 43 | 44 | if (AddNonce) 45 | { 46 | if (nonceService == null) 47 | { 48 | throw new ArgumentNullException( 49 | nameof(nonceService), 50 | "Nonce service was not found, it needs to be added to the service collection"); 51 | } 52 | 53 | parts.Add($"'nonce-{nonceService.GetNonce()}'"); 54 | } 55 | 56 | if (AllowUnsafeEval) 57 | { 58 | parts.Add("'unsafe-eval'"); 59 | } 60 | 61 | if (AllowUnsafeInline) 62 | { 63 | parts.Add("'unsafe-inline'"); 64 | } 65 | 66 | if (StrictDynamic) 67 | { 68 | parts.Add("'strict-dynamic'"); 69 | } 70 | 71 | return parts; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspSrcOptionsBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 4 | { 5 | public abstract class CspSrcOptionsBase 6 | { 7 | private readonly string _directiveName; 8 | 9 | /// 10 | /// Collection of sources from where these resources can be loaded. 11 | /// 12 | public ICollection AllowedSources { get; set; } 13 | 14 | /// 15 | /// Allow loading these resources from the same domain as the app. 16 | /// 17 | public bool AllowSelf { get; set; } 18 | /// 19 | /// Block loading of these resources. 20 | /// 21 | public bool AllowNone { get; set; } 22 | /// 23 | /// Allow loading only through secure channels. 24 | /// 25 | public bool AllowOnlyHttps { get; set; } 26 | /// 27 | /// Allows loading via data scheme, e.g. Base64-encoded images. 28 | /// 29 | public bool AllowDataScheme { get; set; } 30 | /// 31 | /// Allows any source except data:, blob:, and filesystem: schemes. 32 | /// 33 | public bool AllowAny { get; set; } 34 | 35 | protected CspSrcOptionsBase(string directiveName) 36 | { 37 | _directiveName = directiveName + " "; 38 | AllowedSources = new List(); 39 | } 40 | 41 | protected virtual ICollection GetParts(ICspNonceService nonceService) 42 | { 43 | ICollection parts = new List(); 44 | 45 | if (AllowNone) 46 | { 47 | parts.Add("'none'"); 48 | } 49 | else 50 | { 51 | if (AllowAny) 52 | { 53 | parts.Add("*"); 54 | } 55 | if (AllowSelf) 56 | { 57 | parts.Add("'self'"); 58 | } 59 | if (AllowOnlyHttps) 60 | { 61 | parts.Add("https:"); 62 | } 63 | if (AllowDataScheme) 64 | { 65 | parts.Add("data:"); 66 | } 67 | 68 | foreach (string allowedSource in AllowedSources) 69 | { 70 | parts.Add(allowedSource); 71 | } 72 | } 73 | return parts; 74 | } 75 | 76 | public string ToString(ICspNonceService nonceService) 77 | { 78 | ICollection parts = GetParts(nonceService); 79 | 80 | if (parts.Count == 0) 81 | { 82 | return string.Empty; 83 | } 84 | return _directiveName + string.Join(" ", parts); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspStyleSrcOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 5 | { 6 | public class CspStyleSrcOptions : CspSrcOptionsBase 7 | { 8 | public bool AddNonce { get; set; } 9 | 10 | public bool AllowUnsafeInline { get; set; } 11 | 12 | /// 13 | /// Collection of hashes that can be loaded. 14 | /// 15 | public ICollection AllowedHashes { get; set; } 16 | 17 | public CspStyleSrcOptions() 18 | : base("style-src") 19 | { 20 | AllowedHashes = new List(); 21 | } 22 | 23 | protected override ICollection GetParts(ICspNonceService nonceService) 24 | { 25 | ICollection parts = base.GetParts(nonceService); 26 | 27 | foreach (string allowedHash in AllowedHashes) 28 | { 29 | parts.Add($"'{allowedHash}'"); 30 | } 31 | 32 | if (AddNonce) 33 | { 34 | if (nonceService == null) 35 | { 36 | throw new ArgumentNullException( 37 | nameof(nonceService), 38 | "Nonce service was not found, it needs to be added to the service collection"); 39 | } 40 | 41 | parts.Add($"'nonce-{nonceService.GetNonce()}'"); 42 | } 43 | 44 | if (AllowUnsafeInline) 45 | { 46 | parts.Add("'unsafe-inline'"); 47 | } 48 | 49 | return parts; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Csp/Options/CspWorkerSrcOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Csp.Options 2 | { 3 | public class CspWorkerSrcOptions : CspSrcOptionsBase 4 | { 5 | public CspWorkerSrcOptions() 6 | : base("worker-src") 7 | { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Joonasw.AspNetCore.SecurityHeaders 7 | { 8 | internal static class EnumExtensions 9 | { 10 | /// 11 | /// Gets the string value associated with the the corresponding enum option 12 | /// 13 | /// Any Enum where DefaultValue attribute is defined. 14 | /// 15 | internal static string DefaultValue(this Enum enumValue) 16 | { 17 | var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); 18 | 19 | var defaultValueAttribute = fieldInfo 20 | .GetCustomAttributes(typeof(DefaultValueAttribute), inherit: false) 21 | .Cast() 22 | .SingleOrDefault(); 23 | 24 | return defaultValueAttribute?.Value.ToString() ?? enumValue.ToString(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/ExpectCT/ExpectCTMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.ExpectCT 8 | { 9 | public class ExpectCTMiddleware 10 | { 11 | private const string HeaderName = "Expect-CT"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public ExpectCTMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.HeaderValue; 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | //Expect-CT can only be applied to secure requests according to spec 24 | if (context.Request.IsHttps && !ContainsExpectCTHeader(context.Response)) 25 | { 26 | context.Response.Headers.Add(HeaderName, _headerValue); 27 | } 28 | await _next(context); 29 | } 30 | 31 | private static bool ContainsExpectCTHeader(HttpResponse response) 32 | { 33 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/ExpectCT/ExpectCTOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.ExpectCT 5 | { 6 | /// 7 | /// Options for the Expect-CT Header 8 | /// 9 | public class ExpectCTOptions 10 | { 11 | /// 12 | /// Defines the parameters for the Expect-CT header with only the max-age set to 30 days. 13 | /// Please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT for support and more info 14 | /// 15 | public ExpectCTOptions() 16 | { 17 | 18 | } 19 | 20 | /// 21 | /// Defines the parameters for the Expect-CT header sent to clients. 22 | /// Please see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT for support and more info 23 | /// 24 | /// The required max-age directive specifies the number of seconds that 25 | /// the browser should cache and apply the received policy for, whether enforced or report-only. 26 | /// the report-uri directive specifies where the browser should send reports 27 | /// if it does not receive valid CT information. This is specified as an absolute URI. 28 | /// The optional enforce directive controls whether the browser should 29 | /// enforce the policy. Omitting this will treat it as report-only mode. The directive has no value so you simply 30 | /// include it or not depending on whether or not you want the browser to enforce the policy. 31 | public ExpectCTOptions(TimeSpan maxAge, string reportUri, bool enforce = false) 32 | { 33 | MaxAge = maxAge; 34 | Enforce = enforce; 35 | ReportUri = reportUri; 36 | } 37 | 38 | /// 39 | /// Specifies the TimeSpan from seconds after reception of the Expect-CT header field 40 | /// during which the user agent should regard the host from whom the message was 41 | /// received as a known Expect-CT host. 42 | /// 43 | public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(30); 44 | 45 | /// 46 | /// Specifies the number of seconds after reception of the Expect-CT header field 47 | /// during which the user agent should regard the host from whom the message was 48 | /// received as a known Expect-CT host. 49 | /// 50 | public int DurationSeconds => (int)MaxAge.TotalSeconds; 51 | 52 | /// 53 | /// If true, browsers will enforce the Certificate Transparency policy and refuse future connections that violate the policy. 54 | /// 55 | public bool Enforce { get; set; } 56 | 57 | /// 58 | /// The URL where violation reports should be sent. 59 | /// 60 | public string ReportUri { get; set; } 61 | 62 | public string HeaderValue 63 | { 64 | get 65 | { 66 | var values = new List(3) 67 | { 68 | "max-age=" + DurationSeconds 69 | }; 70 | 71 | if (Enforce) 72 | { 73 | values.Add("enforce"); 74 | } 75 | 76 | if (ReportUri != null) 77 | { 78 | values.Add($"report-uri=\"{ReportUri}\""); 79 | } 80 | 81 | return string.Join(", ", values); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/FeaturePolicyFeatureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder 5 | { 6 | public class FeaturePolicyFeatureBuilder : IFeaturePolicyFeatureBuilder> 7 | where TOptions : FeaturePolicyOptionsBase, new() 8 | { 9 | private readonly FeaturePolicyOptionsBase _options; 10 | 11 | public FeaturePolicyFeatureBuilder() 12 | { 13 | _options = new TOptions(); 14 | } 15 | 16 | /// 17 | public void FromNowhere() 18 | { 19 | _options.AllowNone = true; 20 | } 21 | 22 | /// 23 | public FeaturePolicyFeatureBuilder FromSelf() 24 | { 25 | _options.AllowSelf = true; 26 | return this; 27 | } 28 | 29 | /// 30 | public FeaturePolicyFeatureBuilder FromAnywhere() 31 | { 32 | _options.AllowAny = true; 33 | return this; 34 | } 35 | 36 | /// 37 | public FeaturePolicyFeatureBuilder From(string uri) 38 | { 39 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 40 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 41 | 42 | _options.AllowedOrigins.Add(uri); 43 | return this; 44 | } 45 | 46 | internal FeaturePolicyOptionsBase BuildOptions() 47 | { 48 | return _options; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/FeaturePolicyOtherFeatureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder 5 | { 6 | public class FeaturePolicyOtherFeatureBuilder : IFeaturePolicyFeatureBuilder 7 | { 8 | private readonly FeaturePolicyOtherFeatureOptions _options; 9 | 10 | public FeaturePolicyOtherFeatureBuilder(FeaturePolicyOtherFeatureOptions options) 11 | { 12 | _options = options; 13 | } 14 | 15 | public void FromNowhere() 16 | { 17 | _options.AllowNone = true; 18 | } 19 | 20 | public FeaturePolicyOtherFeatureBuilder FromSelf() 21 | { 22 | _options.AllowSelf = true; 23 | return this; 24 | } 25 | 26 | public FeaturePolicyOtherFeatureBuilder FromAnywhere() 27 | { 28 | _options.AllowAny = true; 29 | return this; 30 | } 31 | 32 | public FeaturePolicyOtherFeatureBuilder From(string uri) 33 | { 34 | if (uri == null) throw new ArgumentNullException(nameof(uri)); 35 | if (uri.Length == 0) throw new ArgumentException("Uri can't be empty", nameof(uri)); 36 | 37 | _options.AllowedOrigins.Add(uri); 38 | return this; 39 | } 40 | 41 | internal FeaturePolicyOptionsBase BuildOptions() 42 | { 43 | return _options; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Builder/IFeaturePolicyFeatureBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder 2 | { 3 | internal interface IFeaturePolicyFeatureBuilder 4 | { 5 | /// 6 | /// Block access to feature from all domains. 7 | /// 8 | void FromNowhere(); 9 | /// 10 | /// Allowed from current domain. 11 | /// 12 | /// 13 | T FromSelf(); 14 | /// 15 | /// Allowed from any domain. 16 | /// 17 | /// 18 | T FromAnywhere(); 19 | /// 20 | /// Allowed from the given 21 | /// . 22 | /// 23 | /// The URI to allow. 24 | /// The builder for call chaining 25 | T From(string uri); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/FeaturePolicyMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy 8 | { 9 | public class FeaturePolicyMiddleware 10 | { 11 | private const string HeaderName = "Feature-Policy"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public FeaturePolicyMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | if (options == null) 18 | { 19 | throw new ArgumentNullException(nameof(options)); 20 | } 21 | 22 | _next = next; 23 | _headerValue = options.Value.ToString(); 24 | } 25 | 26 | public async Task Invoke(HttpContext context) 27 | { 28 | // Check if a Feature Policy header has already been added to the response 29 | // This can happen for example if a middleware re-executes the pipeline 30 | if (!ContainsFeaturePolicyHeader(context.Response)) 31 | { 32 | context.Response.Headers.Add(HeaderName, _headerValue); 33 | } 34 | 35 | await _next.Invoke(context); 36 | } 37 | 38 | private static bool ContainsFeaturePolicyHeader(HttpResponse response) 39 | { 40 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAccelerometerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyAccelerometerOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyAccelerometerOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Accelerometer.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAmbientLightSensorOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyAmbientLightSensorOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyAmbientLightSensorOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.AmbientLightSensor.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyAutoplayOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyAutoplayOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyAutoplayOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Autoplay.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyCameraOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyCameraOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyCameraOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Camera.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyEncryptedMediaOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyEncryptedMediaOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyEncryptedMediaOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.EncryptedMedia.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyFullscreenOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyFullscreenOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyFullscreenOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Fullscreen.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyGeolocationOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyGeolocationOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyGeolocationOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Geolocation.DefaultValue(); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyGyroscopeOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyGyroscopeOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyGyroscopeOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Gyroscope.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMagnetometerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyMagnetometerOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyMagnetometerOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Magnetometer.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMicrophoneOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyMicrophoneOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyMicrophoneOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Microphone.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyMidiOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyMidiOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyMidiOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Midi.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyNotificationsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyNotificationsOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyNotificationsOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Notifications.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyOptionsBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 4 | { 5 | public class FeaturePolicyOptionsBase 6 | { 7 | public string FeatureName { get; set; } 8 | /// 9 | /// Collection of origins from where these resources can be loaded. 10 | /// 11 | public ICollection AllowedOrigins { get; set; } 12 | /// 13 | /// Allow loading these resources from the same domain as the app. 14 | /// 15 | public bool AllowSelf { get; set; } 16 | /// 17 | /// Block loading of these resources. 18 | /// 19 | public bool AllowNone { get; set; } 20 | /// 21 | /// Allows any source except data:, blob:, and filesystem: schemes. 22 | /// 23 | public bool AllowAny { get; set; } 24 | 25 | public FeaturePolicyOptionsBase() 26 | { 27 | AllowedOrigins = new List(); 28 | } 29 | 30 | //protected FeaturePolicyOptionsBase(string featureName) 31 | //{ 32 | // FeatureName = featureName; 33 | // AllowedOrigins = new List(); 34 | //} 35 | 36 | protected virtual ICollection GetParts() 37 | { 38 | ICollection parts = new List(); 39 | 40 | if (AllowNone) 41 | { 42 | parts.Add("'none'"); 43 | } 44 | else 45 | { 46 | if (AllowAny) 47 | { 48 | parts.Add("*"); 49 | } 50 | if (AllowSelf) 51 | { 52 | parts.Add("'self'"); 53 | } 54 | if (AllowedOrigins.Count > 0) 55 | { 56 | parts.Add(string.Join(" ", AllowedOrigins)); 57 | } 58 | } 59 | return parts; 60 | } 61 | 62 | public override string ToString() 63 | { 64 | var parts = GetParts(); 65 | 66 | return parts.Count == 0 ? string.Empty : $"{FeatureName} {string.Join(" ", parts)}"; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyOtherFeatureOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyOtherFeatureOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyOtherFeatureOptions(string featureName) 6 | { 7 | FeatureName = featureName; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPaymentOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyPaymentOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyPaymentOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Payment.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPictureInPictureOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyPictureInPictureOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyPictureInPictureOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.PictureInPicture.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyPushOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyPushOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyPushOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Push.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicySpeakerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicySpeakerOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicySpeakerOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Speaker.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicySyncXhrOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicySyncXhrOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicySyncXhrOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.SyncXhr.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyUsbOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyUsbOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyUsbOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Usb.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyVibrateOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyVibrateOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyVibrateOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Vibrate.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/FeaturePolicy/Options/FeaturePolicyVrOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options 2 | { 3 | public class FeaturePolicyVrOptions : FeaturePolicyOptionsBase 4 | { 5 | public FeaturePolicyVrOptions() 6 | { 7 | FeatureName = FeaturePolicyOptions.FeaturePolicyValue.Vr.DefaultValue(); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Hpkp/Builder/HpkpBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Hpkp.Builder 2 | { 3 | /// 4 | /// Builder for building HTTP 5 | /// Public Key Pins rules. 6 | /// 7 | public class HpkpBuilder 8 | { 9 | private readonly HpkpOptions _options = new HpkpOptions(); 10 | 11 | /// 12 | /// Sets how long the public keys will be cached 13 | /// in users' browsers in seconds. Decide this carefully, 14 | /// as the rule is quite hard for normal users to remove 15 | /// from cache if something were to go wrong during 16 | /// this period. 17 | /// 18 | /// The caching duration in seconds. 19 | /// The builder for call chaining 20 | public HpkpBuilder UseMaxAgeSeconds(int seconds) 21 | { 22 | _options.MaxAgeSeconds = seconds; 23 | return this; 24 | } 25 | 26 | /// 27 | /// Adds a given SHA-256 public key hash to the header. 28 | /// The hash can be gotten from any certificate in the 29 | /// certificate chain for your certificate. This way 30 | /// you can either pin your specific certificate, a middle 31 | /// certificate, or even the root certificate. 32 | /// 33 | /// The SHA-256 hash of one of the certificates thumbprints. 34 | /// The builder for call chaining 35 | public HpkpBuilder AddSha256Pin(string pin) 36 | { 37 | _options.Pins.Add("pin-sha256=\"" + pin + "\""); 38 | return this; 39 | } 40 | 41 | /// 42 | /// Enables this rule for all subdomains of the 43 | /// current domain as well. Be very careful with 44 | /// this rule, as you must be sure the given keys 45 | /// will be used on all future subdomains. 46 | /// 47 | /// The builder for call chaining 48 | public HpkpBuilder IncludeSubDomains() 49 | { 50 | _options.IncludeSubDomains = true; 51 | return this; 52 | } 53 | 54 | /// 55 | /// Sets the pinning to report-only mode. 56 | /// Invalid certificates are not blocked, but 57 | /// you will receive reports for violations. 58 | /// Excellent for testing settings. 59 | /// 60 | /// The builder for call chaining 61 | public HpkpBuilder SetReportOnly() 62 | { 63 | _options.ReportOnly = true; 64 | return this; 65 | } 66 | 67 | /// 68 | /// The URI to where violations should be reported to. 69 | /// Can be a relative or absolute URL. 70 | /// 71 | /// The URL where violations are reported to. 72 | /// The builder for call chaining 73 | public HpkpBuilder ReportViolationsTo(string uri) 74 | { 75 | _options.ReportUri = uri; 76 | return this; 77 | } 78 | 79 | public HpkpOptions BuildHpkpOptions() 80 | { 81 | return _options; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Hpkp/HpkpMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Hpkp 8 | { 9 | public class HpkpMiddleware 10 | { 11 | private readonly RequestDelegate _next; 12 | private readonly string _headerName; 13 | private readonly string _headerValue; 14 | 15 | public HpkpMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerName = options.Value.HeaderName; 19 | _headerValue = options.Value.HeaderValue; 20 | } 21 | 22 | public async Task Invoke(HttpContext context) 23 | { 24 | if (!ContainsHpkpHeader(context.Response)) 25 | { 26 | context.Response.Headers.Add(_headerName, _headerValue); 27 | } 28 | await _next(context); 29 | } 30 | 31 | private bool ContainsHpkpHeader(HttpResponse response) 32 | { 33 | return response.Headers.Any(h => h.Key.Equals(HpkpOptions.BlockingHeaderName, StringComparison.OrdinalIgnoreCase) 34 | || h.Key.Equals(HpkpOptions.ReportOnlyHeaderName, StringComparison.OrdinalIgnoreCase)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/HpkpOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders 4 | { 5 | public class HpkpOptions 6 | { 7 | public const string ReportOnlyHeaderName = "Public-Key-Pins-Report-Only"; 8 | public const string BlockingHeaderName = "Public-Key-Pins"; 9 | public int MaxAgeSeconds { get; set; } 10 | public IList Pins { get; set; } = new List(); 11 | public bool IncludeSubDomains { get; set; } 12 | public bool ReportOnly { get; set; } 13 | public string ReportUri { get; set; } 14 | 15 | public string HeaderName => ReportOnly ? ReportOnlyHeaderName : BlockingHeaderName; 16 | 17 | public string HeaderValue 18 | { 19 | get 20 | { 21 | string value = string.Join(";", Pins); 22 | 23 | value += ";" + "max-age=" + MaxAgeSeconds; 24 | 25 | if (IncludeSubDomains) 26 | { 27 | value += "; includeSubDomains"; 28 | } 29 | 30 | if (ReportUri != null) 31 | { 32 | value += "; report-uri=\"" + ReportUri + "\""; 33 | } 34 | 35 | return value; 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Hsts/HstsMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Hsts 8 | { 9 | public class HstsMiddleware 10 | { 11 | private const string HeaderName = "Strict-Transport-Security"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public HstsMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.BuildHeaderValue(); 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | //HSTS can only be applied to secure requests according to spec 24 | // there really is no point adding it to insecure ones since MiTM can just strip the header 25 | if (context.Request.IsHttps && !ContainsHstsHeader(context.Response)) 26 | { 27 | context.Response.Headers.Add(HeaderName, _headerValue); 28 | } 29 | await _next(context); 30 | } 31 | 32 | private bool ContainsHstsHeader(HttpResponse response) 33 | { 34 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Hsts/HstsOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Hsts 4 | { 5 | internal static class HstsOptionsExtensions 6 | { 7 | public static string BuildHeaderValue(this HstsOptions options) 8 | { 9 | if (options == null) 10 | { 11 | throw new ArgumentNullException(nameof(options)); 12 | } 13 | if (options.DurationSeconds <= 0) 14 | { 15 | throw new ArgumentOutOfRangeException(nameof(options.DurationSeconds), "HSTS duration must be positive"); 16 | } 17 | 18 | string headerValue = "max-age=" + options.DurationSeconds; 19 | if (options.IncludeSubDomains) 20 | { 21 | headerValue += "; includeSubDomains"; 22 | } 23 | if (options.Preload) 24 | { 25 | headerValue += "; preload"; 26 | } 27 | return headerValue; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/HstsOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders 4 | { 5 | /// 6 | /// Options for the HTTP Strict Transport Security header. 7 | /// 8 | public class HstsOptions 9 | { 10 | public HstsOptions() 11 | { 12 | 13 | } 14 | 15 | /// 16 | /// Defines the parameters for the HSTS header sent 17 | /// to clients. 18 | /// 19 | /// The amount of time the clients should remember that 20 | /// this domain should only be accessed over HTTPS. 21 | /// If true, clients will also apply the rule on 22 | /// any subdomains of the current domain. Enable this only if you know what you are doing. 23 | /// False by default. 24 | /// If true, allows this rule to be built into browsers, 25 | /// preventing the first insecure connection. Enable this only if you know what you are doing. 26 | /// False by default. 27 | public HstsOptions(TimeSpan duration, bool includeSubDomains = false, bool preload = false) 28 | { 29 | Duration = duration; 30 | IncludeSubDomains = includeSubDomains; 31 | Preload = preload; 32 | } 33 | 34 | /// 35 | /// Gets the duration browsers should remember that 36 | /// this domain should only be accessed over HTTPS. 37 | /// 38 | public TimeSpan Duration { get; set; } = TimeSpan.FromDays(30); 39 | 40 | /// 41 | /// Gets the duration browsers should remember 42 | /// this domain should only be accessed over HTTPS, 43 | /// in seconds. 44 | /// 45 | public int DurationSeconds => (int)Duration.TotalSeconds; 46 | 47 | /// 48 | /// Gets if this rule also applies to any subdomains. 49 | /// 50 | public bool IncludeSubDomains { get; set; } 51 | 52 | /// 53 | /// Gets if this domain should be allowed to be 54 | /// added to preload lists in browsers. 55 | /// 56 | public bool Preload { get; set; } 57 | } 58 | } -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/Joonasw.AspNetCore.SecurityHeaders.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0;net9.0 4 | 6.0.0 5 | Joonasw.AspNetCore.SecurityHeaders 6 | Joonas Westlin 7 | Middleware for adding security headers to an ASP.NET Core application. Allows you to easily add Content Security Policy, Strict Transport Security, and Public Key Pins to an app. 8 | Copyright 2025 Joonas Westlin 9 | True 10 | LICENSE.txt 11 | https://github.com/juunas11/aspnetcore-security-headers 12 | Remove support for .NET 6 and 7, add support for 8 and 9. 13 | aspnetcore security headers hsts hpkp csp 14 | README.md 15 | https://github.com/juunas11/aspnetcore-security-headers 16 | 17 | true 18 | true 19 | true 20 | snupkg 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/ReferrerPolicy/ReferrerPolicyMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.ReferrerPolicy 8 | { 9 | public class ReferrerPolicyMiddleware 10 | { 11 | private const string HeaderName = "Referrer-Policy"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public ReferrerPolicyMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.PolicyValue.DefaultValue(); 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | if (!ContainsReferrerPolicyHeader(context.Response)) 24 | { 25 | context.Response.Headers.Add(HeaderName, _headerValue); 26 | } 27 | await _next(context); 28 | } 29 | 30 | private static bool ContainsReferrerPolicyHeader(HttpResponse response) 31 | { 32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/ReferrerPolicyOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders 4 | { 5 | public class ReferrerPolicyOptions 6 | { 7 | /// 8 | /// Defines the parameters for the Referrer Policy header with the 'no-referrer' option set 9 | /// 10 | public ReferrerPolicyOptions() 11 | { 12 | 13 | } 14 | 15 | /// 16 | /// Defines the parameters for the Referrer Policy header sent in the response to the client 17 | /// 18 | /// The value to be applied to the Referrer Policy header 19 | public ReferrerPolicyOptions(ReferrerPolicyValue value) 20 | { 21 | PolicyValue = value; 22 | } 23 | 24 | /// 25 | /// Gets the referrer policy value to be applied to the ReferrerPolicy header 26 | /// no-referrer is set by default 27 | /// 28 | public ReferrerPolicyValue PolicyValue { get; set; } = ReferrerPolicyValue.NoReferrer; 29 | 30 | public enum ReferrerPolicyValue 31 | { 32 | [DefaultValue("")] 33 | None = 0, 34 | [DefaultValue("no-referrer")] 35 | NoReferrer = 1, 36 | [DefaultValue("no-referrer-when-downgrade")] 37 | NoReferrerWhenDowngrade = 2, 38 | [DefaultValue("same-origin")] 39 | SameOrigin = 3, 40 | [DefaultValue("origin")] 41 | Origin = 4, 42 | [DefaultValue("strict-origin")] 43 | StrictOrigin = 5, 44 | [DefaultValue("origin-when-cross-origin")] 45 | OriginWhenCrossOrigin = 6, 46 | [DefaultValue("strict-origin-when-cross-origin")] 47 | StrictOriginWhenCrossOrigin = 7, 48 | [DefaultValue("unsafe-url")] 49 | UnsafeUrl = 8 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders 5 | { 6 | public static class ServiceCollectionExtensions 7 | { 8 | /// 9 | /// Adds dependencies required for adding 10 | /// nonces to Content Security Policy headers 11 | /// and inline scripts and styles. 12 | /// 13 | /// The 14 | /// Length of the nonces to generate in bytes. 15 | /// The 16 | public static IServiceCollection AddCsp(this IServiceCollection services, int nonceByteAmount = 32) 17 | { 18 | return services.AddScoped(svcProvider => new CspNonceService(nonceByteAmount)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/TagHelpers/NonceTagHelper.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp; 2 | using Microsoft.AspNetCore.Razor.TagHelpers; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.TagHelpers 5 | { 6 | /// 7 | /// Tag helper for adding a nonce to 8 | /// inline scripts and styles. 9 | /// 10 | [HtmlTargetElement("script", Attributes = "asp-add-nonce")] 11 | [HtmlTargetElement("style", Attributes = "asp-add-nonce")] 12 | public class NonceTagHelper : TagHelper 13 | { 14 | private readonly ICspNonceService _nonceService; 15 | [HtmlAttributeName("asp-add-nonce")] 16 | public bool AddNonce { get; set; } 17 | 18 | public NonceTagHelper(ICspNonceService nonceService) 19 | { 20 | _nonceService = nonceService; 21 | } 22 | 23 | public override void Process(TagHelperContext context, TagHelperOutput output) 24 | { 25 | if (AddNonce) 26 | { 27 | // The nonce service is created per request, so we 28 | // get the same nonce here as the CSP header 29 | output.Attributes.Add("nonce", _nonceService.GetNonce()); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XContentTypeOptions/XContentTypeOptionsMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.XContentTypeOptions 8 | { 9 | public class XContentTypeOptionsMiddleware 10 | { 11 | private const string HeaderName = "X-Content-Type-Options"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public XContentTypeOptionsMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.AllowSniffing ? string.Empty : "nosniff"; 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | // Let's just not bother with adding the header if they want to allow sniffing 24 | if (!string.IsNullOrWhiteSpace(_headerValue) && !ContainsXContentTypeOptionsHeader(context.Response)) 25 | { 26 | context.Response.Headers.Add(HeaderName, _headerValue); 27 | } 28 | await _next(context); 29 | } 30 | 31 | private static bool ContainsXContentTypeOptionsHeader(HttpResponse response) 32 | { 33 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XContentTypeOptionsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders 2 | { 3 | public class XContentTypeOptionsOptions 4 | { 5 | /// 6 | /// Defines the X-Content-Type-Options header with the 'nosniff' option 7 | /// 8 | public XContentTypeOptionsOptions() 9 | : this(false) 10 | { 11 | 12 | } 13 | 14 | /// 15 | /// Defines the X-Content-Type-Options header and whether to allow sniffing 16 | /// 17 | /// 18 | public XContentTypeOptionsOptions(bool allowSniffing) 19 | { 20 | AllowSniffing = allowSniffing; 21 | } 22 | 23 | /// 24 | /// Gets the value to allow sniffing on your site 25 | /// 26 | public bool AllowSniffing { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptions/XFrameOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.XFrameOptions 4 | { 5 | internal static class XFrameOptionsExtensions 6 | { 7 | public static string BuildHeaderValue(this XFrameOptionsOptions options) 8 | { 9 | if (options == null) 10 | { 11 | throw new ArgumentNullException(nameof(options)); 12 | } 13 | var headerValue = options.HeaderValue.DefaultValue(); 14 | if (options.HeaderValue == XFrameOptionsOptions.XFrameOptionsValues.AllowFrom) 15 | { 16 | headerValue += " " + options.AllowFromUrl; 17 | } 18 | 19 | return headerValue; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptions/XFrameOptionsMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.XFrameOptions 8 | { 9 | public class XFrameOptionsMiddleware 10 | { 11 | private const string HeaderName = "X-Frame-Options"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public XFrameOptionsMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.BuildHeaderValue(); 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | if (!ContainsXFrameOptionsHeader(context.Response)) 24 | { 25 | context.Response.Headers.Add(HeaderName, _headerValue); 26 | } 27 | await _next(context); 28 | } 29 | 30 | private static bool ContainsXFrameOptionsHeader(HttpResponse response) 31 | { 32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XFrameOptionsOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders 5 | { 6 | public class XFrameOptionsOptions 7 | { 8 | /// 9 | /// Defines the parameters for the X-Frame-Options header with the 'deny' option set 10 | /// 11 | public XFrameOptionsOptions() 12 | : this(XFrameOptionsValues.Deny) 13 | { 14 | 15 | } 16 | 17 | /// 18 | /// Defines the parameters for the X-Frame-Options header sent in response to clients 19 | /// 20 | /// The Value to be applied to the header X-Frame-Options header 21 | /// If ALLOW-FROM is selected, then this value is 22 | /// required and must have sited that are permitted to frame your site. 23 | /// Note: Chrome does not support the ALLOW-FROM option 24 | /// Null by default 25 | public XFrameOptionsOptions(XFrameOptionsValues value, string allowFromUrl = null) 26 | { 27 | HeaderValue = value; 28 | 29 | if (value == XFrameOptionsValues.AllowFrom && string.IsNullOrWhiteSpace(allowFromUrl)) 30 | { 31 | throw new ArgumentException("ALLOW-FROM URL string cannot be empty when ALLOW-FROM option is selected."); 32 | } 33 | 34 | AllowFromUrl = allowFromUrl; 35 | } 36 | 37 | /// 38 | /// Gets the predefined value to be applied to the header X-Frame-Options header. 39 | /// DENY is set by default. 40 | /// Note: Chrome does not support the ALLOW-FROM option 41 | /// 42 | public XFrameOptionsValues HeaderValue { get; set; } 43 | 44 | /// 45 | /// Gets the url allowed from a single domain. 46 | /// Note: Chrome does not support the ALLOW-FROM option 47 | /// 48 | public string AllowFromUrl { get; set; } 49 | 50 | public enum XFrameOptionsValues 51 | { 52 | [DefaultValue("DENY")] 53 | Deny = 0, 54 | [DefaultValue("SAMEORIGIN")] 55 | SameOrigin = 1, 56 | [DefaultValue("ALLOW-FROM")] 57 | AllowFrom = 2 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XXssProtection/XXssProtectionMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.XXssProtection 8 | { 9 | public class XXssProtectionMiddleware 10 | { 11 | private const string HeaderName = "X-XSS-Protection"; 12 | private readonly RequestDelegate _next; 13 | private readonly string _headerValue; 14 | 15 | public XXssProtectionMiddleware(RequestDelegate next, IOptions options) 16 | { 17 | _next = next; 18 | _headerValue = options.Value.BuildHeaderValue(); 19 | } 20 | 21 | public async Task Invoke(HttpContext context) 22 | { 23 | if (!ContainsXXssProtectionHeader(context.Response)) 24 | { 25 | context.Response.Headers.Add(HeaderName, _headerValue); 26 | } 27 | await _next(context); 28 | } 29 | 30 | private static bool ContainsXXssProtectionHeader(HttpResponse response) 31 | { 32 | return response.Headers.Any(h => h.Key.Equals(HeaderName, StringComparison.OrdinalIgnoreCase)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Joonasw.AspNetCore.SecurityHeaders/XXssProtectionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders 2 | { 3 | public class XXssProtectionOptions 4 | { 5 | /// 6 | /// Defines the parameters for the X-Xss-Protection header with protection and block enabled 7 | /// 8 | public XXssProtectionOptions() 9 | : this(true, true) 10 | { 11 | 12 | } 13 | 14 | /// 15 | /// Defines the parameters for the X-Xss-Protection header sent in response to clients 16 | /// 17 | /// Enable protection for this host 18 | /// Block the response if it detects an attack rather than sanitising the script 19 | public XXssProtectionOptions(bool enableProtection, bool enableAttackBlock) 20 | { 21 | EnableProtection = enableProtection; 22 | EnableAttackBlock = enableAttackBlock; 23 | } 24 | 25 | /// 26 | /// Get the Enable Protection value for enabling or disabling Protection 27 | /// Enabled by default. 28 | /// 29 | public bool EnableProtection { get; set; } 30 | 31 | /// 32 | /// Gets the Enable Block value to block the response if it detects an attack rather than sanitising the script 33 | /// Enabled by default. 34 | /// 35 | public bool EnableAttackBlock { get; set; } 36 | 37 | public string BuildHeaderValue() 38 | { 39 | var headerValue = EnableProtection ? "1" : "0"; 40 | if (EnableAttackBlock) 41 | { 42 | headerValue += "; mode=block"; 43 | } 44 | 45 | return headerValue; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "wwwroot/lib" 3 | } 4 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | 12 | public IActionResult About() 13 | { 14 | ViewData["Message"] = "Your application description page."; 15 | return View(); 16 | } 17 | 18 | public IActionResult Contact() 19 | { 20 | ViewData["Message"] = "Your contact page."; 21 | 22 | return View(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Controllers/ReportController.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Samples.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Controllers 5 | { 6 | public class ReportController : ControllerBase 7 | { 8 | public IActionResult Csp(CspViolationReport report) 9 | { 10 | return Ok(); 11 | } 12 | 13 | public IActionResult Hpkp(HpkpViolationReport report) 14 | { 15 | return Ok(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Joonasw.AspNetCore.SecurityHeaders.Samples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 33ce3d09-4099-4a8e-93af-049b12b6eb90 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Middleware/AppBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Middleware 5 | { 6 | public static class AppBuilderExtensions 7 | { 8 | public static IApplicationBuilder UseHttpsEnforcement(this IApplicationBuilder app) 9 | { 10 | if (app == null) 11 | { 12 | throw new ArgumentNullException(nameof(app)); 13 | } 14 | return app.UseMiddleware(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Middleware/EnforceHttpsMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Middleware 5 | { 6 | public class EnforceHttpsMiddleware 7 | { 8 | private readonly RequestDelegate _next; 9 | 10 | public EnforceHttpsMiddleware(RequestDelegate next) 11 | { 12 | _next = next; 13 | } 14 | 15 | public async Task Invoke(HttpContext context) 16 | { 17 | HttpRequest req = context.Request; 18 | if (req.IsHttps == false) 19 | { 20 | string url = "https://" + req.Host + req.Path + req.QueryString; 21 | context.Response.Redirect(url, permanent: true); 22 | } 23 | else 24 | { 25 | await _next(context); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Models/CspViolationReport.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Models 2 | { 3 | public class CspViolationReport 4 | { 5 | /* JSON format: 6 | * 7 | * {"csp-report": 8 | * {"document-uri":"http://localhost:15490/", 9 | * "referrer":"", 10 | * "violated-directive":"connect-src 'self' https:", 11 | * "effective-directive":"connect-src", 12 | * "original-policy":"default-src https:;script-src 'self' https: ajax.aspnetcdn.com 'nonce-ZlcKG0OPAdcghg6c1EJpFOPbCHQKv6IsZRKZbBNaakE=';style-src 'self' https: ajax.aspnetcdn.com 'nonce-ZlcKG0OPAdcghg6c1EJpFOPbCHQKv6IsZRKZbBNaakE=';child-src 'none';connect-src 'self' https:;font-src 'self' https:;media-src 'none';object-src 'none';frame-ancestors 'none';report-uri /csp-report", 13 | * "blocked-uri":"http://localhost:1591/2be9ad3d3c2e4eb3aee5c0f7b76f8714/browserLinkSignalR/negotiate?requestUrl=http%3A%2F%2Flocalhost%3A15490%2F&browserName=&userAgent=Mozilla%2F5.0+(Windows+NT+10.0%3B+Win64%3B+x64)+AppleWebKit%2F537.36+(KHTML%2C+like+Gecko)+Chrome%2F55.0.2883.87+Safari%2F537.36&clientProtocol=1.3&_=1485109104042", 14 | * "line-number":37, 15 | * "column-number":85935, 16 | * "source-file":"http://localhost:1591/2be9ad3d3c2e4eb3aee5c0f7b76f8714/browserLink", 17 | * "status-code":200 18 | * } 19 | * } 20 | */ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Models/HpkpViolationReport.cs: -------------------------------------------------------------------------------- 1 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples.Models 2 | { 3 | public class HpkpViolationReport 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Samples 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "https://localhost:44342/", 7 | "sslPort": 44342 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Samples": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:15491" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About"; 3 | } 4 |

@ViewData["Title"].

5 |

@ViewData["Message"]

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Contact"; 3 | } 4 |

@ViewData["Title"].

5 |

@ViewData["Message"]

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
18 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/ApplicationError.cshtml: -------------------------------------------------------------------------------- 1 | 

2 | Error page 3 |

-------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 |

Development Mode

9 |

10 | Swapping to Development environment will display more detailed information about the error that occurred. 11 |

12 |

13 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 14 |

15 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | @**@ 5 | 6 | 7 | @ViewData["Title"] - Sample App 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 27 | 30 | 31 | 32 | 52 |
53 | @RenderBody() 54 |
55 |
56 |

© 2018 - WebApplication1

57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | @RenderSection("scripts", required: false) 76 | 77 | 78 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 2 | @addTagHelper *, Joonasw.AspNetCore.SecurityHeaders -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "Hsts": { 11 | "Duration": "30:00:00:00", 12 | "IncludeSubDomains": false, 13 | "Preload": false 14 | }, 15 | "Csp": { 16 | "ReportOnly": true, 17 | "ReportUri": "/csp-report", 18 | "UpgradeInsecureRequests": true, 19 | "BlockAllMixedContent": true, //Ignored if UpgradeInsecureRequests is true 20 | "Default": { 21 | "AllowedSources": [ 22 | "'none'" 23 | ] 24 | }, 25 | "Script": { 26 | "AddNonce": true, 27 | "AllowUnsafeEval": false, 28 | "AllowUnsafeInline": false, 29 | "AllowedSources": [ 30 | "'self'", 31 | "localhost:1591", 32 | "localhost:44342", 33 | "ajax.aspnetcdn.com" 34 | ], 35 | "AllowedHashes": [ 36 | "sha256-MV3a0caNPWq8CgfaHwRc3OtqWiEXK6Mo0nt+/2nFwV0=" 37 | ] 38 | //"StrictDynamic": true 39 | }, 40 | "Style": { 41 | "AddNonce": true, 42 | "AllowedSources": [ 43 | "'self'", 44 | "ajax.aspnetcdn.com" 45 | ] 46 | }, 47 | "Img": { 48 | "AllowedSources": [ 49 | "'self'" 50 | ] 51 | }, 52 | "Connect": { 53 | "AllowedSources": [ 54 | "'self'", 55 | "ws://localhost:1591", 56 | "http://localhost:1591" 57 | ] 58 | }, 59 | "Font": { 60 | "AllowedSources": [ 61 | "'self'", 62 | "ajax.aspnetcdn.com" 63 | ] 64 | }, 65 | "Frame": { 66 | "AllowNone": true 67 | }, 68 | "Worker": { 69 | "AllowNone": true 70 | }, 71 | "BaseUri": { 72 | "AllowSelf": true 73 | }, 74 | "Prefetch": { 75 | "AllowSelf": true 76 | }, 77 | "RequireSri": { 78 | "ForScripts": true, 79 | "ForStyles": true 80 | } 81 | }, 82 | "FeaturePolicy": { 83 | "Camera": { 84 | "AllowSelf": true 85 | }, 86 | "Payment": { 87 | "AllowNone": true 88 | }, 89 | "Other": { 90 | "some-new-thing": { 91 | "AllowNone": true 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp.net", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "3.3.6", 6 | "jquery": "2.2.0", 7 | "jquery-validation": "1.14.0", 8 | "jquery-validation-unobtrusive": "3.2.6" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optinally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/_references.js: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Set widths on the form inputs since otherwise they're 100% wide */ 14 | input, 15 | select, 16 | textarea { 17 | max-width: 280px; 18 | } 19 | 20 | /* Carousel */ 21 | .carousel-caption p { 22 | font-size: 20px; 23 | line-height: 1.4; 24 | } 25 | 26 | /* Make .svg files in the carousel display properly in older browsers */ 27 | .carousel-inner .item img[src$=".svg"] 28 | { 29 | width: 100%; 30 | } 31 | 32 | /* Hide/rearrange for smaller screens */ 33 | @media screen and (max-width: 767px) { 34 | /* Hide captions */ 35 | .carousel-caption { 36 | display: none 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/favicon.ico -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/OtherScript.js: -------------------------------------------------------------------------------- 1 | console.log("Script loaded dynamically!"); -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 |  2 | // Test for strict-dynamic 3 | //var otherScript = document.createElement("script"); 4 | //otherScript.src = "/js/OtherScript.js"; 5 | //document.head.appendChild(otherScript); -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap", 3 | "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", 4 | "keywords": [ 5 | "css", 6 | "js", 7 | "less", 8 | "mobile-first", 9 | "responsive", 10 | "front-end", 11 | "framework", 12 | "web" 13 | ], 14 | "homepage": "http://getbootstrap.com", 15 | "license": "MIT", 16 | "moduleType": "globals", 17 | "main": [ 18 | "less/bootstrap.less", 19 | "dist/js/bootstrap.js" 20 | ], 21 | "ignore": [ 22 | "/.*", 23 | "_config.yml", 24 | "CNAME", 25 | "composer.json", 26 | "CONTRIBUTING.md", 27 | "docs", 28 | "js/tests", 29 | "test-infra" 30 | ], 31 | "dependencies": { 32 | "jquery": "1.9.1 - 2" 33 | }, 34 | "version": "3.3.6", 35 | "_release": "3.3.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.3.6", 39 | "commit": "81df608a40bf0629a1dc08e584849bb1e43e0b7a" 40 | }, 41 | "_source": "git://github.com/twbs/bootstrap.git", 42 | "_target": "3.3.6", 43 | "_originalSource": "bootstrap" 44 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2015 Twitter, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA"} -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juunas11/aspnetcore-security-headers/47125b8311ce6c996333e93baa64957b8b90c2c6/test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/bootstrap/dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation-unobtrusive/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation-unobtrusive", 3 | "version": "3.2.6", 4 | "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", 5 | "description": "Add-on to jQuery Validation to enable unobtrusive validation options in data-* attributes.", 6 | "main": [ 7 | "jquery.validate.unobtrusive.js" 8 | ], 9 | "ignore": [ 10 | "**/.*", 11 | "*.json", 12 | "*.md", 13 | "*.txt", 14 | "gulpfile.js" 15 | ], 16 | "keywords": [ 17 | "jquery", 18 | "asp.net", 19 | "mvc", 20 | "validation", 21 | "unobtrusive" 22 | ], 23 | "authors": [ 24 | "Microsoft" 25 | ], 26 | "license": "http://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/aspnet/jquery-validation-unobtrusive.git" 30 | }, 31 | "dependencies": { 32 | "jquery-validation": ">=1.8", 33 | "jquery": ">=1.8" 34 | }, 35 | "_release": "3.2.6", 36 | "_resolution": { 37 | "type": "version", 38 | "tag": "v3.2.6", 39 | "commit": "13386cd1b5947d8a5d23a12b531ce3960be1eba7" 40 | }, 41 | "_source": "git://github.com/aspnet/jquery-validation-unobtrusive.git", 42 | "_target": "3.2.6", 43 | "_originalSource": "jquery-validation-unobtrusive" 44 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** Unobtrusive validation support library for jQuery and jQuery Validate 3 | ** Copyright (C) Microsoft Corporation. All rights reserved. 4 | */ 5 | !function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function m(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=p.unobtrusive.options||{},m=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),m("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),m("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),m("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var u,p=a.validator,v="unobtrusiveValidation";p.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=m(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){p.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=m(this);a&&a.attachValidation()})}},u=p.unobtrusive.adapters,u.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},u.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},u.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},u.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},p.addMethod("__dummy__",function(a,e,n){return!0}),p.addMethod("regex",function(a,e,n){var t;return this.optional(e)?!0:(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),p.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),p.methods.extension?(u.addSingleVal("accept","mimtype"),u.addSingleVal("extension","extension")):u.addSingleVal("extension","extension","accept"),u.addSingleVal("regex","pattern"),u.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),u.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),u.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),u.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),u.add("required",function(a){("INPUT"!==a.element.tagName.toUpperCase()||"CHECKBOX"!==a.element.type.toUpperCase())&&e(a,"required",!0)}),u.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),u.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),a(function(){p.unobtrusive.parse(document)})}(jQuery); -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-validation", 3 | "homepage": "http://jqueryvalidation.org/", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/jzaefferer/jquery-validation.git" 7 | }, 8 | "authors": [ 9 | "Jörn Zaefferer " 10 | ], 11 | "description": "Form validation made easy", 12 | "main": "dist/jquery.validate.js", 13 | "keywords": [ 14 | "forms", 15 | "validation", 16 | "validate" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "demo", 25 | "lib" 26 | ], 27 | "dependencies": { 28 | "jquery": ">= 1.7.2" 29 | }, 30 | "version": "1.14.0", 31 | "_release": "1.14.0", 32 | "_resolution": { 33 | "type": "version", 34 | "tag": "1.14.0", 35 | "commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48" 36 | }, 37 | "_source": "git://github.com/jzaefferer/jquery-validation.git", 38 | "_target": ">=1.8", 39 | "_originalSource": "jquery-validation" 40 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "dist/jquery.js", 4 | "license": "MIT", 5 | "ignore": [ 6 | "package.json" 7 | ], 8 | "keywords": [ 9 | "jquery", 10 | "javascript", 11 | "browser", 12 | "library" 13 | ], 14 | "homepage": "https://github.com/jquery/jquery-dist", 15 | "version": "2.2.0", 16 | "_release": "2.2.0", 17 | "_resolution": { 18 | "type": "version", 19 | "tag": "2.2.0", 20 | "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5" 21 | }, 22 | "_source": "git://github.com/jquery/jquery-dist.git", 23 | "_target": "2.2.0", 24 | "_originalSource": "jquery" 25 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Samples/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/ContentTypeOptionsMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Joonasw.AspNetCore.SecurityHeaders.XContentTypeOptions; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Options; 8 | using Xunit; 9 | 10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 11 | { 12 | public class ContentTypeOptionsMiddlewareTests 13 | { 14 | [Fact] 15 | public async Task HeaderSetCorrectlyWithNoSniffing() 16 | { 17 | string headerValue = null; 18 | RequestDelegate mockNext = (HttpContext ctx) => 19 | { 20 | headerValue = ctx.Response.Headers["X-Content-Type-Options"]; 21 | return Task.CompletedTask; 22 | }; 23 | var options = Options.Create(new XContentTypeOptionsOptions 24 | { 25 | AllowSniffing = false 26 | }); 27 | var mockContext = new DefaultHttpContext(); 28 | var sut = new XContentTypeOptionsMiddleware(mockNext, options); 29 | 30 | await sut.Invoke(mockContext); 31 | 32 | Assert.Equal("nosniff", headerValue); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspRequireSriBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder; 2 | using Xunit; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 5 | { 6 | public class CspRequireSriBuilderTests 7 | { 8 | [Fact] 9 | public void NoOptions_DoesNotGenerateHeader() 10 | { 11 | var builder = new CspRequireSriBuilder(); 12 | 13 | var options = builder.BuildOptions(); 14 | 15 | Assert.True(string.IsNullOrWhiteSpace(options.ToString())); 16 | } 17 | 18 | [Fact] 19 | public void RequireScript_GeneratesHeader() 20 | { 21 | var builder = new CspRequireSriBuilder(); 22 | builder.ForScripts(); 23 | 24 | var options = builder.BuildOptions(); 25 | 26 | Assert.Equal("require-sri-for script", options.ToString()); 27 | } 28 | 29 | [Fact] 30 | public void RequireStyle_GeneratesHeader() 31 | { 32 | var builder = new CspRequireSriBuilder(); 33 | builder.ForStyles(); 34 | 35 | var options = builder.BuildOptions(); 36 | 37 | Assert.Equal("require-sri-for style", options.ToString()); 38 | } 39 | 40 | [Fact] 41 | public void RequireScriptAndStyle_GeneratesHeader() 42 | { 43 | var builder = new CspRequireSriBuilder(); 44 | builder.ForScripts(); 45 | builder.ForStyles(); 46 | 47 | var options = builder.BuildOptions(); 48 | 49 | Assert.Equal("require-sri-for script style", options.ToString()); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspSandboxBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder; 2 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 3 | using Xunit; 4 | 5 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 6 | { 7 | public class CspSandboxBuilderTests 8 | { 9 | [Fact] 10 | public void AllowForms_SetsAllowFormsToTrue() 11 | { 12 | var builder = new CspSandboxBuilder(); 13 | 14 | builder.AllowForms(); 15 | CspSandboxOptions options = builder.BuildOptions(); 16 | 17 | Assert.True(options.AllowForms); 18 | } 19 | 20 | [Fact] 21 | public void AllowModals_SetsAllowModalsToTrue() 22 | { 23 | var builder = new CspSandboxBuilder(); 24 | 25 | builder.AllowModals(); 26 | CspSandboxOptions options = builder.BuildOptions(); 27 | 28 | Assert.True(options.AllowModals); 29 | } 30 | 31 | [Fact] 32 | public void AllowOrientationLock_SetsAllowOrientationLockToTrue() 33 | { 34 | var builder = new CspSandboxBuilder(); 35 | 36 | builder.AllowOrientationLock(); 37 | CspSandboxOptions options = builder.BuildOptions(); 38 | 39 | Assert.True(options.AllowOrientationLock); 40 | } 41 | 42 | [Fact] 43 | public void AllowPointerLock_SetsAllowPointerLockToTrue() 44 | { 45 | var builder = new CspSandboxBuilder(); 46 | 47 | builder.AllowPointerLock(); 48 | CspSandboxOptions options = builder.BuildOptions(); 49 | 50 | Assert.True(options.AllowPointerLock); 51 | } 52 | 53 | [Fact] 54 | public void AllowPopups_SetsAllowPopupsToTrue() 55 | { 56 | var builder = new CspSandboxBuilder(); 57 | 58 | builder.AllowPopups(); 59 | CspSandboxOptions options = builder.BuildOptions(); 60 | 61 | Assert.True(options.AllowPopups); 62 | } 63 | 64 | [Fact] 65 | public void AllowPopupsToEscapeSandbox_SetsAllowPopupsToEscapeSandboxToTrue() 66 | { 67 | var builder = new CspSandboxBuilder(); 68 | 69 | builder.AllowPopupsToEscapeSandbox(); 70 | CspSandboxOptions options = builder.BuildOptions(); 71 | 72 | Assert.True(options.AllowPopupsToEscapeSandbox); 73 | } 74 | 75 | [Fact] 76 | public void AllowPresentation_SetsAllowPresentationToTrue() 77 | { 78 | var builder = new CspSandboxBuilder(); 79 | 80 | builder.AllowPresentation(); 81 | CspSandboxOptions options = builder.BuildOptions(); 82 | 83 | Assert.True(options.AllowPresentation); 84 | } 85 | 86 | [Fact] 87 | public void AllowSameOrigin_SetsAllowSameOriginToTrue() 88 | { 89 | var builder = new CspSandboxBuilder(); 90 | 91 | builder.AllowSameOrigin(); 92 | CspSandboxOptions options = builder.BuildOptions(); 93 | 94 | Assert.True(options.AllowSameOrigin); 95 | } 96 | 97 | [Fact] 98 | public void AllowScripts_SetsAllowScriptsToTrue() 99 | { 100 | var builder = new CspSandboxBuilder(); 101 | 102 | builder.AllowScripts(); 103 | CspSandboxOptions options = builder.BuildOptions(); 104 | 105 | Assert.True(options.AllowScripts); 106 | } 107 | 108 | [Fact] 109 | public void AllowTopNavigation_SetsAllowTopNavigationToTrue() 110 | { 111 | var builder = new CspSandboxBuilder(); 112 | 113 | builder.AllowTopNavigation(); 114 | CspSandboxOptions options = builder.BuildOptions(); 115 | 116 | Assert.True(options.AllowTopNavigation); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspStylesBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder; 4 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 5 | using Xunit; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 8 | { 9 | public class CspStylesBuilderTests 10 | { 11 | [Fact] 12 | public void FromNowhere_SetsAllowNoneToTrue() 13 | { 14 | var builder = new CspStylesBuilder(); 15 | 16 | builder.FromNowhere(); 17 | CspStyleSrcOptions options = builder.BuildOptions(); 18 | 19 | Assert.True(options.AllowNone); 20 | } 21 | 22 | [Fact] 23 | public void FromSelf_SetsAllowSelfToTrue() 24 | { 25 | var builder = new CspStylesBuilder(); 26 | 27 | builder.FromSelf(); 28 | CspStyleSrcOptions options = builder.BuildOptions(); 29 | 30 | Assert.True(options.AllowSelf); 31 | } 32 | 33 | [Fact] 34 | public void From_AddsUrlToAllowedSources() 35 | { 36 | var builder = new CspStylesBuilder(); 37 | 38 | builder.From("www.google.com"); 39 | CspStyleSrcOptions options = builder.BuildOptions(); 40 | 41 | Assert.Equal("www.google.com", options.AllowedSources.Single()); 42 | } 43 | 44 | [Fact] 45 | public void From_ThrowsArgumentNullException_WithNullUrl() 46 | { 47 | var builder = new CspStylesBuilder(); 48 | 49 | Assert.Throws(() => builder.From(null)); 50 | } 51 | 52 | [Fact] 53 | public void From_ThrowsArgumentException_WithEmptyUrl() 54 | { 55 | var builder = new CspStylesBuilder(); 56 | 57 | Assert.Throws(() => builder.From(string.Empty)); 58 | } 59 | 60 | [Fact] 61 | public void WithHash_AddsHashToAllowedHashes() 62 | { 63 | var builder = new CspStylesBuilder(); 64 | 65 | builder.WithHash("sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc="); 66 | CspStyleSrcOptions options = builder.BuildOptions(); 67 | 68 | Assert.Equal("sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=", options.AllowedHashes.Single()); 69 | } 70 | 71 | [Fact] 72 | public void WithHash_ThrowsArgumentNullException_WithNullUrl() 73 | { 74 | var builder = new CspStylesBuilder(); 75 | 76 | Assert.Throws(() => builder.WithHash(null)); 77 | } 78 | 79 | [Fact] 80 | public void WithHash_ThrowsArgumentException_WithEmptyUrl() 81 | { 82 | var builder = new CspStylesBuilder(); 83 | 84 | Assert.Throws(() => builder.WithHash(string.Empty)); 85 | } 86 | 87 | [Fact] 88 | public void AllowUnsafeInline_SetsUnsafeInlineToTrue() 89 | { 90 | var builder = new CspStylesBuilder(); 91 | 92 | builder.AllowUnsafeInline(); 93 | CspStyleSrcOptions options = builder.BuildOptions(); 94 | 95 | Assert.True(options.AllowUnsafeInline); 96 | } 97 | 98 | [Fact] 99 | public void AddNonce_SetsAddNonceToTrue() 100 | { 101 | var builder = new CspStylesBuilder(); 102 | 103 | builder.AddNonce(); 104 | CspStyleSrcOptions options = builder.BuildOptions(); 105 | 106 | Assert.True(options.AddNonce); 107 | } 108 | 109 | [Fact] 110 | public void FromAnywhere_SetsAllowAnyToTrue() 111 | { 112 | var builder = new CspStylesBuilder(); 113 | 114 | builder.FromAnywhere(); 115 | CspStyleSrcOptions options = builder.BuildOptions(); 116 | 117 | Assert.True(options.AllowAny); 118 | } 119 | 120 | [Fact] 121 | public void OnlyOverHttps_SetsAllowOnlyHttpsToTrue() 122 | { 123 | var builder = new CspStylesBuilder(); 124 | 125 | builder.OnlyOverHttps(); 126 | CspStyleSrcOptions options = builder.BuildOptions(); 127 | 128 | Assert.True(options.AllowOnlyHttps); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/CspWorkerBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder; 4 | using Joonasw.AspNetCore.SecurityHeaders.Csp.Options; 5 | using Xunit; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 8 | { 9 | public class CspWorkerBuilderTests 10 | { 11 | [Fact] 12 | public void FromNowhere_SetsAllowNoneToTrue() 13 | { 14 | var builder = new CspWorkerBuilder(); 15 | 16 | builder.FromNowhere(); 17 | CspWorkerSrcOptions options = builder.BuildOptions(); 18 | 19 | Assert.True(options.AllowNone); 20 | } 21 | 22 | [Fact] 23 | public void FromSelf_SetsAllowSelfToTrue() 24 | { 25 | var builder = new CspWorkerBuilder(); 26 | 27 | builder.FromSelf(); 28 | CspWorkerSrcOptions options = builder.BuildOptions(); 29 | 30 | Assert.True(options.AllowSelf); 31 | } 32 | 33 | [Fact] 34 | public void FromSelf_ReturnsCorrectString() 35 | { 36 | var builder = new CspWorkerBuilder(); 37 | 38 | builder.FromSelf(); 39 | CspWorkerSrcOptions options = builder.BuildOptions(); 40 | 41 | Assert.Equal("worker-src 'self'", options.ToString(null)); 42 | } 43 | 44 | [Fact] 45 | public void From_AddsUrlToAllowedSources() 46 | { 47 | var builder = new CspWorkerBuilder(); 48 | 49 | builder.From("www.google.com"); 50 | CspWorkerSrcOptions options = builder.BuildOptions(); 51 | 52 | Assert.Equal("www.google.com", options.AllowedSources.Single()); 53 | } 54 | 55 | [Fact] 56 | public void From_ThrowsArgumentNullException_WithNullUrl() 57 | { 58 | var builder = new CspWorkerBuilder(); 59 | 60 | Assert.Throws(() => builder.From(null)); 61 | } 62 | 63 | [Fact] 64 | public void From_ThrowsArgumentException_WithEmptyUrl() 65 | { 66 | var builder = new CspWorkerBuilder(); 67 | 68 | Assert.Throws(() => builder.From(string.Empty)); 69 | } 70 | 71 | [Fact] 72 | public void FromAnywhere_SetsAllowAnyToTrue() 73 | { 74 | var builder = new CspWorkerBuilder(); 75 | 76 | builder.FromAnywhere(); 77 | CspWorkerSrcOptions options = builder.BuildOptions(); 78 | 79 | Assert.True(options.AllowAny); 80 | } 81 | 82 | [Fact] 83 | public void OnlyOverHttps_SetsAllowOnlyHttpsToTrue() 84 | { 85 | var builder = new CspWorkerBuilder(); 86 | 87 | builder.OnlyOverHttps(); 88 | CspWorkerSrcOptions options = builder.BuildOptions(); 89 | 90 | Assert.True(options.AllowOnlyHttps); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/ExpectCtMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Joonasw.AspNetCore.SecurityHeaders.ExpectCT; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | using Xunit; 7 | 8 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 9 | { 10 | public class ExpectCtMiddlewareTests 11 | { 12 | [Fact] 13 | public async Task SetsHeaderCorrectlyWithoutEnforce() 14 | { 15 | string headerValue = null; 16 | RequestDelegate mockNext = (HttpContext ctx) => 17 | { 18 | headerValue = ctx.Response.Headers["Expect-CT"]; 19 | return Task.CompletedTask; 20 | }; 21 | var options = Options.Create(new ExpectCTOptions 22 | { 23 | Enforce = false, 24 | ReportUri = "https://reporting.com/report", 25 | MaxAge = TimeSpan.FromHours(1) 26 | }); 27 | var mockContext = new DefaultHttpContext(); 28 | mockContext.Request.Scheme = "https"; 29 | var sut = new ExpectCTMiddleware(mockNext, options); 30 | 31 | await sut.Invoke(mockContext); 32 | 33 | Assert.Equal("max-age=3600, report-uri=\"https://reporting.com/report\"", headerValue); 34 | } 35 | 36 | [Fact] 37 | public async Task SetsHeaderCorrectlyWithEnforce() 38 | { 39 | string headerValue = null; 40 | RequestDelegate mockNext = (HttpContext ctx) => 41 | { 42 | headerValue = ctx.Response.Headers["Expect-CT"]; 43 | return Task.CompletedTask; 44 | }; 45 | var options = Options.Create(new ExpectCTOptions 46 | { 47 | Enforce = true, 48 | ReportUri = "https://reporting.com/report", 49 | MaxAge = TimeSpan.FromHours(1) 50 | }); 51 | var mockContext = new DefaultHttpContext(); 52 | mockContext.Request.Scheme = "https"; 53 | var sut = new ExpectCTMiddleware(mockNext, options); 54 | 55 | await sut.Invoke(mockContext); 56 | 57 | Assert.Equal("max-age=3600, enforce, report-uri=\"https://reporting.com/report\"", headerValue); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/FeaturePolicyBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Builder; 2 | using Xunit; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 5 | { 6 | public class FeaturePolicyBuilderTests 7 | { 8 | [Fact] 9 | public void PaymentFromNowhere_SetsValue() 10 | { 11 | var builder = new FeaturePolicyBuilder(); 12 | 13 | builder.AllowPayment.FromNowhere(); 14 | var options = builder.BuildFeaturePolicyOptions(); 15 | string headerValue = options.ToString(); 16 | 17 | Assert.Equal("payment 'none'", headerValue); 18 | } 19 | 20 | [Fact] 21 | public void PaymentFromSelfAndGoogle_SetsValue() 22 | { 23 | var builder = new FeaturePolicyBuilder(); 24 | 25 | builder.AllowPayment.FromSelf().From("https://www.google.com"); 26 | var options = builder.BuildFeaturePolicyOptions(); 27 | string headerValue = options.ToString(); 28 | 29 | Assert.Equal("payment 'self' https://www.google.com", headerValue); 30 | } 31 | 32 | [Fact] 33 | public void PaymentFromNowhereAndFullScreenFromSelf_SetsValue() 34 | { 35 | var builder = new FeaturePolicyBuilder(); 36 | 37 | builder.AllowPayment.FromNowhere(); 38 | builder.AllowFullscreen.FromSelf(); 39 | var options = builder.BuildFeaturePolicyOptions(); 40 | string headerValue = options.ToString(); 41 | 42 | Assert.Equal("fullscreen 'self'; payment 'none'", headerValue); 43 | } 44 | 45 | [Fact] 46 | public void OtherNewFeatureFromSelf_SetsValue() 47 | { 48 | var builder = new FeaturePolicyBuilder(); 49 | 50 | builder.AllowOtherFeature("new-feature").FromSelf(); 51 | var options = builder.BuildFeaturePolicyOptions(); 52 | string headerValue = options.ToString(); 53 | 54 | Assert.Equal("new-feature 'self'", headerValue); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/FeaturePolicyMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy; 6 | using Joonasw.AspNetCore.SecurityHeaders.FeaturePolicy.Options; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Options; 9 | using Xunit; 10 | 11 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 12 | { 13 | public class FeaturePolicyMiddlewareTests 14 | { 15 | [Fact] 16 | public async Task FeaturePolicyHeaderAddedCorrectly() 17 | { 18 | string headerValue = null; 19 | RequestDelegate mockNext = (HttpContext ctx) => 20 | { 21 | headerValue = ctx.Response.Headers["Feature-Policy"]; 22 | return Task.CompletedTask; 23 | }; 24 | var options = Options.Create(new FeaturePolicyOptions 25 | { 26 | Autoplay = new FeaturePolicyAutoplayOptions 27 | { 28 | AllowSelf = true 29 | }, 30 | Payment = new FeaturePolicyPaymentOptions 31 | { 32 | AllowNone = true 33 | }, 34 | Speaker = new FeaturePolicySpeakerOptions 35 | { 36 | AllowSelf = true, 37 | AllowedOrigins = new List 38 | { 39 | "https://site1", 40 | "https://site2" 41 | } 42 | }, 43 | Other = new Dictionary 44 | { 45 | ["some-new-one"] = new FeaturePolicyOtherFeatureOptions("some-new-one") 46 | { 47 | AllowSelf = true 48 | } 49 | } 50 | }); 51 | var mockContext = new DefaultHttpContext(); 52 | var sut = new FeaturePolicyMiddleware(mockNext, options); 53 | 54 | await sut.Invoke(mockContext); 55 | 56 | Assert.Equal("speaker 'self' https://site1 https://site2; payment 'none'; autoplay 'self'; some-new-one 'self'", headerValue); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/FrameOptionsMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Joonasw.AspNetCore.SecurityHeaders.XFrameOptions; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Options; 5 | using Xunit; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 8 | { 9 | public class FrameOptionsMiddlewareTests 10 | { 11 | [Theory] 12 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.Deny, null, "DENY")] 13 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.SameOrigin, null, "SAMEORIGIN")] 14 | [InlineData(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom, "https://site.com", "ALLOW-FROM https://site.com")] 15 | public async Task HeaderSetCorrectly( 16 | XFrameOptionsOptions.XFrameOptionsValues value, 17 | string allowedUrl, 18 | string expectedValue) 19 | { 20 | string headerValue = null; 21 | RequestDelegate mockNext = (HttpContext ctx) => 22 | { 23 | headerValue = ctx.Response.Headers["X-Frame-Options"]; 24 | return Task.CompletedTask; 25 | }; 26 | var options = Options.Create(new XFrameOptionsOptions 27 | { 28 | HeaderValue = value, 29 | AllowFromUrl = allowedUrl 30 | }); 31 | var mockContext = new DefaultHttpContext(); 32 | var sut = new XFrameOptionsMiddleware(mockNext, options); 33 | 34 | await sut.Invoke(mockContext); 35 | 36 | Assert.Equal(expectedValue, headerValue); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/HpkpMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Joonasw.AspNetCore.SecurityHeaders.Hpkp; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Options; 8 | using Xunit; 9 | 10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 11 | { 12 | public class HpkpMiddlewareTests 13 | { 14 | [Fact] 15 | public async Task HpkpHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHpkpHeader() 16 | { 17 | RequestDelegate mockNext = (HttpContext ctx) => 18 | { 19 | return Task.CompletedTask; 20 | }; 21 | var options = Options.Create(new HpkpOptions() 22 | { 23 | MaxAgeSeconds = 60, 24 | Pins = new List{"a"} 25 | }); 26 | var sut = new HpkpMiddleware(mockNext, options); 27 | var mockContext = new DefaultHttpContext(); 28 | mockContext.Response.Headers.Append("Public-Key-Pins", "abc; max-age=60"); 29 | 30 | await sut.Invoke(mockContext); 31 | //Invoke throws System.ArgumentException if it tries to add the header again 32 | } 33 | 34 | [Fact] 35 | public async Task HpkpHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHpkpReportOnlyHeader() 36 | { 37 | bool hpkpHeaderExists = true; 38 | RequestDelegate mockNext = (HttpContext ctx) => 39 | { 40 | hpkpHeaderExists = ctx.Response.Headers.ContainsKey("Public-Key-Pins"); 41 | return Task.CompletedTask; 42 | }; 43 | var options = Options.Create(new HpkpOptions() 44 | { 45 | MaxAgeSeconds = 60, 46 | Pins = new List{"a"} 47 | }); 48 | var sut = new HpkpMiddleware(mockNext, options); 49 | var mockContext = new DefaultHttpContext(); 50 | mockContext.Response.Headers.Append("Public-Key-Pins-Report-Only", "abc; max-age=60"); 51 | 52 | await sut.Invoke(mockContext); 53 | 54 | Assert.False(hpkpHeaderExists); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/HstsMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Joonasw.AspNetCore.SecurityHeaders.Hsts; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Options; 8 | using Xunit; 9 | 10 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 11 | { 12 | public class HstsMiddlewareTests 13 | { 14 | [Fact] 15 | public async Task HstsHeaderIsNotIncluded_WhenRequestHeadersAlreadyContainHstsHeader() 16 | { 17 | RequestDelegate mockNext = (HttpContext ctx) => 18 | { 19 | return Task.CompletedTask; 20 | }; 21 | var options = Options.Create(new HstsOptions() 22 | { 23 | Duration = TimeSpan.FromHours(1) 24 | }); 25 | var sut = new HstsMiddleware(mockNext, options); 26 | var mockContext = new DefaultHttpContext(); 27 | mockContext.Request.Scheme = "https"; 28 | mockContext.Response.Headers.Append("Strict-Transport-Security", "max-age=31536000"); 29 | 30 | await sut.Invoke(mockContext); 31 | //Invoke throws System.ArgumentException if it tries to add the header again 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/Joonasw.AspNetCore.SecurityHeaders.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0;net9.0 4 | false 5 | 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/ReferrerPolicyMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Joonasw.AspNetCore.SecurityHeaders.ReferrerPolicy; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Options; 6 | using Xunit; 7 | 8 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 9 | { 10 | public class ReferrerPolicyMiddlewareTests 11 | { 12 | [Fact] 13 | public async Task SetsHeaderCorrectlyWithSameOrigin() 14 | { 15 | string headerValue = null; 16 | RequestDelegate mockNext = (HttpContext ctx) => 17 | { 18 | headerValue = ctx.Response.Headers["Referrer-Policy"]; 19 | return Task.CompletedTask; 20 | }; 21 | var options = Options.Create(new ReferrerPolicyOptions 22 | { 23 | PolicyValue = ReferrerPolicyOptions.ReferrerPolicyValue.SameOrigin 24 | }); 25 | var mockContext = new DefaultHttpContext(); 26 | var sut = new ReferrerPolicyMiddleware(mockNext, options); 27 | 28 | await sut.Invoke(mockContext); 29 | 30 | Assert.Equal("same-origin", headerValue); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/XFrameOptionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Joonasw.AspNetCore.SecurityHeaders.XFrameOptions; 3 | using Xunit; 4 | 5 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 6 | { 7 | public class XFrameOptionsTests 8 | { 9 | [Fact] 10 | public void DenySetByDefault_ResultIsCorrect() 11 | { 12 | var options = new XFrameOptionsOptions(); 13 | 14 | Assert.Equal(XFrameOptionsOptions.XFrameOptionsValues.Deny, options.HeaderValue); 15 | } 16 | 17 | [Fact] 18 | public void WhenSetAllowFromUrlIsRequired_ResultIsCorrect() 19 | { 20 | Assert.Throws(() => 21 | { 22 | var options = new XFrameOptionsOptions(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom); 23 | }); 24 | } 25 | 26 | [Fact] 27 | public void WhenSetAllowFromUrlDoesNotThrow_ResultIsCorrect() 28 | { 29 | var options = Record.Exception(() => 30 | new XFrameOptionsOptions(XFrameOptionsOptions.XFrameOptionsValues.AllowFrom, "https://google.com")); 31 | 32 | Assert.Null(options); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/XXssProtectionTests.cs: -------------------------------------------------------------------------------- 1 | using Joonasw.AspNetCore.SecurityHeaders.XXssProtection; 2 | using Xunit; 3 | 4 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 5 | { 6 | public class XXssProtectionTests 7 | { 8 | [Fact] 9 | public void EnabledAndBlockedByDefault_ResultIsCorrect() 10 | { 11 | var options = new XXssProtectionOptions(); 12 | 13 | Assert.True(options.EnableProtection); 14 | Assert.True(options.EnableAttackBlock); 15 | } 16 | 17 | [Fact] 18 | public void EnabledAndBlockedAreOffWhenSet_ResultIsCorrect() 19 | { 20 | var options = new XXssProtectionOptions(false, false); 21 | 22 | Assert.False(options.EnableProtection); 23 | Assert.False(options.EnableAttackBlock); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/Joonasw.AspNetCore.SecurityHeaders.Tests/XssProtectionMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Joonasw.AspNetCore.SecurityHeaders.XXssProtection; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Options; 5 | using Xunit; 6 | 7 | namespace Joonasw.AspNetCore.SecurityHeaders.Tests 8 | { 9 | public class XssProtectionMiddlewareTests 10 | { 11 | [Theory] 12 | [InlineData(false, false, "0")] 13 | [InlineData(false, true, "1")] 14 | [InlineData(true, true, "1; mode=block")] 15 | public async Task HeaderSetCorrectly(bool enableBlock, bool enableProtection, string expectedValue) 16 | { 17 | string headerValue = null; 18 | RequestDelegate mockNext = (HttpContext ctx) => 19 | { 20 | headerValue = ctx.Response.Headers["X-XSS-Protection"]; 21 | return Task.CompletedTask; 22 | }; 23 | var options = Options.Create(new XXssProtectionOptions 24 | { 25 | EnableAttackBlock = enableBlock, 26 | EnableProtection = enableProtection 27 | }); 28 | var mockContext = new DefaultHttpContext(); 29 | var sut = new XXssProtectionMiddleware(mockNext, options); 30 | 31 | await sut.Invoke(mockContext); 32 | 33 | Assert.Equal(expectedValue, headerValue); 34 | } 35 | } 36 | } 37 | --------------------------------------------------------------------------------