0

When using Bearer Token Authentication, SignalR passes the JWT token through the query string. Obviously this bears the risk that the the token gets logged along with the request and thus anybody who can read the log can impersonate by copying the token.

The docs say:

If you have concerns about logging this data with your server logs, you can disable this logging entirely by configuring the Microsoft.AspNetCore.Hosting logger to the Warning level or above (these messages are written at Info level)

At this stage a side question pops into my mind: who guarantees that if the log level is Warning and something bad happens, the log won't still contain the request URL?

The docs continue with:

If you still want to log certain request information, you can write a middleware to log the data you require and filter out the access_token query string value (if present).

I guess the idea is to entirely switch off the default logging of requests and replace it with a custom logging middleware. This does not sound trivial to me. So I was wondering:

  • Is there any way of hooking into the logger and then customize what's actually being logged?
  • Or can we leverage the existing HTTP Logging for that? At the first glance it also seems to be a all-or-nothing option in regards to logging of query strings or is there a way of customizing that?
  • Is there a NuGet package that solves the issue?
  • How did others solve this problem?
Dejan
  • 9,150
  • 8
  • 69
  • 117
  • I would focus on a way to remove the authentication from the query string completely. Maybe https://stackoverflow.com/questions/54825739/add-headers-to-aspnet-signalr-javascript-client could help with that. If you want to change what is logged you will probably need to deep dive into the documentation for the logging framework, or change framework – fredrik Dec 14 '21 at 21:11
  • `anybody who can read the log can impersonate by copying the token` so you are now trying to avoid the admins who can access your app logs, then I think what you can do is prevent the tokens not be written in logs, or in other words, you should write custom logging feature. Maybe [this document](https://learn.microsoft.com/en-us/dotnet/core/extensions/custom-logging-provider) can help you. I also found [an answer](https://stackoverflow.com/questions/60778831/is-there-a-way-to-log-the-values-from-the-data-dictionary-of-an-exception-in-ne) which is similar. – Tiny Wang Dec 15 '21 at 02:30

1 Answers1

1

I've resorted to take an approach where the JWT token does need to be sent as part of the query string, as explained here.

To summarize, when set as a cookie, the cookie will automatically be sent as part of the SignalR connection initialization by the browser:

document.cookie = `X-Authorization=${token}; path=/; secure; samesite=strict`; // https://stackoverflow.com/a/48618910/331281
const newConnection = new HubConnectionBuilder()
    .withUrl('/background-queue-hub', {
        skipNegotiation: true, // to avoid CORS problems (see: https://stackoverflow.com/a/52913505/331281)
        transport: HttpTransportType.WebSockets,
    })
...
    

However, this runs the risk of CSWSH. So, server-side we have to check the origin header to mitigate that. It can be done right where the cookie value is copied to the JWT Bearer authentication context:

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => // https://stackoverflow.com/a/66485247/331281
{
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            // for SignalR authentication we need to read the access token form the cookie
            // since we don't want to pass the authentication token through the query string
            // as the query string is being logged
            if (context.HttpContext.Request.Path.StartsWithSegments(SignalRHubPath))
            {
                var allowedOrigins = Configuration["SignalR:AllowedOrigins"].Split(';');
                if (allowedOrigins.Contains(context.Request.Headers["Origin"].ToString())) // see: https://www.tpeczek.com/2017/07/preventing-cross-site-websocket.html
                {
                    context.Token = context.Request.Cookies["X-Authorization"];
                }
                else
                {
                    context.Response.StatusCode = StatusCodes.Status403Forbidden;
                }
            }
            return Task.CompletedTask;
        }
    };
});
Dejan
  • 9,150
  • 8
  • 69
  • 117