IIS 7.5 selectively overwrites the error response page

asp.net-mvciis-7.5ntlmwindows-authenticationwindows-server-2008-r2

Using Windows 2008r2, IIS 7.5, I have an application using Windows Integrated Authentication (WWW-Authenticate: NTLM, Negotiate).

In my ASP.NET MVC code, I am overwriting the IIS error page output…

    protected void Application_EndRequest(Object sender, EventArgs e)
    {
        HttpContext context = ((HttpApplication)sender).Context;
        if (context.Response.Status.Substring(0, 3).Equals("401"))
        {
            HttpApplication app = (HttpApplication)sender;

            string returnUrl = Response.ApplyAppPathModifier("~/OpenId/AskUser").ToString();
            string url = new Uri(this.Request.Url, Response.ApplyAppPathModifier("~/Account/Login?returnUrl=" + returnUrl)).ToString();
            app.Response.ClearContent();
            app.Response.Write(string.Format("<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta http-equiv=\"refresh\" content=\"0;URL='{0}'\"><title>Error Authenticating</title><script language=\"javascript\">self.location='{0}';</script></head><body>Error Authenticating</body></html>", url));
        }
    }

The goal is to redirect the user if they are on a non-Windows computer or mobile device (or using Firefox on a Windows PC) to a forms-based login that they can use instead of expecting their Windows credentials via Windows Integrated Auth.

The problem I am encountering with IIS 7.5 on the server is that even though this EndRequest event is firing (I can tell from a X-DEBUG header I inserted in the code and watching fiddler), the HTML output sent back to the client is the IIS 7.5 default error page for http status code 401, not the HTML page I'm outputting. The first time the requeest gets sent (the first request with the 401 response) it works fine, but if the user hits OK with an empty or wrong username/password combination, subsequent 401 responses do not include the proper output in the body of the response (either the default IIS 7.5 401 response page or a simple sentence of text and no HTML).

Note that this does not happen on IIS 7.5 Express on my developer box – it properly outputs the HTML page from the code segment above. This only happens when I deploy the project to the IIS 7.5 web server.

I also tried creating a custom error page… in my web.config I added the following under system.webServer…

<httpErrors>
  <remove statusCode="401"/>
  <error statusCode="401" prefixLanguageFilePath="" path="ErrorPages/401.html" responseMode="File" />
</httpErrors>

And created and deployed the corresponding HTML file. Once I did that, I no longer got the default IIS 7.5 error page, but now only receive the following terse plain text (not HTML) reply, "You do not have permission to view this directory or page.".

I'm lost as to why IIS Express is outputting everything properly, while IIS 7.5 isn't. Even when I tell IIS 7.5 to output a different page for that type of error, it still wont do it.

Best Answer

This appears to be a duplicate of https://stackoverflow.com/questions/434272/iis7-overrides-customerrors-when-setting-response-statuscode , but I'm too new on Server Fault to flag this.

My take: I had a similar problem when using Mediawiki on an IIS server box.

When you browse to a non-existing page in Mediawiki. It sends back a special wiki page that says that the page doesn't exist and allows you to create it. However this page is sent with a status of 404.

On a standard IIS server IIS will automatically overwrite this with a custom error page to users NOT on the local host, but sends detailed error messages to those that are. In practice "detailed error messages" means letting through the original error page, whether it be custom in your application or generated by ASP.NET because of a coding error.

My method was to enable detailed error messages for both local and remote users in the Error Pages > Feature settings. However this will also allow remote users to see detailed ASP error messages should they occur, which may help a malicious user compromise your security.

Other options are given as answers to the linked question, including better ones that may not have a security implication. Another option in this case is to not return a 401 status code, but just a normal 200 code with your custom page. This sacrifices REST principles for ease of development.

Note that the fact that detailed error messages are shown for the local user, but not remote users, may explain why your solutions works during local testing but not when deployed.