Securing ASP.NET Web Applications in IIS

Earlier this year I wrote an MVC web application to be used by call handling teams to process card payments. Although the application was for internal use only and not public facing it was nevertheless penetration tested by a professional security company. I'm pleased to say that my site passed with top marks!

All to often website security is neglected by developers who may have a negative view of demanding InfoSec teams, or who believe that web security is an IT Ops problem and that they should configure their servers better. I disagree - security is everybody's concern. And the more that devs can contribute and implement the better.

First things first. I am not going to cover SQL Injection or Cross Site Scripting (XSS) - there are a ton of resources on the net that cover the benefits or parameterised SQL and distrusting user input. I expect all devs to know about these vulnerabilities and how to counter them. If you are concatenating SQL statements in your application we need to talk...

This post will cover securing Windows, IIS and ASP.NET applications - with the emphasis on the latter, particularly the web.config file as this is a developer artefact that can be committed to source control and deployed across different environments in the dev lifecycle.

Start with Windows

Patching

Make sure that your server is up-to-date with patches. Tools like nessus can scan your machine and provide a report of outstanding vulnerabilities:

Example nessus report

It's then up to you to remediate them.

Handy hint: make sure you reboot the server for your fixes to take effect!

Now for IIS:

Request Filtering

Request filtering should be configured at the server level.

Disable HTTP trace requests – under HTTP Verbs deny TRACE

Edit Feature Settings and ensure that:
  • Allow high-bit characters is unticked (to reject non-ASCII characters in URLs)
  • Allow double escaping is unticked
  • Maximum URL length is 4096
  • Maximum query string is 2048
Like so:


Remove default install page

Out of the box IIS's default page tells all and sundry that you are running IIS. Delete iisstart.html and images such as iis-85.png from C:\inetpub\wwwroot

Install URL Rewrite module

Download and install the Microsoft URL Rewrite module for IIS

This module should be installed, but does not need to be configured at the server level. It will allow apps to enforce HTTPS-only from their web.configs

Now for the main event: Application Configuration

Application specific hardening can be applied in web.config files – transformed appropriately for your test, staging and production environments.

Although many of the following settings can be configured within IIS, there are benefits for doing so in web.config:
  • It's convenient for developers to do this within Visual Studio
  • It allows for application-specific settings
  • The changes can be checked into source control and deployed via a DevOps toolchain

Remove debug compilation

This is allowable for local debugging:

  <system.web>
    <compilation debug="true" targetFramework="4.7.2"/>
  </system.web>

But the debug attribute should be removed via a transform for all other environments:

  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
  </system.web>

Use Custom Error messages

Avoid disclosing error information in all but local environments.

  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="~/Error.html" />
  </system.web>

Windows Authentication

For intranet applications use Windows authentication to identify users. Disable anonymous authentication.

  <system.web>
    <authentication mode="Windows" />
  </system.web>
 
  <system.webServer>
    <security>
      <authentication>
        <windowsAuthentication enabled="true" />
        <anonymousAuthentication enabled="false" />
      </authentication>
    </security>
  </system.webServer>

Authorization

Authorize access appropriately. For the whole application:

  <system.web>
    <authorization>
      <allow roles="Domain\Security Group" />
      <deny users="*" />
    </authorization>
  </system.web>

Or for individual controller methods:

[Authorize(Users = @"Domain\User Account")]

Set appropriate HTTP Headers

Remove headers that give away server and platform information:

  <system.web>
    <httpRuntime targetFramework="4.7.2" enableVersionHeader="false" />
  </system.web>
 
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <clear/>
        <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
        <add name="X-Frame-Options" value="deny" />
        <add name="X-Xss-Protection" value="1; mode=block" />
        <add name="X-Content-Type-Options" value="nosniff" />
        <add name="Referrer-Policy" value="no-referrer" />
        <add name="X-Permitted-Cross-Domain-Policies" value="none" />
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>

Also inside the Global.asax.cs:

protected void Application_Start()
{
    …
 
    MvcHandler.DisableMvcResponseHeader = true;
}
 
protected void Application_PreSendRequestHeaders()
{
    if (HttpContext.Current != null)
    {
        HttpContext.Current.Response.Headers.Remove("Server");
    }
}

Disable directory browsing

  <system.webServer>
    <directoryBrowse enabled="false" />
  </system.webServer>

Configure maxAllowedContentLength

  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits>
          <headerLimits>
            <add header="Content-type" sizeLimit="100" />
          </headerLimits>
        </requestLimits>
      </requestFiltering>
    </security>
  </system.webServer>

Redirect requests for HTTP to HTTPS

We installed the URL Rewrite module in IIS earlier - here is where we configure it, per application, to always redirect to HTTPS. This should be in appropriate environment transforms:

  <system.webServer>
    <rewrite xdt:Transform="Insert">
      <rules>
        <clear />
        <rule name="Redirect all requests to https" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action
            type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}"
            redirectType="Permanent" appendQueryString="false" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

Cross Site Request Forgery

CSRF can be mitigated in MVC by applying the AntiForgeryToken to views:

@Html.AntiForgeryToken()

You must verify the token in the controller on Post methods:

[HttpPost]
[ValidateAntiForgeryToken]

Wrap up

With all these steps followed your web application should now be secure and, like mine, should score very highly in a penetration test.

Further reading:
Windows Server 101: Hardening IIS via Security Control Configuration
OWASP IIS Hardening guidelines

Comments