From f2dedded2a8646273c4dd13013b8334d64e02d6f Mon Sep 17 00:00:00 2001 From: vnugent Date: Wed, 22 May 2024 15:33:19 -0400 Subject: Squashed commit of the following: commit 4c973bb81d8cff22a77eb082611746713390c99b Author: vnugent Date: Sun May 19 11:41:55 2024 -0400 feat: Update to follow latest core features commit 7118b66c4f2655db01fd061e43f5214d0dd891e8 Author: vnugent Date: Thu May 2 15:47:22 2024 -0400 refactor: Moved session security middleware commit 97722f178f5e5107fcbdd1df4944a818fedf8722 Merge: f70c94e 84f81db Author: vnugent Date: Sat Apr 20 12:13:23 2024 -0400 Merge branch 'master' into develop commit f70c94e948aa41e90d99f187d8a4791a726bc681 Author: vnugent Date: Sat Apr 20 00:48:49 2024 -0400 fix: Missing session detach flag on close commit 2a2078b8cc3dd216c46419bce7577ae572317955 Author: vnugent Date: Mon Apr 15 16:22:38 2024 -0400 fix: fixed bearer token header requirments from crashing --- .../src/Security/WebSessionSecMiddleware.cs | 118 +++++++++++++++++++++ plugins/SessionProvider/src/SessionProvider.csproj | 16 +-- .../src/SessionProvider.sample.json | 6 +- .../SessionProvider/src/SessionProviderEntry.cs | 16 ++- 4 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 plugins/SessionProvider/src/Security/WebSessionSecMiddleware.cs (limited to 'plugins/SessionProvider') diff --git a/plugins/SessionProvider/src/Security/WebSessionSecMiddleware.cs b/plugins/SessionProvider/src/Security/WebSessionSecMiddleware.cs new file mode 100644 index 0000000..6a21ded --- /dev/null +++ b/plugins/SessionProvider/src/Security/WebSessionSecMiddleware.cs @@ -0,0 +1,118 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: SessionProvider +* File: WebSessionSecMiddleware.cs +* +* WebSessionSecMiddleware.cs is part of SessionProvider which is part of the larger +* VNLib collection of libraries and utilities. +* +* SessionProvider is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* SessionProvider is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Threading.Tasks; +using System.Security.Authentication; +using System.Text.Json.Serialization; + +using VNLib.Utils.Logging; +using VNLib.Plugins.Essentials.Extensions; +using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Essentials.Middleware; + +namespace VNLib.Plugins.Essentials.Sessions +{ + [ConfigurationName("web")] + [MiddlewareImpl(MiddlewareImplOptions.SecurityCritical)] + internal sealed class WebSessionSecMiddleware(PluginBase plugin, IConfigScope config) : IHttpMiddleware + { + private readonly ILogProvider _log = plugin.Log.CreateScope("Session-Sec"); + private readonly SecConfig _secConfig = config.Deserialze(); + + /// + public ValueTask ProcessAsync(HttpEntity entity) + { + ref readonly SessionInfo session = ref entity.Session; + + if (session.IsSet) + { + + /* + * Check if the session was established over a secure connection, + * and if the current connection is insecure, redirect them to a + * secure connection. + */ + if (session.SecurityProcol > SslProtocols.None && !entity.IsSecure) + { + //Redirect the client to https + UriBuilder ub = new(entity.Server.RequestUri) + { + Scheme = Uri.UriSchemeHttps + }; + + _log.Debug("Possbile session TLS downgrade detected, redirecting {con} to secure endpoint", entity.TrustedRemoteIp); + + //Redirect + entity.Redirect(RedirectType.Moved, ub.Uri); + return ValueTask.FromResult(FileProcessArgs.VirtualSkip); + } + + //If session is not new, then verify it matches stored credentials + if (!session.IsNew && session.SessionType == SessionType.Web) + { + /* + * When sessions are created for connections that come from a different + * origin, their origin is stored for later. + * + * If the session was created from a different origin or the current connection + * is cross origin, then the origin must match the stored origin. + */ + + if (_secConfig.EnforceStrictCors) + { + if ((entity.Server.CrossOrigin || session.CrossOrigin) + && !session.CrossOriginMatch + && entity.Server.Origin != null) + { + _log.Debug("Denied connection from {0} due to cross-origin session mismatch.", entity.TrustedRemoteIp); + return ValueTask.FromResult(FileProcessArgs.Deny); + } + } + + if (_secConfig.EnfoceStrictTlsProtocol) + { + //Try to prevent security downgrade attacks + if (!(session.IPMatch && session.SecurityProcol <= entity.Server.GetSslProtocol())) + { + _log.Debug("Possible TLS downgrade attack stopeed from connection {con}", entity.TrustedRemoteIp); + return ValueTask.FromResult(FileProcessArgs.Deny); + } + } + } + } + + return ValueTask.FromResult(FileProcessArgs.Continue); + } + + sealed class SecConfig + { + [JsonPropertyName("strict_cors")] + public bool EnforceStrictCors { get; set; } = true; + + [JsonPropertyName("strict_tls_protocol")] + public bool EnfoceStrictTlsProtocol { get; set; } = true; + } + } +} diff --git a/plugins/SessionProvider/src/SessionProvider.csproj b/plugins/SessionProvider/src/SessionProvider.csproj index 805ff7d..b9f2a04 100644 --- a/plugins/SessionProvider/src/SessionProvider.csproj +++ b/plugins/SessionProvider/src/SessionProvider.csproj @@ -2,18 +2,19 @@ net8.0 + enable VNLib.Plugins.Sessions SessionProvider - en-US - enable True - latest-all - false - + false true + + latest-all + + Vaughn Nugent Vaughn Nugent @@ -23,12 +24,11 @@ Copyright © 2024 Vaughn Nugent https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Sessions https://github.com/VnUgE/VNLib.Plugins.Sessions/tree/master/plugins/SessionProvider - - - README.md LICENSE + True + True diff --git a/plugins/SessionProvider/src/SessionProvider.sample.json b/plugins/SessionProvider/src/SessionProvider.sample.json index 0675fa1..0a3083a 100644 --- a/plugins/SessionProvider/src/SessionProvider.sample.json +++ b/plugins/SessionProvider/src/SessionProvider.sample.json @@ -19,7 +19,11 @@ //time (in seconds) a session is valid for "valid_for_sec": 3600, //The maxium number of connections waiting for the cache server responses - "max_waiting_connections": 100 + "max_waiting_connections": 100, + //Enforce strict cross-origin session checks + "strict_cors": true, + ///Enforces strict TLS to help prevent tls downgrades based on stored session variables (privacy note: this can be leaked through brute-forced if session id is stolen) + "strict_tls_protocol": true }, //If the OAuth provider is enabled, you may enable the optional revocation endpoint diff --git a/plugins/SessionProvider/src/SessionProviderEntry.cs b/plugins/SessionProvider/src/SessionProviderEntry.cs index fa2bd27..2c8a757 100644 --- a/plugins/SessionProvider/src/SessionProviderEntry.cs +++ b/plugins/SessionProvider/src/SessionProviderEntry.cs @@ -32,6 +32,7 @@ using VNLib.Net.Http; using VNLib.Utils; using VNLib.Utils.Logging; using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Extensions.Loading.Routing; namespace VNLib.Plugins.Essentials.Sessions { @@ -46,7 +47,7 @@ namespace VNLib.Plugins.Essentials.Sessions /// protected override void OnLoad() { - List providers = new(); + List providers = []; Log.Verbose("Loading all specified session providers"); @@ -89,6 +90,17 @@ namespace VNLib.Plugins.Essentials.Sessions Log.Information("No session providers loaded"); } + //See if web sessions are loaded + if (this.HasConfigForType()) + { + this.ExportMiddleware( + //Init web session sec middlware + this.GetOrCreateSingleton() + ); + + Log.Debug("Web session security middleware initialized"); + } + Log.Information("Plugin loaded"); } @@ -129,7 +141,7 @@ namespace VNLib.Plugins.Essentials.Sessions } //Return empty session - return new (SessionHandle.Empty); + return ValueTask.FromResult(SessionHandle.Empty); } protected override void Free() -- cgit