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 may be 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.
Example conflict
A simple Content Security Policy restriction can be set with default-src. Description of the directive: The default-src directive defines the default policy for fetching resources such as JavaScript, Images, CSS, Fonts, AJAX requests, Frames, HTML5 Media.
And basic web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="default-src 'self';" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Which would have the intended effect of preventing images from being loaded from external sources with the message
Refused to load the image 'https://externalDomain/image.png' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback.
However, some Javascript based functions may stop loading on the site with the message
Refused to execute inline event handler because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
Description of unsafe-line being: Allows use of inline source elements such as style attribute, onclick, or script tag bodies (depends on the context of the source it is applied to) and javascript: URIs
Requiring an update to the Content-Security-Policy above to now read
<add name="Content-Security-Policy" value="default-src 'self'; script-src 'unsafe-inline' *.domain.com domain.com;" />
Adding the unsafe-inline keyword and defining from which domains the Javascript can be loaded.