Bandolier2k Bandolier2k - 1 year ago 324
Node.js Question

CORS in IIS issue with credentials and wildcard in Access-Control-Allow-Origin

I inherited a fairly basic site that serves up data and handles some socket connections. It's running NodeJS behind IIS using iisnode as a bridge. That's all working fine from a "serve normal pages" perspective.

Part of the problem is that the actual connections to the server are coming from desktop clients where the content is loaded through a different application as a gadget and from potentially changing and varying parts of the network, mobiles devices, etc. etc. i.e. - an unknown number of client domains.

I already set Access-Control-Allow origin to * to just throw the barn doors open but now am getting the following error in the client:


11:29:57.668 Cross-Origin Request Blocked: The Same Origin Policy
disallows reading the remote resource at
http://server/socket.io/?EIO=3&transport=polling&t=1486150196479-0’.
(Reason: Credential is not supported if the CORS header
‘Access-Control-Allow-Origin’ is ‘*’). 1 (unknown)


I have attempted to set the Access-Control-Allow-Credentials explicitly to false (as well as true, as well as leaving it out entirely) but none of my attempts have allowed me to get past this.

The raw response headers look like this currently:

Access-Control-Allow-Credentials: false
Access-Control-Allow-Headers: Origin,Content-Type,Accept
Access-Control-Allow-Methods: GET,HEAD,PUT,POST,DELETE,OPTIONS
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Encoding: gzip
Content-Length: 969
Content-Type: text/html
Date: Fri, 03 Feb 2017 19:30:21 GMT
Server: Microsoft-IIS/8.5
Vary: Accept-Encoding
X-Powered-By: ASP.NET


I can't seem to sort out after looking at A LOT of CORS sites and articles over the last few days why it's still complaining about Credentials - and more specifically, how do I get around this?

Thanks!

UPDATE 2017-02-06

The client code is not terribly exciting. Since the server is NodeJS behind IIS, all I'm really doing for this is implementing a socket connection:

var socket = io('http://' + currentServer, {path: '/broadcast/socket.io', reconnection: false, forceNew: true});
socket.on('update message', function (data) {
// do some fancy things
}


This works from within the same domain.

I've also been doing some more digging based on sideshowbarker's comments that led me to this article There are some extra steps to add the variables, and some other things to get that piece working.

My applicationHost.config currently contains this section:

<location path="Default Web Site">
<system.webServer>
<rewrite>
<allowedServerVariables>
<add name="CAPTURED_ORIGIN" />
<add name="RESPONSE_Access-Control-Allow-Origin" />
</allowedServerVariables>
</rewrite>
</system.webServer>
</location>


and my web.config is here:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Fail bad requests">
<match url="." />
<conditions>
<add input="{HTTP_HOST}" negate="true" pattern="localhost" />
</conditions>
<action type="AbortRequest" />
</rule>
<rule name="Capture Origin Header">
<match url=".*" />
<conditions>
<add input="{HTTP_ORIGIN}" pattern=".+" />
</conditions>
<serverVariables>
<set name="CAPTURED_ORIGIN" value="{C:0}" />
</serverVariables>
<action type="None" />
</rule>
</rules>
<outboundRules>
<rule name="Set-Access-Control-Allow-Origin for known origins">
<match serverVariable="RESPONSE_Access-Control-Allow-Origin" pattern=".+" negate="true" />
<!--<action type="Rewrite" value="{C:0}" /> -->
</rule>
</outboundRules>
</rewrite>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,FastCGI,WebSocket" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="400-599" />
</add>
</traceFailedRequests>
</tracing>
</system.webServer>
</configuration>


on the outboundRules I currently have the action type="Rewrite" line commented out because when I enable that, it throws up an error.

HTTP Error 500.52 - URL Rewrite Module Error.

The page cannot be displayed because an internal server error has occurred.

Most likely causes:
•IIS received the request; however, an internal error occurred during the processing of the request. The root cause of this error depends on which module handles the request and what was happening in the worker process when this error occurred.
•IIS was not able to access the web.config file for the Web site or application. This can occur if the NTFS permissions are set incorrectly.
•IIS was not able to process configuration for the Web site or application.
•The authenticated user does not have permission to use this DLL.
•The request is mapped to a managed handler but the .NET Extensibility Feature is not installed.

Detailed Error Information:
Module: RewriteModule
Notification: SendResponse
Handler: StaticFile
Error Code: 0x80070585

Requested URL: http://localhost:80/iisstart.htm
Physical Path: C:\inetpub\wwwroot\iisstart.htm
Logon Method: Anonymous
Logon User: Anonymous
Request Tracing Directory: C:\inetpub\logs\FailedReqLogFiles


The Failed Request Logs are not overly helpful, they show the following for the warning:

411. -MODULE_SET_RESPONSE_ERROR_STATUS
ModuleName: RewriteModule
Notification: SEND_RESPONSE
HttpStatus: 500
HttpReason: URL Rewrite Module Error.
HttpSubStatus: 52
ErrorCode: Invalid index. (0x80070585)
ConfigExceptionInfo:

Answer Source

The question doesn’t show the client code which is sending the request that causes that error, but:

The client code must be using XHR or the Fetch API (or using jQuery or other library that calls one of those), and that code is either setting the XHR withCredentials property to true or is calling the Fetch Request constructor with an options object having the credentials option set to include.

When that’s the case and the server response has an Access-Control-Allow-Origin: * header, your browser will log the error cited in the question.

So one solution is to change the JavaScript client code so it’s not setting XHR withCredentials to true and not calling the Fetch Request constructor with credentials: 'include'.

Another solution is to make your server-side code take the Origin request-header value and echo it to theAccess-Control-Allow-Origin response-header value.

For IIS, you can do that with the URL Rewrite Module by adding the following to your IIS config file (Web.config or ApplicationHost.config file in %SystemDrive%\inetpub\wwwroot\).

<configuration> 
    <system.webServer> 
        <rewrite> 
            <rules> 
                <rule name="Capture Origin Header"> 
                    <match url=".*" /> 
                    <conditions> 
                        <add input="{HTTP_ORIGIN}" pattern=".+" /> 
                    </conditions> 
                    <serverVariables> 
                        <set name="CAPTURED_ORIGIN" value="{C:0}" /> 
                    </serverVariables> 
                    <action type="None" /> 
                </rule> 
            </rules> 
            <outboundRules> 
                <rule name="Set-Access-Control-Allow-Origin for known origins"> 
                    <match serverVariable="RESPONSE_Access-Control-Allow-Origin"
                           pattern=".+" negate="true" /> 
                    <action type="Rewrite" value="{CAPTURED_ORIGIN}" /> 
                </rule> 
            </outboundRules> 
        </rewrite> 
    </system.webServer> 
</configuration>

Then remove whatever other existing code/config is setting Access-Control-Allow-Origin: *.

Note: The above is a modified version of the example config file in the step-by-step guide Enable CORS for specific domains in IIS using URL Rewrite.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download