Fix open-redirect token leak and related auth hardening
Security review fixes: - Constrain OAuth connect returnUrl to a site-relative path so the redeemable token_key can't be redirected off-domain (was a refresh- token leak / connection hijack) - Route all login redirects (entra/dev/local) through ToLocalReturnUrl, also closing a protocol-relative // open redirect in local-login - Neutralize CSV formula prefixes in both audit-log exporters via CsvSanitizer - Force Secure flag on the prod auth cookie (Always, not SameAsRequest) - Gate admin pages with an app_role-claim "Admin" policy instead of a render-time check Findings and rationale recorded in SECURITY-TODO.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using SharepointToolbox.Web.Core.Models;
|
||||
using SharepointToolbox.Web.Infrastructure.Persistence;
|
||||
using SharepointToolbox.Web.Services.Export;
|
||||
using SharepointToolbox.Web.Services.Session;
|
||||
|
||||
namespace SharepointToolbox.Web.Services.Audit;
|
||||
@@ -41,23 +42,18 @@ public class AuditService : IAuditService
|
||||
sb.AppendLine("Timestamp,UserEmail,UserDisplay,UserRole,Action,Client,Sites,Details");
|
||||
foreach (var e in entries.OrderByDescending(x => x.Timestamp))
|
||||
{
|
||||
// CsvSanitizer adds spreadsheet formula-injection guards (= + - @) on top of
|
||||
// RFC 4180 quoting; the user/display/client/site fields are user-controlled.
|
||||
sb.AppendLine(string.Join(",",
|
||||
CsvEscape(e.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")),
|
||||
CsvEscape(e.UserEmail),
|
||||
CsvEscape(e.UserDisplay),
|
||||
CsvEscape(e.UserRole.ToString()),
|
||||
CsvEscape(e.Action),
|
||||
CsvEscape(e.ClientName),
|
||||
CsvEscape(string.Join("; ", e.Sites)),
|
||||
CsvEscape(e.Details)));
|
||||
CsvSanitizer.Escape(e.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")),
|
||||
CsvSanitizer.Escape(e.UserEmail),
|
||||
CsvSanitizer.Escape(e.UserDisplay),
|
||||
CsvSanitizer.Escape(e.UserRole.ToString()),
|
||||
CsvSanitizer.Escape(e.Action),
|
||||
CsvSanitizer.Escape(e.ClientName),
|
||||
CsvSanitizer.Escape(string.Join("; ", e.Sites)),
|
||||
CsvSanitizer.Escape(e.Details)));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string CsvEscape(string value)
|
||||
{
|
||||
if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
|
||||
return $"\"{value.Replace("\"", "\"\"")}\"";
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user