azure-signalr icon indicating copy to clipboard operation
azure-signalr copied to clipboard

SignalR is exposing access_token in query params

Open AMAhmadMuztaba opened this issue 1 year ago • 3 comments

SignalR is sending access_token in the query parmeter . But in a recent pentest, it is shown as a 'two bomb finding'.

Capture

I have gone though "https://learn.microsoft.com/en-us/aspnet/core/signalr/security?view=aspnetcore-7.0" -where Microsoft claims sending though query params is as secure as sending through headers . why is it secure? is there any way to hide the access token from the network call so that it doesn't get exposed?

AMAhmadMuztaba avatar Mar 09 '23 08:03 AMAhmadMuztaba

Browser does not allow adding headers with WebSocket connections, that’s why the token is in the query string, that is a general practice for JS WebSocket connections.

From a security perspective, it doesn't really matter where the access token is stored. In an ordinary HTTP request it would be stored in the header, or in a message after the WebSocket connection is established. However, many websockets for clients don't support client headers, and both of these are equally accessible to an attacker who can inspect traffic. Connections default to being over TLS these days, so from the outside you can't access query params, nor can you access the contents of messages.

Traditionally it was considered poor practice to have credentials in query params because URLs can get stored in places such as logs for proxies, browser history, etc. However, neither of those concerns apply to websockets (a browser won't keep history of the connections made by a page), and proxies do not have access to the URL when there is a TLS tunnel. This concern arose when non-TLS interactions were the default. For comparison, most OAuth flows result in an endpoint access being made with an access_token query param.

vicancy avatar Mar 10 '23 01:03 vicancy

What about HTTP server logs? I got a negative pentest result from passing access tokens in the URI parameter.

A web socket upgrade request is actually a GET request, and it seems to be logged along with an entire URL to server logs, possibly giving server admins impersonation-ready credentials.

I've studied the code of SignalR TS (js) library and the token is always passed in the case of a web browser: https://github.com/dotnet/aspnetcore/blob/ec9a2d83b54ee370a2ae75c632d5f442d09ba482/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts#L75. I thought the library is somehow compatible with the authentication ticket method, but unfortunately, it explicitly requires access tokens instead of one-time access tickets.

A reference to vulnerability description: https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url

michal-klimek avatar May 16 '23 14:05 michal-klimek

Up, still no solutions or workaround here ?

JeanRessouche avatar Mar 12 '24 10:03 JeanRessouche

Any update to this yet? We got similar negative feedback in pen testing

pwmather avatar Jul 10 '24 13:07 pwmather

What about HTTP server logs? I got a negative pentest result from passing access tokens in the URI parameter.

A web socket upgrade request is actually a GET request, and it seems to be logged along with an entire URL to server logs, possibly giving server admins impersonation-ready credentials.

I've studied the code of SignalR TS (js) library and the token is always passed in the case of a web browser: https://github.com/dotnet/aspnetcore/blob/ec9a2d83b54ee370a2ae75c632d5f442d09ba482/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts#L75. I thought the library is somehow compatible with the authentication ticket method, but unfortunately, it explicitly requires access tokens instead of one-time access tickets.

A reference to vulnerability description: https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url

Preventing the logging of the access token on the server side isn't actually hard, I'm using Serilog and Serilog.Enrichers.Sensitive to achieve that. In case someone finds this useful, here's my code:

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(configuration)
    .Enrich.WithSensitiveDataMasking(options =>
    {
        options.MaskingOperators.Clear();
        options.MaskingOperators.Add(new AccessTokenMaskingOperator());
    })
    .CreateLogger();
public class AccessTokenMaskingOperator : RegexMaskingOperator
{
    private const string pattern = "access_token=[A-z0-9\\-_]+\\.[A-z0-9\\-_]+\\.[A-z0-9\\-_]+";

    public AccessTokenMaskingOperator() : base(pattern) { }

    protected override string PreprocessMask(string mask, Match match)
    {
        return "access_token=***REDACTED***";
    }
}

This would turn the logs into something like:

Request starting HTTP/1.1 GET http://localhost:8080/hubs/sample?id=someid&access_token=***REDACTED*** - null null

Hope this helps

Et3rnos avatar Jul 17 '24 11:07 Et3rnos

The same issue was reported for us in a penetration test

tartieret avatar Jul 23 '24 19:07 tartieret

What about HTTP server logs? I got a negative pentest result from passing access tokens in the URI parameter.

A web socket upgrade request is actually a GET request, and it seems to be logged along with an entire URL to server logs, possibly giving server admins impersonation-ready credentials.

I've studied the code of SignalR TS (js) library and the token is always passed in the case of a web browser: https://github.com/dotnet/aspnetcore/blob/ec9a2d83b54ee370a2ae75c632d5f442d09ba482/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts#L75. I thought the library is somehow compatible with the authentication ticket method, but unfortunately, it explicitly requires access tokens instead of one-time access tickets.

A reference to vulnerability description: https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url

The URI with access_token is used to connect to Azure SignalR service and the access token is not logged anywhere inside SignalR service.

I am closing the issue as the reason is explained in https://github.com/Azure/azure-signalr/issues/1770#issuecomment-1463086043

vicancy avatar Jul 24 '24 02:07 vicancy

out HTTP server logs? I got a negative pe

Not really helping, can be logged by a reverse proxy not in our control, like an ingress controller

JeanRessouche avatar Jul 30 '24 08:07 JeanRessouche

What about HTTP server logs? I got a negative pentest result from passing access tokens in the URI parameter. A web socket upgrade request is actually a GET request, and it seems to be logged along with an entire URL to server logs, possibly giving server admins impersonation-ready credentials. I've studied the code of SignalR TS (js) library and the token is always passed in the case of a web browser: https://github.com/dotnet/aspnetcore/blob/ec9a2d83b54ee370a2ae75c632d5f442d09ba482/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts#L75. I thought the library is somehow compatible with the authentication ticket method, but unfortunately, it explicitly requires access tokens instead of one-time access tickets. A reference to vulnerability description: https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url

The URI with access_token is used to connect to Azure SignalR service and the access token is not logged anywhere inside SignalR service.

I am closing the issue as the reason is explained in #1770 (comment)

Please reopen, i don't understand why you mention Azure here, SignalR is used outside of it, in place where it's logged!

JeanRessouche avatar Jul 30 '24 08:07 JeanRessouche

out HTTP server logs? I got a negative pe

Not really helping, can be logged by a reverse proxy not in our control, like an ingress controller

To clarify, here is the Azure SignalR SDK repo and the issue talks about acecss_token used for SignalR Clients to connect to Azure SignalR. SignalR clients goes through the TLS tunnel to Azure SignalR, as explained before, proxies in between (if any) do not have access to the data inside the TLS tunnel, queries are encrypted.

One thing to mention is if you really have concerns about the token leak, for example, some client might get hacked and the token is leaked, you could use this AccessTokenLifetime option to make this token lifetime short-lived, so that even if the token is leaked it is expired.

vicancy avatar Jul 31 '24 07:07 vicancy