Aspnetcore: donetcore 2.2 broke signalr

Created on 7 Dec 2018  路  29Comments  路  Source: dotnet/aspnetcore

I have the latest aspnetcore 2.2.0 locally, and no longer get OnConnected events in SignalR (authenticated hub). Still using SignalR 1.0.4 on server, client is angular, and doesn't work with either 1.0.4 or 1.1.0. Same client works with the previous aspnetcore version (2.1.6) with SignalR 1.0.3 and 1.0.4. I get the same problem with both regular SignalR and Azure SignalR.

Client side

"@aspnet/signalr": "^1.1.0",

area-signalr

Most helpful comment

This seems to be a workaround for now.

          services.AddCors(options =>
            {
                options.AddPolicy(
                    "AllowAny",
                    x =>
                    {
                        x.AllowAnyHeader()
                        .AllowAnyMethod()
                        .SetIsOriginAllowed(isOriginAllowed: _ => true)
                        .AllowCredentials();
                    });
            });

All 29 comments

Are you able to connect at all? Could you collect server and client logs?

It's an Angular client. I see the OPTIONS call for negotiate, but the normal POST that comes after that never happens: this is missing:

POST https://prodapi.northstar.app/stops/negotiate

So it stalls pretty early on.

Ok, so that looks like it might be a CORS issue. Does your server do:

o.AddPolicy("Everything", p =>
{
    p.AllowAnyHeader()
      .AllowAnyMethod()
      .AllowAnyOrigin()
      .AllowCredentials();
});

It does this:

            services.AddCors(action =>
                action.AddPolicy(policyName,
                    builder =>
                        builder
                            .AllowAnyMethod()
                            .AllowAnyHeader()
                            .AllowAnyOrigin()
                            .AllowCredentials()));

Where policyName is "AllowAllCorsPolicy"

Later I do

.UseCors(CorsPolicyName)

In the log files I see:

NorthStar.API> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
NorthStar.API>       Request starting HTTP/1.1 OPTIONS http://localhost:44319/stops/negotiate  
NorthStar.API> warn: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[11]
NorthStar.API>       The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. Configure the policy by listing individual origins if credentials needs to be supported.
NorthStar.API> info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
NorthStar.API>       CORS policy execution successful.
NorthStar.API> info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
NorthStar.API>       Request finished in 23.3594ms 204 


So in 2.2 .AllowAnyOrigin() + .AllowCredentials() is no longer allowed as it is insecure. You need to explicitly set the allowed origins using WithOrigins()

I see the warning in the log file above. Do you mean I have to explicitly enumerate every origin that can call my Web API? I added withCredentials, I think, for SignalR to work... is there something that can simplify this setup?

I use the WebApi in dev, test, prod, so would I need to add all possible angular client Urls?
I also call the Api from a Web Job... it uses HttpClient... the WebJob runs in the same App Service as the API... do I need to enable that somehow?

                        .WithOrigins(
                            "http://localhost:4200",
                            "https://portal.northstar.app",
                            "https://portaldev.northstar.app")

Note - this code solves the issue...but this is a pretty sneaky change to make that breaks a lot and no word about it...

Blogged about it here:

https://trailheadtechnology.com/breaking-change-in-aspnetcore-2-2-for-signalr-and-cors/

I ran into this issue as well, and feel like a warning in debug is not significant enough.

Since this completely denies connection, I think this should throw an exception at runtime.

We are also using .AllowAnyOrigin() + .AllowCredentials() and do not have a known list of origins because we are publishing a Cordova application to mobile devices. Any suggestions?

@jsheetzati I'm not entirely familiar with how Cordova works - is each client assigned an arbitrary host? Allowing a wildcard origin with credentials has a known XSRF vulnerability and there aren't guidelines for securing this outside of listing individual allowed hosts.

We will have to investigate further and try some things out, but I think we will be okay.

