IIS outbound rewite rule with condition based on the request (web.config) - iis

I currently have the problem that IIS serves all my cookies with the sameSite=lax attribute after an update of .Net Framework on Windows Server (https://support.microsoft.com/en-us/help/4524419/kb4524419)
The problem is similar to how SameSite attribute added to my Asp.net_SessionID cookie automatically?
This breaks the functionality of most of the IFrames that are in use in webpages with another domain, as the browser does not send the ASP.Net Session-ID back to the server with subsequent requests.
Now while there are some suggestions in the above-mentioned thread they do not really work for me. This is due to Safaris nonstandard behavior. Safari on MacOSX and iOS 12.x treats the value "None" for the sameSite-attribute as unknown and therefore sets the value to "Strict" which again breaks the functionality of the IFrames for Safari users.
Now I wonder whether it is possible to define an outbound rewrite rule in the IIS web.config that first checks the request-header to see if the client is using a Safari browser. Depending on the Client browser, version different rewrite-outbound rules should change the cookies corresponding to what the browser expects.
Is it possible to write outbound rules with conditions based on the request? I did not find any documentation or website indicating this works...

I modified upon several SO answers to come up with this URL rewrite that adds SameSite=None to session cookies, and also remove SameSite=None from all cookies for most incompatible browsers. The aim of this rewrite is to preserve the "legacy" behaviour pre-Chrome 80. It specifically covers the Safari on MacOSX and iOS 12.x scenario you mention.
Full write-up in my Coder Frontline blog:
<rewrite>
<outboundRules>
<preConditions>
<!-- Checks User Agent to identify browsers incompatible with SameSite=None -->
<preCondition name="IncompatibleWithSameSiteNone" logicalGrouping="MatchAny">
<add input="{HTTP_USER_AGENT}" pattern="(CPU iPhone OS 12)|(iPad; CPU OS 12)" />
<add input="{HTTP_USER_AGENT}" pattern="(Chrome/5)|(Chrome/6)" />
<add input="{HTTP_USER_AGENT}" pattern="( OS X 10_14).*(Version/).*((Safari)|(KHTML, like Gecko)$)" />
</preCondition>
</preConditions>
<!-- Adds or changes SameSite to None for the session cookie -->
<!-- Note that secure header is also required by Chrome and should not be added here -->
<rule name="SessionCookieAddNoneHeader">
<match serverVariable="RESPONSE_Set-Cookie" pattern="((.*)(ASP.NET_SessionId)(=.*))(SameSite=.*)?" />
<action type="Rewrite" value="{R:1}; SameSite=None" />
</rule>
<!-- Removes SameSite=None header from all cookies, for most incompatible browsers -->
<rule name="CookieRemoveSameSiteNone" preCondition="IncompatibleWithSameSiteNone">
<match serverVariable="RESPONSE_Set-Cookie" pattern="(.*)(SameSite=None)" />
<action type="Rewrite" value="{R:1}" />
</rule>
</outboundRules>
</rewrite>
This should work for most ASP .Net and ASP .Net Core applications, although newer Frameworks have proper code and config options to let you control this behaviour. I would recommend researching all the options available to you before using my rewrite above.

Related

Windows IIS ARR Reverse Proxy Encoding Issue

We have an environment with an Windows 2019 Server IIS 10, which is acting as Reverse Proxy (ARR) for my IIS Server farm (Application Request Routing 3.0 and URL Rewrite 2.1). We send the users name in the HTTP headers. But my ARR somehow screws up the encoding (we are using german special characters, e.g. ö,ü,ß...), so when i check the respone of the WebServer it shows me: H%C3%B6lmuth M%C3%A4%C3%9Fterm%C3%BCller instead of Hölmuth Mäßtermüller.
I have an old environment with Windows 2012R2 Server with the same configuration, in this environment the display of the name is correct. I have checked all kind of settings between old and new servers, but cannot find any difference.
Futher i have used Failed Request Loggins and Network Monitor to see what the server receives and sends, below find the results.
Received Request on the IIS ARR (old and new):
X-AUTHENTICATE-FamilyName: M%C3%A4%C3%9Fterm%C3%Bcller
X-AUTHENTICATE-GivenName: H%C3%83%C2%B6lmuth
X-AUTHENTICATE-cn: H%C3%B6lmuth M%C3%A4%C3%9Fterm%C3%BCller
Request send to the IIS (new):
X-AUTHENTICATE-FamilyName: M%C3%A4%C3%9Fterm%C3%BCller
X-AUTHENTICATE-GivenName: H%C3%83%C2%B6lmuth
X-AUTHENTICATE-cn: H%C3%B6lmuth M%C3%A4%C3%9Fterm%C3%BCller
Request send to the IIS (old):
X-AUTHENTICATE-FamilyName: Mäßtermüller
X-AUTHENTICATE-GivenName: Hölmuth
X-AUTHENTICATE-cn: Hölmuth Mäßtermüller
Anyone has an idea how i can change this behaviour? Help would be much appreciated.
Can you give me an example how to use the {UNENCODED_URL} variable. Currently we are using the rewrite module to route requests to specific Server Farms, see my rules below:
<rewrite>
<globalRules useOriginalURLEncoding="true">
<rule name="ARR_BPBP-DEV_loadbalance" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<action type="Rewrite" url="http://BPBP-DEV/{R:0}" />
<conditions>
<add input="{HTTP_HOST}" pattern="bmi-bpbp-dev.vecos.at" />
</conditions>
</rule>
<rule name="ARR_BPBP-TEST_loadbalance" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<action type="Rewrite" url="http://BPBP-TEST/{R:0}" />
<conditions>
<add input="{HTTP_HOST}" pattern="bmi-bpbp-test.vecos.at" />
</conditions>
<serverVariables>
</serverVariables>
</rule>
</globalRules>
How can i adapt the rules to use the {UNENCODED_URL} variable?
When an HTTP request arrives on Windows, the latest HTTP.sys encodes both URL and HTTP headers, and puts the original URL in UNENCODED_URL server variable so that it can be recovered afterwards.
However, the original headers (such as X-AUTHENTICATE-FamilyName: Mäßtermüller) do not seem to be preserved (no clear documentation on that), so there isn't any easy way to recover them.
If you want to modify the the header from X-AUTHENTICATE-FamilyName: M%C3%A4%C3%9Fterm%C3%BCller back to X-AUTHENTICATE-FamilyName: Mäßtermüller, the only way I can think of is to write a custom IIS module to perform the decoding step.
Alternatively, you might modify your other code to accept such encoded header values (and decode them when needed in your code), as anyway that's how Windows/IIS behaves now and you cannot fight it.
Try to set the useOriginalURLEncoding to false, and URL rewrite will no longer encode the urls when using the {UNENCODED_URL} variable in the rules.
To set the flag to go IIS Manager then select Configuration Editor and go to the section system.webServer/rewrite/rules, where you will find the useOriginalURLEncoding flag.

how to create multiple custom 503 pages in IIS

A Temporary down page (e.g. updating your servers SW) should ideally have a response code of 503, but you could get away with 307, but in no case should it be 200 (as google will index this and it will affect your SEO)
In IIS rewrite rules, you have 3 options for implementing a redirect to a down page, rewrite, redirect and customResponse:
<action type="Rewrite" url="/site1.html" />
<action type="Redirect" url="/site1.html" redirectType="Temporary" />
<action type="CustomResponse" statusCode="503" subStatusCode="0" statusReason="Site Unavailable" statusDescription="Down for maintenance" />
The problem is if you want a 503 response, you cant redirect to the required page.
We have 3 websites for different brands using episerver CMS.
When we do maintenance, or just want to take a site down, we have a single azure web app (aka iis) which has 3 holding pages, one for each.
so our site down website has 3 pages:
/site1.html
/site2.html
/site3.html
We use Azure traffic manager to point to the live site or the site down page, and we currently have redirects which work, but incorrectly give 200 response:
<rewrite>
<rules>
<rule name="site1" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="mysite" />
</conditions>
<action type="Rewrite" url="/site1.html" />
</rule>
<rule name="site2" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{HTTP_HOST}" pattern="myothersite" />
</conditions>
<action type="Rewrite" url="/site2.html" />
</rule>
etc.
</rules>
</rewrite>
In order to fix this issue (offer a different site down page per site, and respond with 503), what are the options?
I would guess its possible to setup something like 3 different named virtual hosts, each with nothing except a custom 503 error page + a catch all CustomResponse action? Any examples of such a config?
To be clear, our app may well be running normally, but we may want to use our traffic manager to point the public at a "down" page which has a 503 respose during maintenance. The website sorving the down page has nothing to do with the website serving the site/applicaion itself.
Sadly, as the Microsoft document describes – there is no way to customize the 503 HTTP error.
Even use rewrite to make it display custom 503 page, In fact the request get into IIS and rewrite successful, then response to client. The whole process is perfect and your web service doesn’t stop.
The error is detected by the IIS server as it attempts to hand the incoming request to application. Everything application does is performed in its app pool. Modules like rewrite and custom error page are all executed in this way. 503 handled by the http.sys you cannot create a custom error page at all, as it is processed before it gets to iis. Therefore, both hope that the web server will stop reporting 503, but also hope that the server can process the request to display the page you defined. These two conflicts.
If your application is asp.net, there’s another way to custom 503. You can place a text file named "app_offline.htm" in the root of the site, all requests to that website will redirect to that app_offline.htm file. Basically, if you need to take an entire ASP.NET site offline, you can place some nice message in that file. Then, any new requests to a URL, any URL, in that website will redirect to that file allowing you to do maintenance to the site, upgrades, or whatever. It is not really a redirect though. ASP.NET essentially shuts down the site, unloads it from the server, and stops processing any requests to that site. That is, until you delete the app_offline.htm file - then things will continue as normal and your ASP.NET site will load up and start serving requests again.

Using emsserver/emsserver.dll in URL of API Call in Production Environment

I have created an application that utilizes RAD Studio's EMS Server functionality. The development has been completed and tested in a production environment. The EMS Server documentation shows that in order to make an API call the emsserver.dll needs to be included in the URL.
https://{hostname}/emsserver/emserver.dll/API/Login?token={TokenValue}
Most APIs I have encountered do not have the dll embedded into the URL.
https://{hostname}/API/Login?token={TokenValue}
This is not a big deal as the API call works fine as is. I was just wondering if there is property or setting I can use in RAD Server or IIS in order to default the emsserver/emsserver.dll portion of the URL.
Do you mean you want to redirect or rewrite the url from https://{hostname}/API/Login?token={TokenValue} to https://{hostname}/emsserver/emserver.dll/API/Login?token={TokenValue} in IIS?
If this is your reuqimrent, I suggest you could try to use url rewrite extension to achieve your reuqirement.
You could install it from this url and add below url rewrite rule into your web.config file.
<system.webServer>
<rewrite>
<rules>
<rule name="Redirect to dll">
<match url="API/Login" />
<action type="Rewrite" url="https://{hostname}/emsserver/emserver.dll/API/Login" />
</rule>
</rules>
</rewrite>
</system.webServer>

Redirect to FQDN in IIS Not Working

None of the answers I've found to questions like this (notably this one) work for me, so here I go.
We are running IIS 8 on Windows Server 2012 R2. We have a wildcard certificate (*.mydomain.com) installed on the server. On the Default Web Site we have a single binding to accept incoming https requests on port 443. I have a second Web Site set with a single binding to accept incoming http requests on port 80.
On the latter Web Site I've created a URL Rewrite rule to redirect all incoming non-HTTP traffic to https://myserver.mydomain.com{REQUEST_URI}, and this works perfectly. If I browse to either http://myserver.mydomain.com/homepage or http://myserver/homepage, I am sucessfully redirected to the HTTPS version of the site with the full domain name included and thus it loads just fine.
If I browse to https://myserver.mydomain.com/homepage, the site also loads perfectly.
However, I am trying to create another URL Rewrite rule on the Default Web Site so that requests to https://myserver/homepage (Note: HTTPS but the full domain name is omitted) are redirected to the https://myserver.mydomain.com/homepage. The reason for this is that the SSL certificate is only matched if the full domain is included. As it stands, if I enter https://myserver/homepage I get a security warning (NET::ERR_CERT_COMMON_NAME_INVALID).
I've created the rule which I think should work, based on the answer I linked to above, but it doesn't work and I don't know why. My rule look like this:
<rewrite>
<rules>
<rule name="Redirect to FQDN" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^myserver$" />
<add input="{HTTPS}" pattern="^ON$" />
</conditions>
<action type="Rewrite" url="https://myserver.mydomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
Even with this rule in place and enabled, when I browse to https://myserver/homepage I get the security message. What am I doing wrong with this rule?
Problem is that you have SSL only for *.mydomain.com. And when you accessing https://myserver it will return certificate error because your cert is not valid for this domain.
For better understanding about steps during SSL connection:
In your case, the problem is between step 2 and 3

How can I indicate to users that my IIS website is undergoing maintenance?

For my IIS website, I'd like to redirect ALL requests to ONE page. The purpose of this is that I want to do some maintenance on the database (take it off-line) that all my web applications use. I have about 50 web apps running under this website, so I'd like to avoid visiting each of them to change something. I'm thinking I could make a single change in machine.config? Any hints would be appreciated.
If you are using ASP.NET 2.0 (or higher), you can drop an app_offline.htm page on the root.
More info here.
in webconfig
<rewrite>
<rules>
<rule name="redirect all requests" stopProcessing="true">
<match url="^(.*)$" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" pattern="" ignoreCase="false" />
</conditions>
<action type="Rewrite" url="index.php" appendQueryString="true" />
</rule>
</rules>
</rewrite>
Make all the pages un-available, probably stop the current web site and create an entire new completly blank site in its place. Then put up a custom error page for the 404 (file ot found) error. Custom Errors is a tab on the properties dialog of the web site in IIS. Just create the page you want to send, then change the entry for 404 on the custom errors tab to point to the new file you just created.
In IIS 10 there is an optional component "HTTP Redirect" (it may be available in earlier IIS versions; I don't know).
It allows you to set up very simple catch-all redirects, using any of the common HTTP redirect response codes.
This can be installed via Server Manager, in Windows Server 2019.
Could you create a new site in IIS with a binding to port 80 with a blank host-header (much like the Default site) and then stop the other site(s)? That way all requests would be handled by the new site, which could simply be a static HTML page notifying users that the site is down for maintenance.

Resources