Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

TDEAL-16: Security improvements #231

Merged
merged 93 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
dad5c9d
Add constants for content security policy directives.
sarahelsaig Dec 22, 2023
c761c22
Add middleware extension method and extension point for building Cont…
sarahelsaig Dec 22, 2023
02fb71b
Add documentation.
sarahelsaig Dec 22, 2023
93f32b6
Allow inline scripts and rename extension method.
sarahelsaig Dec 22, 2023
d47d807
Fix UseContentSecurityPolicyHeader for OC basic features.
sarahelsaig Dec 22, 2023
1226806
Add AntiClickjackingContentSecurityPolicyProvider.
sarahelsaig Dec 22, 2023
339741e
Secure anti forgery token.
sarahelsaig Dec 22, 2023
c3beb82
Change default SameSite attribute of SetCookieForever.
sarahelsaig Dec 22, 2023
e547d69
Also pass in HttpContext.
sarahelsaig Dec 22, 2023
9b14fdd
Config common directives.
sarahelsaig Dec 22, 2023
b3f9572
Add spelling word "clickjacking".
sarahelsaig Dec 26, 2023
2358851
Add missing newline.
sarahelsaig Dec 26, 2023
32ddf01
Fix CA1052.
sarahelsaig Dec 26, 2023
fcb78ee
Add form-action directive.
sarahelsaig Dec 26, 2023
d4cf8fe
Merge remote-tracking branch 'origin/dev' into issue/TDEAL-16
sarahelsaig Dec 26, 2023
deb91ba
Add ConfigureSessionCookieAlwaysSecure.
sarahelsaig Dec 26, 2023
4f33eb4
Add UseContentTypeOptionsHeader.
sarahelsaig Dec 26, 2023
4f8b5e6
Add defaults extension methods.
sarahelsaig Dec 28, 2023
4df453c
Fix usings.
sarahelsaig Dec 28, 2023
c87124e
Delete .github/actions/spelling/allow/security.txt
sarahelsaig Dec 28, 2023
d01ce29
Update Lombiq.HelpfulLibraries.AspNetCore/Security/ApplicationBuilder…
sarahelsaig Dec 28, 2023
9633f9a
Update Lombiq.HelpfulLibraries.AspNetCore/Security/ApplicationBuilder…
sarahelsaig Dec 28, 2023
cfba7e2
Fix ConfigureSecurityDefaults.
sarahelsaig Dec 29, 2023
be0e243
Merge branch 'issue/TDEAL-16' of https://github.com/Lombiq/Helpful-Li…
sarahelsaig Dec 29, 2023
826815d
Add more documentation for the default security extension methods.
sarahelsaig Dec 29, 2023
242f4b4
Add InlineStartup and change ConfigureSecurityDefaults to use it.
sarahelsaig Dec 29, 2023
02e3149
Rename UseContentTypeOptionsHeader to UseNosniffContentTypeOptionsHeader
sarahelsaig Dec 29, 2023
cf26ddd
Update xmldoc to make it clear that it's a quote.
sarahelsaig Dec 29, 2023
c9bc0ae
Update documentation.
sarahelsaig Dec 29, 2023
0c308db
Typo.
sarahelsaig Dec 29, 2023
539ac6e
Annoying SA1629 doesn't let me have a block of quote on its own
sarahelsaig Dec 29, 2023
3536a5c
Merge remote-tracking branch 'origin/issue/TDEAL-16' into issue/TDEAL-16
sarahelsaig Dec 29, 2023
4bf1cd2
Some code cleanup.
sarahelsaig Dec 29, 2023
f60e643
Add AddContentSecurityPolicyProvider
sarahelsaig Dec 29, 2023
7651b15
These may be amended during program setup.
sarahelsaig Dec 29, 2023
e9dd931
Merge the anti-clickjacking provider into the main UseContentSecurity…
sarahelsaig Dec 29, 2023
2627cb5
Permit fonts.gstatic.com
sarahelsaig Dec 29, 2023
d3b0e50
Update security documentation.
sarahelsaig Dec 29, 2023
945f7c2
Prevent adding the "Content-Security-Policy" header twice.
sarahelsaig Dec 29, 2023
fcf8959
Skip security middlewares during setup.
sarahelsaig Dec 29, 2023
9647be5
Control inline scripts and styles separately.
sarahelsaig Dec 29, 2023
a2a8651
Fix formatting.
sarahelsaig Dec 29, 2023
7f97b99
Adjust doc.
sarahelsaig Dec 29, 2023
cdae9cc
Merge remote-tracking branch 'origin/dev' into issue/TDEAL-16
sarahelsaig Dec 30, 2023
1586b19
Merge branch 'issue/TDEAL-16' of https://github.com/Lombiq/Helpful-Li…
sarahelsaig Dec 30, 2023
2491839
Update the header at the end of the pipeline.
sarahelsaig Jan 4, 2024
57f8ede
Add VueContentSecurityPolicyProvider
sarahelsaig Jan 5, 2024
2d20cee
Merge branch 'issue/TDEAL-16' of https://github.com/Lombiq/Helpful-Li…
sarahelsaig Jan 5, 2024
56245a4
Move it into the OnStarting event.
sarahelsaig Jan 5, 2024
7c1c76a
No need for isDeferred after all.
sarahelsaig Jan 5, 2024
2ae4224
unusing
sarahelsaig Jan 5, 2024
a205528
Update Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurity…
sarahelsaig Jan 6, 2024
2e5b7f5
Use GetServices.
sarahelsaig Jan 6, 2024
0d37c79
comment
sarahelsaig Jan 6, 2024
5cf2a8d
Update Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurity…
sarahelsaig Jan 6, 2024
45d3129
Code cleanup in CdnContentSecurityPolicyProvider.
sarahelsaig Jan 6, 2024
02ff2f5
Add font-src to CdnContentSecurityPolicyProvider.
sarahelsaig Jan 6, 2024
3cda6bc
Fall back if font-src is not defined
sarahelsaig Jan 6, 2024
5a8dca8
Use already existing constant for default tenant name.
sarahelsaig Jan 6, 2024
e5ed11d
Exclude CSP for error page and non-HTML.
sarahelsaig Jan 6, 2024
a0793b7
Fix content type check.
sarahelsaig Jan 7, 2024
4441bf5
Need CSP for 404 after all.
sarahelsaig Jan 7, 2024
32b529e
Remove stray exclamation point.
sarahelsaig Jan 7, 2024
42ffcb0
The other way around.
sarahelsaig Jan 7, 2024
204cc49
Use ConcurrentBag instead of ReadOnlyCollection so these are editable.
sarahelsaig Jan 8, 2024
a26713a
Update connect-src with all permitted sources.
sarahelsaig Jan 8, 2024
0b2c7d7
Add UseStrictAndSecureCookies
sarahelsaig Jan 9, 2024
fc1a38e
Fix errors.
sarahelsaig Jan 9, 2024
fb99d1e
Fix analyzer violations.
sarahelsaig Jan 9, 2024
889cb22
Add UseStrictAndSecureCookies to the security defaults.
sarahelsaig Jan 9, 2024
6a373ce
Don't insert duplicate CSP header.
sarahelsaig Jan 10, 2024
b1a6a07
Merge branch 'issue/TDEAL-16' of https://github.com/Lombiq/Helpful-Li…
sarahelsaig Jan 10, 2024
49940d6
Code cleanup.
sarahelsaig Jan 10, 2024
8c8a473
Additional remarks about HTTPS.
sarahelsaig Jan 10, 2024
2ee9107
Add attribute to mark unsafe-eval actions.
sarahelsaig Jan 10, 2024
71fe999
Code styling.
sarahelsaig Jan 10, 2024
8d6aee7
Add ContentSecurityPolicyAttribute.
sarahelsaig Jan 10, 2024
6774eb6
Add class doc and simplify pattern.
sarahelsaig Jan 10, 2024
0b6e366
Add ResourceManagerContentSecurityPolicyProvider.
sarahelsaig Jan 10, 2024
7d94e89
Code styling.
sarahelsaig Jan 10, 2024
591441c
ThenUpdateAsync.
sarahelsaig Jan 10, 2024
54b9d23
make it virtual
sarahelsaig Jan 10, 2024
6a42332
Better ThenUpdateAsync.
sarahelsaig Jan 10, 2024
c9c9e1c
Make DirectiveName protected.
sarahelsaig Jan 10, 2024
76162f4
Finish comment.
sarahelsaig Jan 12, 2024
4193052
Update Lombiq.HelpfulLibraries.AspNetCore/Security/ApplicationBuilder…
sarahelsaig Jan 12, 2024
f9993f5
Update Lombiq.HelpfulLibraries.OrchardCore/Security/OrchardCoreBuilde…
sarahelsaig Jan 12, 2024
29d34d8
Documentation cross-linking.
sarahelsaig Jan 12, 2024
520cd28
Document MergeWordSets.
sarahelsaig Jan 12, 2024
b68e2de
Code styling.
sarahelsaig Jan 12, 2024
fc86b3c
Add ConfigureSecurityDefaultsWithStaticFiles.
sarahelsaig Jan 12, 2024
764c93a
Update Lombiq.HelpfulLibraries.Common/Extensions/StringExtensions.cs
sarahelsaig Jan 12, 2024
dc9180c
Rephrase docstring.
sarahelsaig Jan 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Docs/Security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Lombiq Helpful Libraries - ASP.NET Core Libraries - Security
Piedone marked this conversation as resolved.
Show resolved Hide resolved