I found that the clients makes a preflight OPTIONS request with the following Accept header text/html,application/xhtml+xm鈥lication/xml;q=0.9,*/*;q=0.8. If the header is removed or replaced with application/json the aspnet backend responds just fine with a json object containing the negotiation metadata. I expect it has to do with this issue https://github.com/aspnet/AspNetCore/issues/3323.

I did not find where header is set in the typescript source of the client. I assume these are default POST headers. Could we set the Accept header for the negotiate request explicitly?

@remkoboschker the CORS middleware does no filtering based on the Accept header and the behavior for allowed headers should be identical (if not less restrictive) as compared to previous versions of ASP.NET Core. That said, could you file a separate issue detailing what your issue is and possibly a sample repro app? It would help to keep this thread focused on the original issue.

@pranavkm I could be mistaken, but I do believe my comment is on topic, because I have the same issue as @johnkwaters. I also found a work around in the sense that changing the accept header does make the subsequent POST to negotiate happen. Also the issue I refer to states that the CORS middleware does in fact filter on the accept header. The accepted media types make it a non simple request but the CORS middleware does not recognise it as such and does not return the access control allow headers, if I have understood the issue correctly.

EDIT I noticed that the issues mentions the content-type header not the accept header. So I might be mistaken. Still changing the accept header works. I will dig a bit further and open a new issue if can pinpoint. Thank you.

We are also using .AllowAnyOrigin() + .AllowCredentials() and do not have a known list of origins because we are publishing a Cordova application to mobile devices. Any suggestions?

We will have to investigate further and try some things out, but I think we will be okay.

@jsheetzati: We've the same problem. Did you find a solution/workaround?

We are still using UIWebView in Cordova for our iOS application. It seems like the UIWebView currently does not validate CORS headers. We will have to revisit this issue when we switch over to using WKWebView instead. We do not want to whitelist a null or file origin for our .NET API.

One option that I came across is using https://github.com/wymsee/cordova-HTTP which will proxy requests through native iOS code before hitting our backend server. CORS does not come into play with this scenario because the request isn't originating at the browser level. Unfortunately, changing all our client code to use the proxy plugin is not a small effort and less than ideal.

Our QA process involves creating a VM with a dynamic IP for testing. There is no way for me to know the origin before pushing my code out to the QA server.

What other options are there to allow for any origin, or any kind of wildcard?

This is a blocker for upgrading to 2.2!

This is also a very sneaky change that was hit when upgrading to 2.2. Please advise on work around as we do not know all of the origins that will be connecting to the SignalR hub.

I also cannot specify all origins as these may come from an app with an unknown IP address.

This functionality did work prior to 2.2. Irrespective of a XSRF vuln, the "fix" by preventing .AllowAnyOrigin() + .AllowCredentials() has now rendered SignalR broken and unusable which is a serious blocker when upgrading. Why not still allow these and let the user know it's not secure and show a warning? Or provide a workaround that can still allow this functionality.

From https://github.com/aspnet/AspNetCore/issues/3106

This seems to be a workaround for now.

          services.AddCors(options =>
            {
                options.AddPolicy(
                    "AllowAny",
                    x =>
                    {
                        x.AllowAnyHeader()
                        .AllowAnyMethod()
                        .SetIsOriginAllowed(isOriginAllowed: _ => true)
                        .AllowCredentials();
                    });
            });

We have an open source project for error collection and we need to support both cors and credentials from clients connecting from anywhere in the world with a valid api key.

The CORS spec, and numerous security researchers explain why allowing credentials with an allowed host of * and then reflecting back the actual incoming host is a security vulnerability.

This vulnerability was fixed in 2.2 in a manner that allows the browser to reflect the problem. In 3.0 it is likely we will go further than throw exceptions server side on such misconfigurations.

As this is a fix, not a bug, I'm afraid the behaviour will not be reverted. If you require the same, insecure, behaviour as was previously present then our only advice is to write your own cors middleware (or borrow the 2.1 source) to add the headers as you see fit.

@blowdart any recommendations then for having a public api that requires cors?

Don't use credentials, like the spec says, or have registration of the host names that will host the API

But what is the solution for this common and widespread use case?
When you do not have a known list of origins because you are publishing a Cordova application to mobile devices. Any suggestions?

This worked for me. Thanks dylanvdmerwe !

services.AddCors(options =>
{
options.AddPolicy(
"AllowAny",
x =>
{
x.AllowAnyHeader()
.AllowAnyMethod()
.SetIsOriginAllowed(isOriginAllowed: _ => true)
.AllowCredentials();
});
});

But what is the solution for this common and widespread use case?
When you do not have a known list of origins because you are publishing a Cordova application to mobile devices. Any suggestions?

The answer of 'QWAQWA1' doesn't work for me using SignalR. I get following error:

"Access to XMLHttpRequest at '/hub/negotiate' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource"

Any work arounds for asp.net core 3.0 when you have a public api?

This issue really shouldn't be closed, it causes a huge amount of chaos and needs to be looked at.

This seems to be a workaround for now.

          services.AddCors(options =>
            {
                options.AddPolicy(
                    "AllowAny",
                    x =>
                    {
                        x.AllowAnyHeader()
                        .AllowAnyMethod()
                        .SetIsOriginAllowed(isOriginAllowed: _ => true)
                        .AllowCredentials();
                    });
            });

This works for me as well. Thank you very much, dylanvdmerwe!

I am disgruntled with changes such like this. If a developer (or his customer) wants to strictly specify origins, he'll do this. But I don't like being treated like a baby!

Was this page helpful?
0 / 5 - 0 ratings