From 335659f2a3d412aa040fd77d871366dc4d4f8501 Mon Sep 17 00:00:00 2001 From: vnugent Date: Mon, 15 Jan 2024 13:27:11 -0500 Subject: optional token query check --- .../src/SecurityProvider/AccountSecProvider.cs | 80 ++++++++++++++-------- 1 file changed, 53 insertions(+), 27 deletions(-) (limited to 'plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs') diff --git a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs index 4d1c3ba..f9d7ef7 100644 --- a/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs +++ b/plugins/VNLib.Plugins.Essentials.Accounts/src/SecurityProvider/AccountSecProvider.cs @@ -390,12 +390,13 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider using JsonDocument data = jwt.GetPayload(); //Get iat time - if (data.RootElement.TryGetProperty("iat", out JsonElement iatEl)) + if (data.RootElement.TryGetProperty("iat", out JsonElement iatEl) + && iatEl.ValueKind == JsonValueKind.Number) { - //Try to get iat in uning seconds + //Try to get iat in unint seconds isValid &= iatEl.TryGetInt64(out long iatSec); - //Recover dto from seconds + //Recover dto from unix seconds regardless of int success DateTimeOffset iat = DateTimeOffset.FromUnixTimeSeconds(iatSec); //Verify iat against current time with allowed disparity @@ -411,32 +412,29 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider } //Check the audience matches the request uri - if(data.RootElement.TryGetProperty("aud", out JsonElement tokenOriginEl)) + if(data.RootElement.TryGetProperty("aud", out JsonElement tokenOriginEl) + && tokenOriginEl.ValueKind == JsonValueKind.String) { - string? tokenOrigin = tokenOriginEl.GetString(); + string? unsafeUserOrigin = tokenOriginEl.GetString(); string? requestOrigin = null; //If strict origin is enabled, we need to check against the request uri - Uri? origin = _config.EnforceSameOriginToken ? - entity.Server.RequestUri : - entity.Session.SpecifiedOrigin; - + Uri origin = entity.Session.CrossOrigin && _config.EnforceSameOriginToken ? + entity.Session.SpecifiedOrigin! : + (entity.Server.Origin ?? entity.Server.RequestUri); //Finally fall back to the request uri if no origin is specified + - //Check origin matches stored origin - if (origin != null) - { - requestOrigin = $"{origin.Scheme}://{origin.Authority}"; + requestOrigin = origin.GetLeftPart(UriPartial.Authority); - //Make sure the token href matches the request uri - isValid &= string.Equals(tokenOrigin, requestOrigin, StringComparison.OrdinalIgnoreCase); - } + //Make sure the token href matches the request uri + isValid &= string.Equals(unsafeUserOrigin, requestOrigin, StringComparison.OrdinalIgnoreCase); if (!isValid) { _logger.Debug("Client security OTP JWT origin mismatch from {ip} : {current} != {token}", entity.TrustedRemoteIp, requestOrigin, - tokenOrigin + unsafeUserOrigin ); } } @@ -446,20 +444,48 @@ namespace VNLib.Plugins.Essentials.Accounts.SecurityProvider } //Check the subject (path) matches the request uri - if (data.RootElement.TryGetProperty("path", out JsonElement tokenPathEl)) + if (data.RootElement.TryGetProperty("path", out JsonElement tokenPathEl) + && tokenPathEl.ValueKind == JsonValueKind.String) { - string? path = tokenPathEl.GetString(); + + ReadOnlySpan unsafeUserPath = tokenPathEl.GetString(); + /* + * Query parameters are optional, so we need to check if the path contains a + * query, if so we can compare the entire path and query, otherwise we need to + * compare the path only + */ + if (unsafeUserPath.Contains("?", StringComparison.OrdinalIgnoreCase)) + { + //Compare path and query when possible + string requestPath = entity.Server.RequestUri.PathAndQuery; - //Make sure the token href matches the request uri - isValid &= string.Equals(path, entity.Server.RequestUri.PathAndQuery, StringComparison.OrdinalIgnoreCase); + isValid &= unsafeUserPath.Equals(requestPath, StringComparison.OrdinalIgnoreCase); - if (!isValid) + if (!isValid && _logger.IsEnabled(LogLevel.Debug)) + { + _logger.Debug("Client security OTP JWT path mismatch from {ip} : {current} != {token}", + entity.TrustedRemoteIp, + requestPath, + unsafeUserPath.ToString() + ); + } + } + else { - _logger.Debug("Client security OTP JWT path mismatch from {ip} : {current} != {token}", - entity.TrustedRemoteIp, - entity.Server.RequestUri.PathAndQuery, - path - ); + //Use path only + string requestPath = entity.Server.RequestUri.LocalPath; + + //Compare path only + isValid &= unsafeUserPath.Equals(requestPath, StringComparison.OrdinalIgnoreCase); + + if (!isValid && _logger.IsEnabled(LogLevel.Debug)) + { + _logger.Debug("Client security OTP JWT path mismatch from {ip} : {current} != {token}", + entity.TrustedRemoteIp, + requestPath, + unsafeUserPath.ToString() + ); + } } } else -- cgit