## `Content-Security-Policy`

- `ApplicationBuilderExtensions`: Contains the `AddContentSecurityPolicyHeader` extension method to add a middleware that provides the `Content-Security-Policy` header.
- `CdnContentSecurityPolicyProvider`: An optional policy provider that permits additional CDN host names for the `script-scr` and `style-src` directives.
- `ContentSecurityPolicyDirectives`: The `Content-Security-Policy` directive names that are defined in the W3C [recommendation](https://www.w3.org/TR/CSP2/#directives) and some common values.
- `IContentSecurityPolicyProvider`: Interface for services that update the dictionary that will be turned into the `Content-Security-Policy` header value.
- `ServiceCollectionExtensions`: Extensions methods for `IServiceCollection`, e.g. `AddContentSecurityPolicyProvider()` is a shortcut to register `IContentSecurityPolicyProvider` in dependency injection.

There is a similar section for security extensions related to Orchard Core [here](../../Lombiq.HelpfulLibraries.OrchardCore/Docs/Security.md).

These extensions provide additional security and can resolve issues reported by the [ZAP security scanner](https://github.com/Lombiq/UI-Testing-Toolbox/blob/dev/Lombiq.Tests.UI/Docs/SecurityScanning.md).
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ public static class CookieHttpContextExtensions
/// <summary>
/// Sets the cookie with the given name with a maximal expiration time.
/// </summary>
public static void SetCookieForever(this HttpContext httpContext, string name, string value) =>
public static void SetCookieForever(this HttpContext httpContext, string name, string value, SameSiteMode sameSite = SameSiteMode.Lax) =>
httpContext.Response.Cookies.Append(name, value, new CookieOptions
{
Expires = DateTimeOffset.MaxValue,
Secure = true,
HttpOnly = true,
SameSite = sameSite,
});

/// <summary>
Expand Down
1 change: 1 addition & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Please see the inline documentation of each piece of the API to learn more.
- [Localization](Docs/Localization.md)
- [Middlewares](Docs/Middlewares.md)
- [MVC](Docs/Mvc.md)
- [Security](Docs/Security.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using Lombiq.HelpfulLibraries.AspNetCore.Security;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
using System.Threading.Tasks;
using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives;
using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives.CommonValues;

namespace Microsoft.AspNetCore.Builder;

public static class ApplicationBuilderExtensions
{
/// <summary>
/// Adds a middleware that supplies the <c>Content-Security-Policy</c> header. It may be further expanded by
/// registering services that implement <see cref="IContentSecurityPolicyProvider"/>.
/// </summary>
/// <param name="allowInlineScript">
/// If <see langword="true"/> then inline scripts are permitted. When using Orchard Core a lot of front end shapes
/// use inline script blocks without a nonce (see https://github.com/OrchardCMS/OrchardCore/issues/13389) making
/// this a required setting.
/// </param>
/// <param name="allowInlineStyle">
/// If <see langword="true"/> then inline styles are permitted. Note that even if your site has no embedded style
/// blocks and no style attributes, some Javascript libraries may still create some from code.
/// </param>
public static IApplicationBuilder UseContentSecurityPolicyHeader(
this IApplicationBuilder app,
bool allowInlineScript,
bool allowInlineStyle) =>
app.Use(async (context, next) =>
{
const string key = "Content-Security-Policy";

if (context.Response.Headers.ContainsKey(key))
{
await next();
return;
}

var securityPolicies = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
// Default values enforcing a same origin policy for all resources.
[BaseUri] = Self,
[DefaultSrc] = Self,
[FrameSrc] = Self,
[ScriptSrc] = Self,
[StyleSrc] = Self,
[FormAction] = Self,
// Needed for SVG images using "data:image/svg+xml,..." data URLs.
[ImgSrc] = $"{Self} {Data}",
// Modern sites shouldn't need <object>, <embed>, and <applet> elements.
[ObjectSrc] = None,
// Necessary to prevent clickjacking (https://developer.mozilla.org/en-US/docs/Glossary/Clickjacking).
[FrameAncestors] = Self,
};

if (allowInlineScript) securityPolicies[ScriptSrc] = $"{Self} {UnsafeInline}";
if (allowInlineStyle) securityPolicies[StyleSrc] = $"{Self} {UnsafeInline}";

context.Response.OnStarting(async () =>
{
// No need to do content security policy on non-HTML responses.
if (context.Response.ContentType?.ContainsOrdinalIgnoreCase(MediaTypeNames.Text.Html) != true) return;

// The thought behind this provider model is that if you need something else than the default, you
// should add a provider that only applies the additional directive on screens where it's actually
// needed. This way we maintain minimal permissions. Also if you need additional permissions for a
// specific action you can use the [ContentSecurityPolicyAttribute(value, name, parentName)] attribute.
foreach (var provider in context.RequestServices.GetServices<IContentSecurityPolicyProvider>())
{
await provider.UpdateAsync(securityPolicies, context);
}

var policy = string.Join("; ", securityPolicies.Select(pair => $"{pair.Key} {pair.Value}"));
context.Response.Headers[key] = policy;
});

await next();
});

/// <summary>
/// Adds a middleware that sets the <c>X-Content-Type-Options</c> header to <c>nosniff</c>.
/// </summary>
/// <remarks><para>
/// "The Anti-MIME-Sniffing header X-Content-Type-Options was not set to ’nosniff’. This allows older versions of
/// Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response
/// body to be interpreted and displayed as a content type other than the declared content type. Current (early
/// 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing
/// MIME-sniffing." As written in <see href="https://www.zaproxy.org/docs/alerts/10021/">the documentation</see>.
/// </para></remarks>
public static IApplicationBuilder UseNosniffContentTypeOptionsHeader(this IApplicationBuilder app) =>
app.Use(async (context, next) =>
{
const string key = "X-Content-Type-Options";

if (!context.Response.Headers.ContainsKey(key))
{
context.Response.Headers.Add(key, "nosniff");
}

await next();
});

/// <summary>
/// Adds a middleware that checks all <c>Set-Cookie</c> headers and replaces any with a version containing
/// <c>Secure</c> and <c>SameSite=Strict</c> modifiers if they were missing.
/// </summary>
/// <remarks><para>
/// With this all cookies will only work in a secure context, so you should have some way to automatically redirect
/// any HTTP request to HTTPS.
/// </para></remarks>
public static IApplicationBuilder UseStrictAndSecureCookies(this IApplicationBuilder app)
{
static void UpdateIfMissing(ref string cookie, ref bool changed, string test, string append)
{
if (!cookie.ContainsOrdinalIgnoreCase(test))
{
cookie += append;
changed = true;
}
}

return app.Use((context, next) =>
{
const string setCookieHeader = "Set-Cookie";
context.Response.OnStarting(() =>
{
var setCookie = context.Response.Headers[setCookieHeader];
if (!setCookie.Any()) return Task.CompletedTask;

var newCookies = new List<string>(capacity: setCookie.Count);
var changed = false;

foreach (var cookie in setCookie.WhereNot(string.IsNullOrWhiteSpace))
{
var newCookie = cookie;

UpdateIfMissing(ref newCookie, ref changed, "SameSite", "; SameSite=Strict");
UpdateIfMissing(ref newCookie, ref changed, "Secure", "; Secure");

newCookies.Add(newCookie);
}

if (changed)
{
context.Response.Headers[setCookieHeader] = new StringValues(newCookies.ToArray());
}

return Task.CompletedTask;
});

return next();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives;

namespace Lombiq.HelpfulLibraries.AspNetCore.Security;

/// <summary>
/// A content security policy directive provider that provides additional permitted host names for <see
/// cref="StyleSrc"/> and <see cref="ScriptSrc"/>.
/// </summary>
public class CdnContentSecurityPolicyProvider : IContentSecurityPolicyProvider
{
/// <summary>
/// Gets the URLs whose <see cref="Uri.Host"/> will be added to the <see cref="StyleSrc"/> directive.
/// </summary>
public static ConcurrentBag<Uri> PermittedStyleSources { get; } = new(new[]
{
new Uri("https://fonts.googleapis.com/css"),
new Uri("https://fonts.gstatic.com/"),
new Uri("https://cdn.jsdelivr.net/npm"),
});

/// <summary>
/// Gets the URLs whose <see cref="Uri.Host"/> will be added to the <see cref="ScriptSrc"/> directive.
/// </summary>
public static ConcurrentBag<Uri> PermittedScriptSources { get; } = new(new[]
{
new Uri("https://cdn.jsdelivr.net/npm"),
});

/// <summary>
/// Gets the URLs whose <see cref="Uri.Host"/> will be added to the <see cref="FontSrc"/> directive.
/// </summary>
public static ConcurrentBag<Uri> PermittedFontSources { get; } = new(new[]
{
new Uri("https://fonts.googleapis.com/"),
new Uri("https://fonts.gstatic.com/"),
});

public ValueTask UpdateAsync(IDictionary<string, string> securityPolicies, HttpContext context)
{
var any = false;

if (PermittedStyleSources.Any())
{
any = true;
MergeValues(securityPolicies, StyleSrc, PermittedStyleSources);
}

if (PermittedScriptSources.Any())
{
any = true;
MergeValues(securityPolicies, ScriptSrc, PermittedScriptSources);
}

if (PermittedFontSources.Any())
{
any = true;
MergeValues(securityPolicies, FontSrc, PermittedFontSources);
}

if (any)
{
var allPermittedSources = PermittedStyleSources.Concat(PermittedScriptSources).Concat(PermittedFontSources);
MergeValues(securityPolicies, ConnectSrc, allPermittedSources);
}

return ValueTask.CompletedTask;
}

private static void MergeValues(IDictionary<string, string> policies, string key, IEnumerable<Uri> sources)
{
var directiveValue = policies.GetMaybe(key) ?? policies.GetMaybe(DefaultSrc) ?? string.Empty;

policies[key] = string.Join(' ', directiveValue
.Split(' ')
.Union(sources.Select(uri => uri.Host))
.Distinct());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Lombiq.HelpfulLibraries.AspNetCore.Security;

/// <summary>
/// The <c>Content-Security-Policy</c> directives defined in the <a href="https://www.w3.org/TR/CSP2/#directives">W3C
/// Recommendation</a>.
/// </summary>
public static class ContentSecurityPolicyDirectives
{
public const string BaseUri = "base-uri";
public const string ChildSrc = "child-src";
public const string ConnectSrc = "connect-src";
public const string DefaultSrc = "default-src";
public const string FontSrc = "font-src";
public const string FormAction = "form-action";
public const string FrameAncestors = "frame-ancestors";
public const string FrameSrc = "frame-src";
public const string ImgSrc = "img-src";
public const string MediaSrc = "media-src";
public const string ObjectSrc = "object-src";
public const string PluginTypes = "plugin-types";
public const string ReportUri = "report-uri";
public const string Sandbox = "sandbox";
public const string ScriptSrc = "script-src";
public const string StyleSrc = "style-src";
public const string WorkerSrc = "worker-src";

public static class CommonValues
{
// These values represent special words so they must be surrounded with apostrophes.
public const string Self = "'self'";
public const string None = "'none'";
public const string UnsafeInline = "'unsafe-inline'";
public const string UnsafeEval = "'unsafe-eval'";

// These values represent allowed protocol schemes.
public const string Https = "https:";
public const string Data = "data:";
public const string Blob = "blob:";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives;

namespace Lombiq.HelpfulLibraries.AspNetCore.Security;

/// <summary>
/// A service for updating the dictionary that will be turned into the <c>Content-Security-Policy</c> header value by
/// <see cref="ApplicationBuilderExtensions.UseContentSecurityPolicyHeader"/>.
/// </summary>
public interface IContentSecurityPolicyProvider
{
/// <summary>
/// Updates the <paramref name="securityPolicies"/> dictionary where the keys are the <c>Content-Security-Policy</c>
/// directives names and the values are the matching directive values.
/// </summary>
public ValueTask UpdateAsync(IDictionary<string, string> securityPolicies, HttpContext context);

/// <summary>
/// Returns the first non-empty directive from the <paramref name="names"/> or <see cref="DefaultSrc"/> or an empty
/// string.
/// </summary>
public static string GetDirective(IDictionary<string, string> securityPolicies, params string[] names)
{
foreach (var name in names)
{
if (securityPolicies.TryGetValue(name, out var value) && !string.IsNullOrWhiteSpace(value))
{
return value;
}
}

return securityPolicies.GetMaybe(DefaultSrc) ?? string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Lombiq.HelpfulLibraries.AspNetCore.Security;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers a Content Security Policy provider that implements <see cref="IContentSecurityPolicyProvider"/> and
/// will be used by <see cref="ApplicationBuilderExtensions.UseContentSecurityPolicyHeader"/>.
/// </summary>
public static IServiceCollection AddContentSecurityPolicyProvider<TProvider>(this IServiceCollection services)
where TProvider : class, IContentSecurityPolicyProvider =>
services.AddScoped<IContentSecurityPolicyProvider, TProvider>();

/// <summary>
/// Configures the session cookie to be always secure. With this configuration the token won't work in an HTTP
/// environment so make sure that HTTPS redirection is enabled.
/// </summary>
public static IServiceCollection ConfigureSessionCookieAlwaysSecure(this IServiceCollection services) =>
services.Configure<SessionOptions>(options => options.Cookie.SecurePolicy = CookieSecurePolicy.Always);
}
Loading