aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-05-02 15:47:22 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-05-02 15:47:22 -0400
commit7118b66c4f2655db01fd061e43f5214d0dd891e8 (patch)
treed553c97930d1627ce587028f2a096121163a00ca /plugins
parent97722f178f5e5107fcbdd1df4944a818fedf8722 (diff)
refactor: Moved session security middleware
Diffstat (limited to 'plugins')
-rw-r--r--plugins/SessionProvider/src/Security/WebSessionSecMiddleware.cs118
-rw-r--r--plugins/SessionProvider/src/SessionProvider.csproj16
-rw-r--r--plugins/SessionProvider/src/SessionProvider.sample.json6
-rw-r--r--plugins/SessionProvider/src/SessionProviderEntry.cs16
4 files changed, 145 insertions, 11 deletions
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<SecConfig>();
+
+ ///<inheritdoc/>
+ public ValueTask<FileProcessArgs> 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,19 +2,20 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
+ <Nullable>enable</Nullable>
<RootNamespace>VNLib.Plugins.Sessions</RootNamespace>
<AssemblyName>SessionProvider</AssemblyName>
- <NeutralLanguage>en-US</NeutralLanguage>
- <Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
- <AnalysisLevel>latest-all</AnalysisLevel>
- <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
-
+ <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<!-- Resolve nuget dll files and store them in the output dir -->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<PropertyGroup>
+ <AnalysisLevel Condition="'$(BuildingInsideVisualStudio)' == true">latest-all</AnalysisLevel>
+ </PropertyGroup>
+
+ <PropertyGroup>
<Authors>Vaughn Nugent</Authors>
<Company>Vaughn Nugent</Company>
<Product>SessionProvider</Product>
@@ -23,12 +24,11 @@
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Plugins.Sessions</PackageProjectUrl>
<RepositoryUrl>https://github.com/VnUgE/VNLib.Plugins.Sessions/tree/master/plugins/SessionProvider</RepositoryUrl>
- </PropertyGroup>
-
- <PropertyGroup>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
+ <RequireLicenseAcceptance>True</RequireLicenseAcceptance>
</PropertyGroup>
+
<ItemGroup>
<None Include="..\..\..\LICENSE">
<Pack>True</Pack>
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
///<inheritdoc/>
protected override void OnLoad()
{
- List<RuntimeSessionProvider> providers = new();
+ List<RuntimeSessionProvider> 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<WebSessionSecMiddleware>())
+ {
+ this.ExportMiddleware(
+ //Init web session sec middlware
+ this.GetOrCreateSingleton<WebSessionSecMiddleware>()
+ );
+
+ 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()