I recently wrote about Security and performance improvements in your web.config for Asp.Net MVC “Classic” . Since then, our team has gone on a journey to convert to Asp.Net Core 2.1. It took longer than we expected and I’m planning on having a reference blog to show you what we ran into and how we got around it. After the conversion, I ran the Webhint scanner from JS Foundation and Security Headers and found that my security header and caching changes in the web.config aren’t getting applied.
Note: As of August 7th, 2018, Sonarwhal is now “WebHint.io .
TLDR; I’m creating a new project with dotnet new mvc
, running Webhint.io against it, then showing how to remediate the issues with
NWebSec
and other .Net Core middleware. See
my source code on Github
.
Run dotnet new mvc
in a command line (I have version 2.1.4 at the time of writting, but the version doesn’t matter for this context.
There’s a CLI for Webhint and the latest version is 3.2.1 at this moment. Here are the instructions from the Webhint page.
npm install -g hint
Then I fired up my application and ran hint https://localhost:5000
Here’s my initial results:
Visit
to get info on the config. Run npm create hintrc
and then hint ...
again and you’ll be rolling along.
Note: Webhint creates an html report in the hint_report
folder it creates from the folder you ran the cli from. This is the same report as if you ran it from their website. This is a quick way to get links to their documentation. It’s created when you use the default setup.
{
"extends": [
"web-recommended"
]
}
Use NWebSec Middleware to improve security . DamienBod has provided this article and a linked GitHub project that I’m going to use as a reference.
dotnet add package NWebsec.AspNetCore.Middleware
does the trick.
Just follow Damien’s article, it lays it out really well with good references.
Here’s what my Chrome Dev Tools Network tabs shows for the headers on the main page before I added in the security changes:
There are now a lot more security related headers in the response
I ran hint https://localhost:44738
again after following Damien’s advice (
see my commit
). There’s good improvements here, but only by 1 error (no-html-only-headers is one less) according to Webhint.
Note: Remember that I’m running locally and in development mode, so I’m not worried about the ssl errors at this moment.
Note: you also should compare the Lighthouse before and after. I wrote about this in my last security article .
Let’s get more details using the @hint/formatter-codeframe
. We could also look at the details from the html report (which gets overwritten every time you run the command), but let’s see it in the console output.
There is a global config file C:\Users\{user}\AppData\Roaming\npm\node_modules\hint\.hintrc
, but you should change the .hintrc
file that is in the same directory as your CLI command. I looked at
their docs
and changed my .hintrc to use the recommended from their docs.
Note: you can also specify the formatter in-line hint -f codeframe https://wwww.localhost:5001
.
npm install @hint/formatter-codeframe -g
{
"extends": ["web-recommended"],
"formatters": ["codeframe"]
}
Note: Then I tried ‘stylish’, which hung in the finishing...
state for awhile and had a blank output. So I’ll stick with the codeframe style. That gives a lot of details we’d see in the website run scan.
I put in a
quick suggestion on improving their docs
while I was at it.
HINT: --debug
is helpful if you get a blank output hint https://localhost:44738 --debug
showed a lot of errors with ‘ECONNREFUSED’ after I came back to this a few days later. I had to switch to the non-http address to get around that error (I’m not sure why and moved on).
The excel formatter creates a sheet for each resource, there’s a lot of good detail in there individually.
npm install -g @hint/formatter-json
looks to be another nice option
“x-content-type-options requires that all scripts and stylesheets are served with the X-Content-Type-Options: nosniff HTTP response header.” NWebSec reference
Learn how to configure with NWebSec from their docs.
These are fixed by the default.
However, there are a few more flagged that we have to add some customization to fix.
Response should not include unneeded 'x-content-type-options' header. https://localhost:5001/
Response should not include unneeded 'x-content-type-options' header. https://localhost:5001/images/banner1.svg:78:13
Response should not include unneeded 'x-content-type-options' header. https://localhost:5001/favicon.ico
I’ve created an issue on the NWebSec page to see if there is a proper way to do this or they can provide configuration options. I’ll update this when I learn the right way.
Thanks to espenrl , I have an answer. “UseWhen is a builtin API of ASP.NET Core that allows a middleware to be run conditionally. IsCssOrJsFile is my own method. NOTE: MapWhen and UseWhen have very different behavior.”
builder.UseWhen(
ctx => IsCssOrJsFile(ctx.Request.Path),
app => app.UseXContentTypeOptions());
nWebSec reference documentation on SRI.
I’m hoping to add a webpack SRI article in the future. I’ll add a link at that time. Sneak preview: I’m going to use the Waysact library .
Another approach and more info about subresource integrity .
Webhint reports The hash algorithm "sha256" doesn't meet the baseline "sha384" https://code.jquery.com/jquery-3.3.1.min.js:441:5
Let’s fix that hash: <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT=" crossorigin="anonymous"></script>
We can use SRI Hash Generator to get a hash.
pagead2.googlesyndication.com/pagead/js/adsbygoogle.js issues, StackOverflow suggests using cloudflare instead .
I put up a question on StackOverflow .
As far as I see it, the only way to fix SRI for files from a CDN is if the CDN provides the hash that meets the requirements.
Let’s use https://code.jquery.com/jquery-3.3.1.min.js
instead of having it packaged locally.
"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js
is another one we have to deal with a lot.
Strict-Transport-Security “(HSTS) is an opt-in security enhancement that is specified by a web application through the use of a special response header.” Here’s the NWebSec specific docs . “This creates an opportunity for a man-in-the-middle attack. The redirect could be exploited to direct visitors to a malicious site instead of the secure version of the original site.”
app.UseHsts(options => options.MaxAge(days: 30).IncludeSubdomains());
I’m getting warnings, so I’ll assume that it’s my local non-HSTS setup (even after moving `app.UseHsts() inside of development in my Startup.cs) and check it after I deploy. “Strict-Transport-Security: The connection to the site is untrustworthy, so the specified header was ignored.”
Webhint reported “Webhint ‘strict-transport-security’ header ‘max-age’ value should be more than 10886400”, so I changed the MaxAge(days: 30)
from Damien to MaxAge(days: 180)
.
NWebSec docs say “(CSP) provides a safety net for injection attacks by specifying a whitelist from where various content in a webpage can be loaded from.” This is more involved and requires more reading to understand and setup correctly.
app.UseCsp(opts => opts
...
Here’s the official documentation for NWebSec
We’ll whitelist our CDN files and allow local connections at a global level (“define your baseline policy in web.config, CSP middleware or through global filters”)
services.AddMvc(opts =>
{
opts.Filters.Add(typeof(CspAttribute));
opts.Filters.Add(new CspDefaultSrcAttribute { Self = true });
Damien has more info about
NWebSec and CDNs
. First, add integrity hashes to your <script src
in your html.
It is recommended not to broadcast what server technology that is in use. We don’t want to give hackers any free information. IIS exposes the version of MVC and IIS version (or Kestrel) by default.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<httpRuntime enableVersionHeader="false"/>
</system.web>
<system.webServer>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" disableStartUpErrorPage="true" />
<security>
<requestFiltering removeServerHeader="true" />
</security>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By"/>
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Troy Hunt talks about removing server response headers .
Mathias Bynens has good information on rel=noopener .
To fix this, manually add the rel="noopener"
to your links in html. for example: <a href="somewhere.html" target="_blank" rel="noopener"><b>Click me!!1 (now with <code>rel=noopener</code>)</b></a>
We could integrate this into our builds using the CLI, but I’ve put in a feature request for a VSTS build task in the marketplace that would standardize the approach.
We can use Webhint to “lint” our sites for missing industry standard recommendations. I believe that as web developers we should use these tools to ensure we are as secure as we can be (of course more security analysis is needed, but this is a great starting baseline). The tools also teach us about security and other issues.
The cli can give us quick feedback locally and in a build against a more production like environment.
Go out and test your websites with Webhint and see where you can improve today!
I plan a follow up article on how to improve the Interoperability and Performance flags from Webhint soon.
I wrote about Lighthouse in my last security article . That’s another great tool to run often.
You can create custom hints to ensure custom rules for your organization as well!
Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout, I pledge to turn off ads)
Also check out my Resources Page for referrals that would help me.