Response header updates for PCI Compliance

Server and Scripting Response Headers

To meet PCI compliance requirements, four response headers often have to be removed: server, X-Powered-By, and in the case of a .NET (MVC) app, X-AspNet-Version and X-AspNetMvc-Version.  The response headers can be viewed in a browser using dev tools like Chrome Developer Tools, the referenced headers being the last four in the screenshot below.


Removing the headers is fairly straight forward.  With all but X-AspNetMvc-Version being removed via web.config update.  In the case of X-AspNetMvc-Version, it is disabled in a project's global.asax(.cs) file.


Server

If a site is currently on IIS 8, it's recommended to have the site migrated to IIS 10 / Windows Server 2019 by Support.  Where on IIS 10 the server header can be removed via the attribute removeServerHeader

<system.webServer>
  <security>
    <requestFiltering removeServerHeader="true" />
  </security>
</system.webServer>

Otherwise on IIS 8 the header response would be removed via the following rewrite rule for RESPONSE_Server.  Unlike the attribute above, this method still returns a "Server" header, but the response is blank

<system.webServer>
  <rewrite>
    <outboundRules>
      <rule name="Remove RESPONSE_Server" >
        <match serverVariable="RESPONSE_Server" pattern=".+" />
        <action type="Rewrite" value="" />
      </rule>
    </outboundRules>
  </rewrite>
</system.webServer>

X-Powered-By 

The X-Powered-By header is removed via the customHeaders element

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

X-AspNet-Version and X-AspNetMvc-Version

ASP.NET version is disabled using the enableVersionHeader attribute

<system.web>
  <httpRuntime enableVersionHeader="false" />
</system.web>

To remove X-AspNetMvc-Version, "MvcHandler.DisableMvcResponseHeader = true" is added to the Application_Start event of the global.asax or global.asax.cs as in the examples below

protected void Application_Start()
{
    MvcHandler.DisableMvcResponseHeader = true;
}
or

    Sub Application_Start()
        MvcHandler.DisableMvcResponseHeader = True
    End Sub

Example web.config and response

An example web.config removing the server, X-Powered-By and X-AspNet-Version would look like

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <httpRuntime enableVersionHeader="false" />
  </system.web>
  <system.webServer>
    <security>
      <requestFiltering removeServerHeader="true" />
    </security>
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>
Then, along with the project update for MVC, the resulting response header would be



Additional Response Headers

Additional response headers maybe required, for example Content-Security-Policy (https://content-security-policy.com/), to set restrictions on how elements are loaded on the site.  Because various headers may be required, a basic syntax example is

<httpProtocol>
  <customHeaders>
    <add name="headerName" value="setting or directive" />
  </customHeaders>
</httpProtocol>
As an example, and not explicitly a guide, nopCommerce has many of these customHeaders set by default, as seen below

<httpProtocol>
  <customHeaders>
    <add name="X-XSS-Protection" value="1; mode=block" />
    <add name="X-Frame-Options" value="SAMEORIGIN" />
    <add name="X-Content-Type-Options" value="nosniff" />
    <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
    <add name="Content-Security-Policy" value="default-src 'self'; connect-src *; font-src * data:; frame-src *; img-src * data:; media-src *; object-src *; script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline';" />
    <add name="Referrer-Policy" value="same-origin" />
    <add name="Permissions-Policy" value="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=*, usb=()" />
  </customHeaders>
</httpProtocol>
Note that setting these customHeaders can have unintended consequences on what elements are being loaded on the site.  And extra care and review may be required versus the headers detailed in the first section of the article.