NWebsec - Security libraries for ASP.NET¶
Getting started with NWebsec¶
NWebsec lets you configure quite a few security headers, some are useful for most applications while others are a bit more specialized. Here’s some guidance to get you started.
First, you need to add NWebsec to your application. The easiest way to do this would be to get it through NuGet. Search for NWebsec in the package manager GUI, or install it through the console with one of the following commands:
For an MVC3/4/5 app:
Install-Package NWebsec.Mvc
For non-MVC apps use the following:
Install-Package NWebsec
Alternatively, you can get the latest version of the assemblies under releases if you want to add them by hand. In that case, refer to Configuration to also edit the configuration by hand.
Now it’s time to start securing your application! It’s good practice to remove the version headers added by ASP.NET and IIS, so you’d want to suppress version headers for your web application. The NuGet installation procedure will make some modifications to the web.config to disable version headers.
To avoid various attacks carried out through iframes, the X-Frame-Options header should be enabled. MIME sniffing is a source to many problems, including security issues, so you’d want to run with the X-Content-Type-Options header.
For applications that run over SSL/TLS, you should most definitely employ the Strict-Transport-Security header — instructing the browser to interact with anything on your domain over a secured connection only.
Unless your application needs to redirect users to arbitrary sites on the internet, you’d want redirect validation enabled. There might be a few sites you’d want to whitelist for redirects, in particular if you use WIF or Google/Facebook/any other external authentication provider. Consult Redirect validation if you run into trouble.
So, for an application running over http the following is a reasonable starting point for your web.config:
<configuration>
...
<nwebsec>
<httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd">
<redirectValidation enabled="true" />
<securityHttpHeaders>
<x-Frame-Options policy="Deny"/>
<x-Content-Type-Options enabled="true" />
</securityHttpHeaders>
</httpHeaderSecurityModule>
</nwebsec>
...
</configuration>
If your site is served over https, you’d also want to include the Strict-Transport-Security header, as such (note that the browser will load all content over https for the entire domain when the header is used):
<configuration>
...
<nwebsec>
<httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd">
<redirectValidation enabled="true" />
<securityHttpHeaders>
<x-Frame-Options policy="Deny"/>
<strict-Transport-Security max-age="365" />
<x-Content-Type-Options enabled="true" />
</securityHttpHeaders>
</httpHeaderSecurityModule>
</nwebsec>
...
</configuration>
Note! If users can log into you application, you should always run it over https to keep your users safe!
NWebsec lets you add other security headers as well, but these are more tightly coupled to the individual resources in your application. In particular, the Content-Security-Policy (CSP) header can significantly improve the security of a web application but also requires great care when you’re building a new application from the ground up — even more so if you retrofit it onto an existing application. SendSafely has published two blog posts discussing how they dealt with the challenge, links included for the interested reader:
- Using Content Security Policy to Prevent Cross-Site Scripting (XSS)
- Retrofitting Code for Content Security Policy
See Configuring Content-Security-Policy to learn how to enable CSP, this is where the real job starts. Good luck! :)
Note also that security headers can be enabled through MVC attributes, refer to NWebsec.Mvc for details.
NWebsec libraries¶
NWebsec¶
NWebsec provides an HTTP Module that controls which headers are sent in the response from an ASP.NET application. The HTTP Module works for both Web Forms application and MVC applications as it hooks into the ASP.NET/IIS pipeline. There, it makes changes to the HTTP header collection according to the NWebsec configuration.
The Http Module lets you set common security headers without any code changes, it is loaded and configured strictly through web.config. This is a quick win for the security of ASP.NET applications. Security of legacy applications can be improved quite easily as well. The flexibility of the configuration means that various parts of an application can run with different configurations.
The NWebsec library is a “configuration only” install, it requires no code changes to your application. You’ll find it on NuGet, or you can download the assemblies from the GitHub releases. See Configuration to learn how to configure it.
Security headers¶
Supported security headers are:
- Content-Security-Policy / Content-Security-Policy-Report-Only
- Strict-Transport-Security
- Public-Key-Pins / Public-Key-Pins-Report-Only
- X-Frame-Options
- X-XSS-Protection
- X-Content-Type-Options
- X-Download-Options
NWebsec.Owin¶
NWebsec.Owin provides OWIN middleware that lets you output HTTP security headers. It currently supports:
- Strict-Transport-Security
- X-Content-Type-Options
- X-Download-Options
- X-Frame-Options
- X-Xss-Protection
- Content-Security-Policy
- X-Robots-Tag
In addition, it provides middleware for redirect validation.
You’ll notice that not all features from the NWebsec library are available yet, e.g. cache-headers, these are under consideration.
Dependencies¶
NWebsec.Owin depends on the OWIN NuGet package and the OWIN IAppBuilder startup interface defined there. It does not have any Katana (MSFTs OWIN libraries) dependencies. The idea is to be able to support other OWIN host implementations as they become available.
Note that the middleware has been developed and tested under Katana.
Documentation¶
The middleware is documented alongside the web.config and MVC attributes. Refer to the Configuration for samples.
NWebsec.Mvc¶
NWebsec.Mvc lets you configure NWebsec through MVC ActionFilter attributes. MVC 3 or newer is supported. By decorating controllers and actions with attributes, the NWebsec configuration is overridden. This is how it works:
NWebsec.Mvc depends on the NWebsec package, which includes the HttpHeaderModule. This module relies on its Configuration, if there is any. Now, the attributes in NWebsec.Mvc lets you override the HttpHeaderModule’s configuration, here’s the order in which the final configuration is calculated:
- Web.config
- MVC global filter
- MVC controller
- MVC action
This gives a fair amount of flexibility. Do your stuff in config or in MVC, or both. You decide!
The attributes¶
Most elements from the Configuration have an MVC attribute counterpart.
Now back to the attributes — they are probably best explained in code. Here are the attributes registered as global filters in Global.asax in a typical MVC app:
using System.Web.Mvc;
using NWebsec.Csp;
using NWebsec.Mvc.HttpHeaders;
using NWebsec.Mvc.HttpHeaders.Csp;
....
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new SetNoCacheHttpHeadersAttribute());
filters.Add(new XRobotsTagAttribute() { NoIndex = true, NoFollow = true });
filters.Add(new XContentTypeOptionsAttribute());
filters.Add(new XDownloadOptionsAttribute());
filters.Add(new XFrameOptionsAttribute());
filters.Add(new XXssProtectionAttribute());
//CSP
filters.Add(new CspAttribute());
filters.Add(new CspDefaultSrcAttribute { Self = Source.Enable });
filters.Add(new CspScriptSrcAttribute { Self = Source.Enable });
//CSPReportOnly
filters.Add(new CspReportOnlyAttribute());
filters.Add(new CspScriptSrcReportOnlyAttribute { None = Source.Enable });
}
Here’s what the attributes will look like when applied to a controller and an action method:
using System.Web.Mvc;
using NWebsec.HttpHeaders;
using NWebsec.Mvc.HttpHeaders;
...
[SetNoCacheHttpHeaders]
[XContentTypeOptions]
[XDownloadOptions]
[XFrameOptions(Policy = XFrameOptionsPolicy.SameOrigin)]
[XXssProtection]
public class HomeController : Controller
{
[XFrameOptions(Policy = XFrameOptionsPolicy.Deny)]
public ActionResult Index()
{
return View();
}
[SetNoCacheHttpHeaders(Enabled = false)]
[XContentTypeOptions(Enabled = false)]
[XDownloadOptions(Enabled = false)]
[XFrameOptions(Policy = XFrameOptionsPolicy.Disabled)]
[XXssProtection(Policy = XXssProtectionPolicy.Disabled)]
[XRobotsTag(Enabled = false)]
public ActionResult HeadersDisabled()
{
return View();
}
}
Note how the Index action method is decorated with only one attribute. It has its own XFrameOptions setting, and will inherit all other attributes from the controller.
The HeadersDisabled action shows how headers can be disabled per action or controller. This lets you define a strict global security policy for your application and relax the policy where needed.
NWebsec is made up of several libraries, each offering different approaches to improve the security of your web applications.
NWebsec¶
NWebsec lets you add security headers and other features to your applications through an HTTP module that is loaded and configured through web.config. This makes it fairly easy to improve the security of legacy apps, as it requires no changes to code.
NWebsec.Owin¶
NWebsec.Owin offers OWIN middleware for many of NWebsec’s features.
NWebsec.Mvc¶
NWebsec.Mvc configures NWebsec through MVC attributes. This is useful when you need to do adjustments to the application wide config from NWebsec/NWebsec.Owin, for particular controllers or actions. Some developers prefer to minimize their web.config, and configure most things through code. They can use the NWebsec.Mvc attributes as an alternative to web.config for most features.
Configuration¶
Configuring cache headers¶
Cache headers play an important role for your users’ privacy if you’re running a secure website. Browsers tend to cache web pages that the users loads, for easy retrieval and later display. Browser caching is an important mechanism to “speed up” up the Internet, to avoid having to fetch content that has not changed.
In short the browser can behave in two ways when it comes to caching. It could ask the server whether a resource has changed, if not it can simply redisplay the page it has stored in cache. The browser can send the server an If-Modified-Since header to achieve this.
Also, the browser can serve previously loaded pages directly from cache — without checking with the server whether the page has changed. This is a common behaviour when the user is navigating back and forth with the “Back” and “Forward” buttons in the browser.
You can read an excellent write-up on the issues related to browser cache and history on Opera’s Yngve Pettersen’s blog: Introducing Cache Contexts, or: Why the browser does not know you are logged out.
To instruct the browser to reload pages when the user is navigating with the back and forward buttons you can configure NWebsec to set the following headers:
Cache-Control: no-cache, no-store, must-revalidate Expires: -1 Pragma: no-cache
NWebsec will not add these headers for content that typically should be cached:
- The WebResource.axd and ScriptResource.axd handlers that are commonly used in Web Forms applications.
- Static content when the application is running in Integrated Pipeline mode.
- The “bundles” that were introduced in ASP.NET MVC 4.
Warning
Setting these headers will make the browser reload every page in the browsing history when the user navigates with the “Back” and “Forward” buttons. This will affect the load on your server(s) — and also the user experience. Do not enable these headers unless you really have to.
There are two ways to enable the cache control headers:
In web.config:
<nwebsec>
<httpHeaderSecurityModule >
<setNoCacheHttpHeaders enabled="true" />
</httpHeaderSecurityModule >
</nwebsec>
Note
Enabling the no cache headers in config is a point of no return, as of NWebsec 3.0.0. This is by design after the PreSendRequestHeaders event was deprecated by MSFT. If you want to enable these headers globally for your app but make exceptions for some of your controllers/actions, use a global MVC filter instead, as per the following example.
Or as an MVC filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new SetNoCacheHttpHeadersAttribute());
}
You can also set the attribute on controllers and actions, see NWebsec.Mvc.
Redirect validation¶
If you’re familiar with the OWASP top ten list you’ll know that Unvalidated Redirects and Forwards has been lingering as number ten on that list the last couple of years. These types of vulnerabilities are used in real attacks but fortunately there are several ways to deal with them. You’ll find a nice write-up on the issues and potential countermeasures here: OWASP Top 10 for .NET developers part 10: Unvalidated Redirects and Forwards.
The proper remedy for unvalidated redirects lie in the code. Whenever an application redirects the user based on input parameters, that input should be validated. As we all know, developers slip once in a while. Legacy applications can also pose a challenge, with code written during a time where security might not have been a priority. Having a “safety net” to help detect and fix such vulnerabilities makes it easier to keep the code base secure.
Redirect validation is slightly opportunistic as of NWebsec 3.0.0, as it’s executed in the HttpApplication.EndRequest Event. In most cases, the redirect validation exception will be thrown before the request headers are sent to the client. However, doing e.g. a Response.Flush()
in a handler will send the headers to the client immediately - before the EndRequest event fires. An exception will be thrown in any case if a non-whitelisted redirect is detected.
Configuring redirect validation¶
NWebsec lets you enable redirect validation in its simplest form with a single line of config:
<nwebsec>
<httpHeaderSecurityModule>
<redirectValidation enabled="true">
</httpHeaderSecurityModule>
</nwebsec>
NWebsec.Owin lets you do the same with a single line in your OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseRedirectValidation();
}
This configuration will validate all HTTP responses with a status code of 3xx, except 304 (Not Modified). Redirects to relative URIs are allowed, as well as redirects to the same site — meaning the same scheme/host/port. Redirects to other destinations will trigger a RedirectValidationException, terminating the response.
Web applications often redirect users from HTTP to HTTPS, this must be explicitly allowed. If the site is running on a non-default HTTPS port, one or more port numbers must be configured. Also, if your application needs to redirect to other sites they can be added to a whitelist in config, as such:
<nwebsec>
<httpHeaderSecurityModule>
<redirectValidation enabled="true">
<allowSameHostRedirectsToHttps enabled="true" />
<!--<allowSameHostRedirectsToHttps enabled="true" httpsPorts="4567, 7654"/>-->
<add allowedDestination="http://www.nwebsec.com/"/>
<add allowedDestination="https://www.google.com/accounts/"/>
</redirectValidation>
</httpHeaderSecurityModule>
</nwebsec>
For NWebsec.Owin your startup class would look like:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseRedirectValidation(options =>
options.AllowedDestinations("http://www.nwebsec.com/", "https://www.google.com/accounts/")
.AllowSameHostRedirectsToHttps());
}
In addition to same site redirects, this would allow redirects to anywhere on http://www.nwebsec.com/ as well as https://www.google.com/accounts/ and subpaths. Redirects to other destinations will trigger an exception.
As an example based on the above configuration, a redirect to https://www.google.com/accounts/foo/bar would be allowed but a redirect to https://www.google.com/foo/ would raise an exception.
Final notes¶
If you’re using WIF or depend on identity providers such as Google or Facebook to sign in users, you’ll need to add the STS/authentication endpoints to the allowed destinations whitelist.
Make sure to register the redirect validation middleware early in the pipeline. The middleware will not be called if a preceding middleware redirects and terminates the pipeline.
Configuring Content-Security-Policy¶
Consult Breaking changes if you’re upgrading to the NWebsec 4.x packages.
Content-Security-Policy (CSP) provides a safety net for injection attacks by specifying a whitelist from where various content in a webpage can be loaded from.
If you’re unfamiliar with CSP you should read An Introduction to Content Security Policy by Mike West, one of the Chrome developers. You’ll also find information about CSP on the Mozilla Developer Network.
At the time of writing, CSP 1.0 is supported by most major browsers (Opera/Chrome/Firefox/Safari). Internet Explorer CSP support is lacking for now, but they’re working on it.
Chrome and Firefox have partial support for CSP Level 2.
A page’s content security policy is set through the following headers:
- Content-Security-Policy
- Content-Security-Policy-Report-Only
CSP support in NWebsec¶
NWebsec emits the Content-Security-Policy header, but no longer supports the deprecated X-Content-Security-Policy and/or X-WebKit-CSP headers.
You should read this entire article to understand how CSP configuration is inherited and/or overridden in NWebsec. We’ll start with a general introduction, and then move on to the configuration section and the MVC attributes.
CSP configuration¶
NWebsec emits the CSP header if CSP is enabled and one or more directives are configured — except for redirects and static content. The directives specified in CSP 1.0 are:
- default-src — Specifies the default for other sources
- script-src
- style-src
- object-src
- img-src
- media-src
- frame-src
- font-src
- connect-src
- sandbox (optional to implement)
- report-uri — Specifies where CSP violations can be reported
CSP level 2 adds quite a few new directives over these, currently supported by NWebsec are:
- frame-ancestors
- base-uri
- child-src
- form-action
- sandbox (no longer optional)
CSP 2 also introduces script and style hashes and nonces. You’ll find a good write-up on this on the Mozilla blog. NWebsec supports script and style nonces as of version 3.2.0.
To use a directive, it must be configured with at least one source. The standard specifies some special sources.
- ‘none’ — No content of this type is allowed
- Supported by all directives
- ‘self’ — Content of this type can only be loaded from the same origin (no content from other sites)
- Supported by all directives
- ‘unsafe-inline’ — Allows unsafe inline content.
- Supported by style-src (inline css) and script-src (inline script)
- ‘unsafe-eval’ — Allows script functions considered unsafe (such as eval())
- Supported by script-src
You can also specify your own sources, in various formats specified by the [url:standard|http://www.w3.org/TR/CSP/#source-list]. Here are a few examples.
- * — Allow content from anywhere
- https: — Scheme only, load only content served over https
- *.nwebsec.com — Wildcard host, allow content from any nwebsec.com sub-domain.
- www.nwebsec.com:81 — You can specify a port number
- https://www.nwebsec.com — You can of specify an absolute URI for a host (path has no effect though)
NWebsec validates the configured sources and will let you know if something is wrong.
CSP 2 specifies support for internationalized domain names in custom sources.
The built-in report handler¶
If you configure the report-uri directive, the browser will report CSP violations to that URI (as JSON). The CSP standard includes an example of a CSP violation report. There are two things that make it a bit cumbersome to deal with these reports.
- The report-uri must be a relative URI, so you might have to include a report handler in every one of your applications (unless they’re running on the same domain).
- The browser submits the violation report without session cookies. That means you need to poke a hole in your authorization rules to get the requests through.
NWebsec 2.0.0 introduced a built-in CSP report handler, so you don’t need to implement your own. It will pick up the report before the ASP.NET authorization event fires, so you don’t need to poke a whole in the authorization rules.
When a CSP report is received, NWebsec raises an event. You can handle these events by adding the following code to global.asax
protected void NWebsecHttpHeaderSecurityModule_CspViolationReported(object sender, CspViolationReportEventArgs e)
{
var report = e.ViolationReport;
}
You’d probably want to log the violation to keep track of what’s going on in your web application.
Report-Only mode¶
The CSP standard actually defines two headers: Content-Security-Policy and Content-Security-Policy-Report-Only. Browsers will enforce the CSP when they see the first header, i.e. they will not load content that violates the policy and report the violation. If you use the Report-Only header, CSP will not be enforced by the browser, so all content will be loaded but violations will still be reported.
NWebsec lets you configure these headers independently so you can use one or the other, or both.
Configuring CSP through web.config¶
You need to enable CSP, here’s the relevant configuration line from the NWebsec [[Configuration]]:
<content-Security-Policy enabled="true">
To use the report only header, you can use:
<content-Security-Policy-Report-Only enabled="true">
You configure directives like you do for <content-Security-Policy>
.
Directives in web.config¶
CSP defines a list of directives, where each directive has a list with one or more source definitions, the configuration example includes common directives:
<content-Security-Policy enabled="true">
<default-src self="true"/>
<script-src self="true">
<add source="nwebsec.codeplex.com" />
<add source="scripts.nwebsec.com" />
</script-src>
<style-src unsafeInline="false" self="true" />
<img-src self="true">
<add source="images.nwebsec.com"/>
</img-src>
<object-src none="true" />
<media-src none="true" />
<frame-src none="true" />
<font-src none="true" />
<connect-src none="true" />
<frame-ancestors none="true" />
<report-uri enableBuiltinHandler="true"/>
</content-Security-Policy>
This config would give you the header:
Content-Security-Policy: default-src ‘self’; script-src ‘self’ nwebsec.codeplex.com scripts.nwebsec.com; object-src ‘none’; style-src ‘self’; img-src ‘self’ images.nwebsec.com; media-src ‘none’; frame-src ‘none’; font-src ‘none’; connect-src ‘none’; frame-ancestors ‘none’; report-uri /WebResource.axd?cspReport=true
Sources in web.config¶
Each directive can have one or more sources. The special CSP sources are configured through attributes, while custom sources are added to a source collection. Here’s an example where the script-src directive is configured with the special ‘self’ source, and two custom sources:
<script-src self="true">
<add source="nwebsec.codeplex.com" />
<add source="scripts.nwebsec.com" />
</script-src>
Here’s an example where the special sources ‘unsafe-eval’ and ‘unsafe-inline’ are enabled for the script-src directive (use with caution, this will effectively disable the XSS protection):
<script-src unsafeEval="true" unsafeInline="true" />
Configuring CSP middleware¶
The NWebsec.Owin package includes CSP middleware. Here’s an example of how you register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseCsp(options => options
.DefaultSources(s => s.Self())
.ScriptSources(s => s.Self().CustomSources("scripts.nwebsec.com"))
.ReportUris(r => r.Uris("/report")));
app.UseCspReportOnly(options => options
.DefaultSources(s => s.Self())
.ImageSources(s => s.None()));
}
Script and style nonces through HtmlHelpers¶
The NWebsec.Mvc package includes HtmlHelpers to add CSP 2 script and style nonces to allow inline scripts/styles. The helpers will output the complete nonce-attribute. Here is an example of usage:
<script @Html.CspScriptNonce()>document.write("Hello world")</script>
<style @Html.CspStyleNonce()>
h1 {
font-size: 10em;
}
</style>
Configuring CSP through MVC attributes¶
The NWebsec.Mvc package provides MVC attributes to configure the security headers. The CSP policy defined by the MVC attributes are overridden per directive, this aligns with how this works in the web.config. That means that you define your baseline policy in web.config, CSP middleware or through global filters, and you can easily override a particular directive on a controller or action.
Here’s an example. You can e.g. enable CSP, and register a directive as global filters:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CspAttribute());
filters.Add(new CspDefaultSrcAttribute { Self = true });
}
And consider the following controller:
[CspScriptSrc(Self = true, CustomSources = "scripts.nwebsec.codeplex.com")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View("Index");
}
[CspDefaultSrc(CustomSources = "nwebsec.codeplex.com")]
public ActionResult Index2()
{
return View("Index");
}
[CspDefaultSrc(CustomSources = "stuff.nwebsec.codeplex.com")]
[CspScriptSrc(CustomSources = "scripts.nwebsec.codeplex.com ajax.googleapis.com")]
public ActionResult Index3()
{
return View("Index");
}
}
The index action will inherit the global attribute as well as the attribute set on the controller, which yields this header:
Content-Security-Policy: default-src 'self'; script-src 'self' scripts.nwebsec.codeplex.com
The index2 action inherits previous directives yielding:
Content-Security-Policy: default-src 'self' nwebsec.codeplex.com; script-src 'self' scripts.nwebsec.codeplex.com
The index3 action also inherits all directives, thus giving us this header:
Content-Security-Policy: default-src 'self' stuff.nwebsec.codeplex.com; script-src 'self' scripts.nwebsec.codeplex.com scripts.nwebsec.com ajax.googleapis.com
To have a directive completely removed, disable it as such:
[CspScriptSrc(Enabled = false)]
You can also disable CSP altogether:
[Csp(Enabled = false)]
Configuring Strict-Transport-Security¶
There are four configuration options:
- max-age is a
TimeSpan
(see TimeSpan.Parse) - includeSubdomains adds includeSubDomains in the header, defaults to false
- preload adds the preload directive, defaults to false. Max-age must be at least 18 weeks, and includeSubdomains must be enabled to use the preload directive. See the Chromium HSTS docs for details.
- httpsOnly ensures that the HSTS header is set over secure connections only, defaults to true.
Configuration | Resulting header |
---|---|
max-age=”00:00:00” | Strict-Transport-Security: max-age=0 |
max-age=”12:00:00” | Strict-Transport-Security: max-age=43200 |
max-age=”365” includeSubdomains=”true” | Strict-Transport-Security: max-age=31536000; includeSubDomains |
max-age=”365” includeSubdomains=”true” preload=”true” | Strict-Transport-Security: max-age=31536000; includeSubDomains; preload |
In web.config:
<strict-Transport-Security max-age="365" />
<strict-Transport-Security max-age="00:30:00" includeSubdomains="true" />
<strict-Transport-Security max-age="365" includeSubdomains="true" preload="true"/>
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseHsts(options => options.MaxAge(days:30).IncludeSubdomains());
//app.UseHsts(options => options.MaxAge(days:365).IncludeSubdomains().Preload());
}
Configuring Public-Key-Pins¶
There are four configuration options, as well as a list of certs to pin and/or a list of pin values. Note that you must supply two pins to generate a valid header, i.e. two certs, a cert and a pin value, or two pin values.
- max-age is a
TimeSpan
(see TimeSpan.Parse) - includeSubdomains adds includeSubDomains in the header, defaults to false
- httpsOnly ensures that the HSTS header is set over secure connections only, defaults to true.
- reportUri specifies an absolute URI to where the browser can report HPKP violations. The scheme must be HTTP or HTTPS.
- certificates specifies a list of certificates (by thumbprints) that should be pinned.
- pins specifies a list of pinning values for certificates that should be pinned
The following examples assume that we supply the pinning values:
- n3dNcH43TClpDuyYl55EwbTTAuj4T7IloK4GNaH1bnE=
- d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=
Configuration | Resulting header |
---|---|
max-age=”00:00:00” | Public-Key-Pins: max-age=0 |
max-age=”12:00:00” | Public-Key-Pins: max-age=43200;pin-sha256=”n3dNcH43TClpDuyYl55EwbTTAuj4T7IloK4GNaH1bnE=”;pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=” |
max-age=”365” includeSubdomains=”true” |
Public-Key-Pins: max-age=31536000;includeSubdomains;pin-sha256=”n3dNcH43TClpDuyYl55EwbTTAuj4T7IloK4GNaH1bnE=”;pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=” |
max-age=”365” includeSubdomains=”true” report-uri=”https://report.nwebsec.com/ |
Public-Key-Pins: max-age=31536000;includeSubdomains;pin-sha256=”n3dNcH43TClpDuyYl55EwbTTAuj4T7IloK4GNaH1bnE=”;pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=”;report-uri=”https://report.nwebsec.com/” | |
In web.config:
<public-Key-Pins max-age="30" includeSubdomains="true">
<certificates>
<add thumbprint="FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF"/>
</certificates>
<pins>
<add pin="Base64 pin"/>
</pins>
</public-Key-Pins>
<public-Key-Pins-Report-Only max-age="00:00:10" report-uri="https://report.nwebsec.com/">
<certificates>
<add thumbprint="00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" />
<add thumbprint="01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01" />
</certificates>
</public-Key-Pins-Report-Only>
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseHpkp(options => options
.MaxAge(seconds: 20)
.Sha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=")
.PinCertificate("FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF")
.ReportUri("https://nwebsec.com/report")
);
}
Configuring X-Frame-Options¶
This header can be configured in three ways:
Configuration | Resulting header |
---|---|
policy=”Disabled” | None |
policy=”Deny” | X-Frame-Options: Deny |
policy=”SameOrigin” | X-Frame-Options: SameOrigin |
NWebsec: In web.config
<x-Frame-Options policy="Disabled"/>
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseXfo(options => options.SameOrigin());
}
NWebsec.Mvc: As an MVC attribute, defaults to policy=”Deny”:
[XFrameOptions]
[XFrameOptions(Policy = XFrameOptionsPolicy.SameOrigin)]
The header is omitted for redirects.
Configuring X-XSS-Protection¶
There are two configuration options
- policy can be set to :
- Disabled
- FilterDisabled
- FilterEnabled
- blockMode adds mode=block in the header, defaults to false
Configuration | Resulting header |
---|---|
policy=”Disabled” | None |
policy=”FilterDisabled” | X-XSS-Protection: 0 |
policy=”FilterEnabled” blockMode=”true” | X-XSS-Protection: 1; mode=block |
In web.config:
<x-XSS-Protection policy="FilterEnabled" blockMode="true"/>
<x-XSS-Protection policy="FilterDisabled" />
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseXXssProtection(options => options.EnabledWithBlockMode());
}
Or as an MVC attribute, defaults to “FilterDisabled” blockMode=”true”:
[XXssProtection]
[XXssProtection(Policy = XXssProtectionPolicy.Disabled)]
The header is omitted for redirects and static content.
Configuring X-Content-Type-Options¶
There are two settings:
Configuration | Resulting header |
---|---|
enabled=”false” | None |
enabled=”true” | X-Content-Type-Options: nosniff |
In web.config:
<x-Content-Type-Options enabled="false"/>
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseXContentTypeOptions();
}
Or as an MVC attribute (which defaults to true):
[XContentTypeOptions]
[XContentTypeOptions(Enabled = false)]
The header is omitted for redirects.
Configuring X-Download-Options¶
There are two settings:
Configuration | Resulting header |
---|---|
enabled=”false” | None |
enabled=”true” | X-Download-Options: noopen |
In web.config:
<x-Download-Options enabled="false"/>
NWebsec.Owin: Register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseXDownloadOptions();
}
Or as an MVC attribute (which defaults to true):
[XDownloadOptions]
[XDownloadOptions(Enabled = false)]
The header is omitted for redirects.
Configuring X-Robots-Tag¶
You might be familiar with the Robots Exclusion Protocol (REP), often communicated by a robots.txt file on the root of a site or through meta tags in HTML such as:
<meta name="robots" content="noindex, nofollow">
REP gives some control over which content search engines will index on your site, i.e. for search engines who respect REP. Remember, you are politely asking search engines to not index your content — but not all search engines are that polite.
In addition to robots.txt and meta tags, some of the major search engines support REP through an HTTP header. The following header is the equivalent to the aforementioned meta tag:
X-Robots-Tag: noindex, nofollow
Using the HTTP header can be a nice alternative to the robots.txt and the meta tags — especially for content other than html such as PDF, XML or Office files. The header is supported by Bing and Google, refer to these two resources for the nitty gritty details:
- Prevent a bot from getting “lost in space”
- Robots meta tag and X-Robots-Tag HTTP header specifications
NWebsec lets you emit the X-Robots-Tag header as of version 2.1.0. The directives supported are:
- noindex - Instructs search engines to not index the page
- nofollow - Instructs search engines to not follow links on the page
- nosnippet - Instructs search engines to not display a snippet for the page in search results
- noarchive - Instructs search engines to not offer a cached version of the page in search results
- noodp - Instructs search engines to not use information from the Open Directory Project for the page’s title or snippet
- notranslate - Instructs search engines to not offer translation of the page in search results (Google only)
- noimageindex - Instructs search engines to not index images on the page (Google only)
There are several ways to enable the X-Robots-Tag header:
With NWebsec you can enable it in web.config (enabled and at least one directive must be true):
<nwebsec>
<httpHeaderSecurityModule>
<x-Robots-Tag enabled="true"
noIndex="true"
noFollow="false"
noArchive="false"
noOdp="false"
noSnippet="false"
noImageIndex="false"
noTranslate="false"/>
</httpHeaderSecurityModule>
</nwebsec>
With NWebsec.Owin, you register the middleware in the OWIN startup class:
using NWebsec.Owin;
...
public void Configuration(IAppBuilder app)
{
app.UseXRobotsTag(options => options.NoIndex().NoFollow());
}
NWebsec.Mvc lets you register an MVC filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new XRobotsTagAttribute() { NoIndex = true, NoFollow = true });
}
You can also set the attribute on controllers and actions, see NWebsec.Mvc for details.
Suppressing version headers¶
It’s considered good practice to remove the version number headers commonly emitted by IIS and ASP.NET applications, you’ve probably seen these before:
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 3.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
NWebsec helps you suppress almost all of these version headers, i.e. all but the Server: Microsoft-IIS/8.0 header.
NWebsec.Mvc will disable the MVC version header programatically (through a PreApplicationStartMethodAttribute).
The NWebsec package will add the following to your web.config to get rid of the AspNet version header:
<system.web>
<httpRuntime enableVersionHeader="false"/>
</system.web>
The X-Powered-By: ASP.NET header is actually added by the IIS itself. Unfortunately, it doesn’t exist in the header collection when any of the ASP.NET events fire in the processing pipeline. To get rid of the X-Powered-By: ASP.NET header, NWebsec will add the following to your web.config. This will clear the list of headers added by IIS (except the Server header).
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<clear />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Unfortunately, NWebsec no longer removes the “Server” header as of version 3.0. The “Server” header is added by IIS, and can only be removed by an ASP.NET application in the PreSendRequestHeaders event. However, in mid 2012 Microsoft published new guidance on What not to do in ASP.NET, and what to do instead, instructing developers to not use the PreSendRequestHeaders event.
Prior to version 3, NWebsec did its work in the PreSendRequestHeaders event. From version 3, NWebsec uses different events to set headers to avoid issues with async requests. Consequently, the “Server header” can no longer be removed by NWebsec.
Note that removing the “Server” header primarily saves you some bandwith, it’s not that much of a security improvement. You can use a variety of fingerprinting techniques to detect the IIS version, see e.g. Fingerprinting IIS.
If you’re determined to remove the header, there are a few ways you can go about it.
- If you have a load balancer/reverse proxy, it might be able to remove the “Server” header for you.
- You can install UrlScan on each web server to get rid of the “Server” header. For instructions on how to do it by hand, go read Shhh… don’t let your response headers talk too loudly.
Good luck!
Those running an older versions should consult Breaking changes before upgrading major versions.
You’re strongly advised to upgrade if you’re still running NWebsec 2.x, as you can run into issues with async requests due to changes in ASP.NET.
Web.config¶
Only the most basic configuration is added to web.config when installing the NWebsec library with the NuGet package manager. Here’s an example of what’s added to web.config:
<configuration>
<configSections>
<sectionGroup name="nwebsec">
<section name="httpHeaderSecurityModule" type="NWebsec.Modules.Configuration.HttpHeaderSecurityConfigurationSection, NWebsec, Version=4.1.0.0, Culture=neutral, PublicKeyToken=3613da5f958908a1" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web>
<httpRuntime enableVersionHeader="false"/>
</system.web>
<system.webServer>
<modules>
<add name="NWebsecHttpHeaderSecurityModule" type="NWebsec.Modules.HttpHeaderSecurityModule, NWebsec, Version=4.1.0.0, Culture=neutral, PublicKeyToken=3613da5f958908a1" />
</modules>
<httpProtocol>
<customHeaders>
<clear />
</customHeaders>
</httpProtocol>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="NWebsecConfig" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
<nwebsec>
<httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd">
</httpHeaderSecurityModule>
</nwebsec>
</configuration>
The NWebsec config section is declared, the module is loaded, custom http headers will be cleared, the NWebsec configuration directory is declared as a hidden segment, and an empty NWebsec configuration section is added.
You’ll probably notice that configuration is also added to the <system.webserver> section in order to load the NWebsec httpHeaderModule . If you’re running on IIS 6 or in Classic Pipeline Mode you will have to do some manual changes to your web.config to load the module, see IIS 6 or IIS 7 Classic Pipeline Mode.
The configuration schema gives you intellisense for all NWebsec configuration elements, so feel free to start of with the empty section and add the security headers you need.
For the curious, here’s a complete configuration section with all headers disabled:
<configuration>
<nwebsec>
<httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd">
<redirectValidation enabled="false">
<allowSameHostRedirectsToHttps enabled="false" httpsPorts="8443,443"/>
<add allowedDestination="http://www.nwebsec.com/"/>
</redirectValidation>
<setNoCacheHttpHeaders enabled="false" />
<x-Robots-Tag enabled="false"
noIndex="false"
noFollow="false"
noArchive="false"
noOdp="false"
noSnippet="false"
noImageIndex="false"
noTranslate="false"/>
<securityHttpHeaders>
<x-Frame-Options policy="Disabled"/>
<strict-Transport-Security max-age="00:00:00"
includeSubdomains="true"
httpsOnly="true"
preload="false" />
<!--<public-Key-Pins max-age="00:00:10">
<certificates>
<add thumbprint="cert thumbprint" storeName="Root" />
</certificates>
<pins>
<add pin="Base64 pin"/>
</pins>
</public-Key-Pins>-->
<x-Content-Type-Options enabled="false" />
<x-Download-Options enabled="false" />
<x-XSS-Protection policy="Disabled" blockMode="true" />
<content-Security-Policy enabled="false">
<default-src self="true"/>
<script-src self="true">
<add source="nwebsec.codeplex.com" />
<add source="scripts.nwebsec.com" />
</script-src>
<style-src unsafeInline="false" self="true" />
<img-src self="true">
<add source="images.nwebsec.com"/>
</img-src>
<object-src none="true" />
<media-src none="true" />
<frame-src none="true" />
<font-src none="true" />
<connect-src none="true" />
<frame-ancestors none="true" />
<base-uri self="true"/>
<child-src self="true"/>
<form-action self="true"/>
<sandbox enabled="true"/>
<plugin-types>
<add media-type="application/pdf"/>
</plugin-types>
<report-uri enableBuiltinHandler="true"/>
</content-Security-Policy>
<!-- This section works exactly like "x-Content-Security-Policy", but will output report-only headers instead. -->
<content-Security-Policy-Report-Only enabled="false">
<default-src self="true" />
<script-src unsafeEval="true" unsafeInline="true" />
<report-uri>
<add report-uri="/cspreporthandler" />
</report-uri>
</content-Security-Policy-Report-Only>
</securityHttpHeaders>
</httpHeaderSecurityModule>
</nwebsec>
</configuration>
IIS 6 or IIS 7 Classic Pipeline Mode¶
If your application is running in Classic Pipeline Mode (as opposed to Integrated Pipeline Mode), you’ll have to add configuration by hand to load the HttpHeaderModule, here’s an example:
<system.web>
<httpModules>
<add name="NWebsecHttpHeaderSecurityModule" type="NWebsec.Modules.HttpHeaderSecurityModule, NWebsec, Version=4.1.0.0, Culture=neutral, PublicKeyToken=3613da5f958908a1" />
</httpModules>
</system.web>
You should also consider removing the <system.webServer>
section if that did not exist before it was added by the NuGet installer.
Breaking changes¶
Every now an then there needs to be some breaking changes between NWebsec versions. You’ll find them documented here.
NWebsec 4.x / NWebsec.Owin 2.x¶
Removed support for the X-Content-Security-Policy and X-WebKit-Csp headers¶
Browser CSP implementations that honor these experimental headers tend to be buggy. The worst example being Safari 5, which often would completely break the site.
- You’ll get a configuration error if you have these enabled in web.config
- You’ll get a compilation error if you have these enabled through MVC attributes
Removed/moved all types from the NWebsec.Csp namespace¶
Consequently, the following using statement will trigger a compilation error:
using NWebsec.Csp;
You can safely remove it from your code. This was legacy from the very first versions of NWebsec.
CSP attribute source configuration.¶
With NWebsec.Mvc versions < 4 you would configure CSP sources with an enum. This made the code unnecessarily hard to read.
[CspDefaultSrc(Self = Source.Enable)]
[CspDefaultSrc(Self = Source.Disable)]
As of version four you configure them with a boolean. This makes makes the code easier to read.
[CspDefaultSrc(Self = true)]
[CspDefaultSrc(Self = false)]
You can easily update your code by doing a search and replace for these.
HSTS - Strict Transport Security¶
NWebsec has not been completely in line with the HSTS specification which states that the header should only be set over secure connections. The default for NWebsec has now been changed for the sake of consistency with the upcoming support for HPKP which also should be set over secure connections only.
This means that the header might not be set if you’re using a TLS terminator, which communicates with your website over plain HTTP. You can still set the header in this scenario, but you must specifically enable it.
To enable it for all responses you must set the httpsOnly attribute to false in web.config
<strict-Transport-Security httpsOnly="false"/>
Or you specify that it should be included in all responses for the OWIN middleware:
app.UseHsts(options => options.MaxAge(days:18*7).AllResponses());
NWebsec 3.x¶
The <suppressVersionHttpHeaders>
configuration setting is no longer supported.
NWebsec and the SDL¶
You might be familiar with Microsoft’s Security Development Lifecycle (SDL) — a software security assurance process. The SDL is how Microsoft ensures that security is taken care of throughout their development processes. It’s broken down in sections for different development phases and each section contains requirements and recommendations to ensure both security and privacy in their software. They’ve published the process guidance to aid others in introducing security activities in their own development processes.
Many of the requirements and recommendations are concrete and actionable — this page lists the SDL requirements that NWebsec will help you fulfill (the others you’ll have to take care of yourself #kthxbai :).
These requirements are from the SDL Process Guidance Version 5.2 released May 23, 2012.
Session fixation¶
From Phase Two Design, Security Recommendations:
Strong log-out and session management. Proper session handling is one of the most important parts of web application security. At the most fundamental level, sessions must be initiated, managed, and terminated in a secure manner. If a product employs an authenticated session, it must begin as an encrypted authentication event to avoid session fixation.
You need to take care of the encryption by running your web application over TLS, but session fixation is taken care of when you combine that recommendation with:
Authentication events must invalidate unauthenticated sessions and create a new session identifier.
[[NWebsec.SessionSecurity]] ensures that unauthenticated sessions IDs aren’t reused for authenticated sessions and prevents session fixation attacks through the means of [[Authenticated session identifiers]].
The X-Download-Options header¶
From Phase Two Design, Security Recommendations:
Apply no-open header to user-supplied downloadable files. Use the HTTP Header X-Download-Options: noopen for each HTTP file download response that may contain user-controllable content. Recommended tool: Casaba Passive Security Auditor.
See Configuring X-Download-Options to let NWebsec add this header for you.
The X-Content-Type-Options header¶
From Phase Three Implementation. Security Requirements:
Internet Explorer 8 MIME handling: Sniffing OPT-OUT. This recommendation addresses functionality new in Internet Explorer 8 that may have security implications in some cases. It is recommended that for each HTTP response that could contain user controllable content, you utilize the HTTP Header X-Content-Type-Options:nosniff. The Watcher tool may be of use in meeting this requirement.
See Configuring X-Content-Type-Options to let NWebsec add this header for you.
The Content-Security-Policy header¶
From Phase Three Implementation. Security Recommendations:
Do not use the JavaScript eval() function (or equivalents). The JavaScript eval() function is used to interpret a string as executable code. While eval() enables a web application to dynamically generate and execute JavaScript (including JSON), it also opens up potential security holes, such as injection attacks, where an attacker-fed string may also get executed. For this reason, the eval() function or functional equivalents, such as setTimeout() and setInterval(), should not be used.
See Configuring Content-Security-Policy to let NWebsec add this header for you — CSP will disable all these JavaScript functions (see the script-src directive in section 4.2 of the CSP specification).
The X-Frame-Options header¶
From Phase Three Implementation. Security Recommendations:
ClickJacking defense. For each page that could contain user controllable content, you should use a “frame-breaker” script and include the HTTP response header named X-FRAME-OPTIONS in each authenticated page. The Watcher tool may be of use in meeting this recommendation. The exit criteria for this recommendation is as follows:
- A “frame-breaker” script is included in each authenticated page to prevent unintentionally framing.
- The X-FRAME-OPTIONS header has been added to all authenticated page HTTP responses that should not be framed (for example, DENY) or is utilized to only allow trusted sites to frame site content (for example, the current site with the use of SAMEORIGIN).
See Configuring X-Frame-Options to let NWebsec add this header for you. Also remember that you need to take care of the “frame-breaker” script to fully meet this requirement.
Redirect validation¶
From Phase Three Implementation. Security Requirements:
Safe redirect, online only. Automatically redirecting the user (through Response.Redirect, for example) to any arbitrary location specified in the request (such as a query string parameter) could open the user to phishing attacks. Therefore, it is recommended that you not allow HTTP redirects to arbitrary user-defined domains.
See Redirect validation to add enable the NWebsec safety net for unvalidated redirect vulnerabilities.
NWebsec consists of several security libraries for ASP.NET applications. Three of these libraries work together to remove version headers, control cache headers, stop potentially dangerous redirects, and set important security headers. They are collectively referred to as “NWebsec”:
If you’re not sure what “security headers” are, check out this blog post: Security through HTTP response headers.
There are also two stand-alone libraries. Since they don’t follow the versions of the security header libraries, they are documented as separate projects.
NWebsec.SessionSecurity improves ASP.NET session security. Read more about the improvements in the blog post Ramping up ASP.NET session security.
NWebsec.AzureStartupTasks helps you harden the TLS configuration for Azure web role instances. Learn why you need to harden the default TLS configuration in the blog post Hardening Windows Server 2008/2012 and Azure SSL/TLS configuration.
Check out the NWebsec demo site to see the headers and session security improvements in action.
To keep up with new releases or to give feedback, find @NWebsec on Twitter. You can also get in touch at nwebsec (at) nwebsec (dot) com.
\ Sort by:\ best rated\ newest\ oldest\
\\
Add a comment\ (markup):
\``code``
, \ code blocks:::
and an indented block after blank line