There are many ways in which attackers will attempt to exploit your applications and users. Some of the more famous are cross-site scripting (XSS), script injection, clickjacking, insecure requests, and identifying web application frameworks to name a few.
In my previous article, 4 Simple Steps to Secure Your Express Node Application, we learned how to use HTTPS to avoid man in the middle attacks, secure our cookies from being read by client side JavaScript, prevent our cookies from being sent on an HTTP request, and how to prevent cross-site request forgery attacks.
This article will focus more on middleware we can add to Express to further lock down our application.
Helmet
We will be using Helmet, a set of Express middleware, to help lock down and secure our web applications.
To install Helmet, simply run the following command in your application.
1
|
|
Here is a simple sample Express application to get us started using Helmet. You will notice the main addition is requiring the Helmet module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
The following sections will dive into each of the security features provided by Helmet.
Content Security Policy (CSP)
Content Security Policy (CSP) is a promising defense against the risk and impact of XSS attacks. Its main goal is to prevent anything unintended being injected into our page. This can include frames, images, tracking scripts, and opening the door to XSS vulnerabilities.
CSP works by setting whitelists in the Content-Security-Policy
response header to define sources of trusted content. The browser is then only allowed to execute or render resources from those trusted sources. This means that if someone were to successfully inject their script into your page, the browser would not execute it as the source would not be in the whitelist.
The main types of resources that can be controlled with CSP are:
- JavaScript
- Stylesheets
- Images
- AJAX, WebSockets, etc.
- Fonts
- Plugins
- HTML5 Media Elements
- Frames
So enough about what CSP is and what it provides. You are here to learn how to protect your web application. If you want to learn more, I would suggest a great article on HTML5 Rocks. Let’s dive into how we can implement CSP with Helmet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
So what I have done here is told our Express application to use helmet.csp() and passed in some optional parameters. Helmet does have a way to use it with defaults, but I would suggest being explicit so you know exactly what it is being done. If you do want to use the defaults, you can do it with app.use(helmet());
.
defaultSrc
This is the default policy for loading all content. Whatever is defined here applies to all the other type unless you set them to 'none'
. In our case we are saying that any content coming from the same origin is allowed.
scriptSrc
This is the policy for controlling the valid sources of JavaScript. In our case we are setting *.google-analytics.com
as an allowed source for scripts. This is in addition to the same origin rule we defined in defaultSrc
.
styleSrc
This is the policy for controlling the valid sources of stylesheets. What we are doing here is using the 'unsafe-inline'
keyword to allow inline stylesheets. If this was not set, any inline stylesheets would not work.
imgSrc
This is the policy for controlling the valid sources of images. Just like scriptSrc
, we are setting the allowed domain for images.
connectSrc
This is the policy for controlling the valid sources of AJAX, WebSockets, or EventSource. In this case, we are using the 'none'
keyword to state that no content of this type should be allowed. This overrides the defaultSrc
directive.
fontSrc
This is the policy for controlling the valid sources of fonts. It is blank to state the only whitelist to use is the one defined in defaultSrc
.
objectSrc
This is the policy for controlling the valid sources of plugins like <object>
, <embed>
, or <applet>
. It is blank to state the only whitelist to use is the one defined in defaultSrc
.
mediaSrc
This is the policy for controlling the valid sources of HTML5 media types like <audio>
or <video>
. It is blank to state the only whitelist to use is the one defined in defaultSrc
.
frameSrc
This is the policy for controlling the valid sources of frames. It is blank to state the only whitelist to use is the one defined in defaultSrc
.
There are four special keywords you can use when defining whitelists:
'none'
will match nothing'self'
will match the current origin but not subdomains'unsafe-inline'
allows inline JavaScript and CSS'unsafe-eval'
allows things like eval() to work
There are also 5 other directives you can use. If interested, you can read more about them here.
sandbox
eportUri
reportOnly
setAllHeaders
safari5
And finally here is what the response header looks like for our example (newlines added for better reading).
1 2 3 4 5 6 7 8 9 10 |
|
XSS Filter
The XSS Filter was created to help protect against XSS attack. Ironically, it actually helps attackers perform XSS attacks against older versions of Internet Explorer! You can read more about it here.
What we need to do is have the X-XSS-Protection
header set to 0
in order to disable it for older versions of IE. Helmet provides a piece of middleware to do just that. It will detect the web browser and either set it to 1; mode=block
or 0
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Here is what the response header looks like when using a modern web browser without the exploit.
1
|
|
And here is what it would look like on an older version of Internet Explorer with the exploit.
1
|
|
Frame Options
To help mitigate the risk of clickjacking attacks, Helmet offers a piece of middleware to help control the X-Frame
response header. What this does is allow you to control if and where your page can be put into a <frame>
or <iframe>
. This is also nice even from a non security point of view. You may not want your site to be loaded within anyone else’s frames from a purely aesthetic point of view.
There are three options with the X-Frame
response header:
- Deny - Does not allow your page to be served inside any frames.
- SameOrigin - Allows your page to be served inside a frame with the same origin.
- Allow-From - Allows your page to be served inside a frame from a specific URL.
Deny
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Here are the response headers.
1
|
|
SameOrigin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Here are the response headers.
1
|
|
Allow-From
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Here are the response headers.
1
|
|
HTTP Strict Transport Security (HSTS)
HTTP Strict Transport Security (HSTS) allows a web server to tell user agents to interact with it in the future only over HTTPS. It controls it by defining a period of time with the Strict-Transport-Security
response header. This is great if you are running an HTTPS site but still have an HTTP endpoint in order to provide redirection to the HTTPS endpoint. This can help reduce the chance of Man-In-The-Middle (MITM) attacks by reducing the frequency of requests being made over insecure channels.
Helmet provides middleware to help control setting and configuring the HSTS headers. You can specify how long the user agent should make requests over HTTPS and whether or not to include subdomains.
Here it is in action. We are telling it to set the maximum age to 90 days (value is in milliseconds). While the code uses milliseconds, the response header will be in seconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Here are the response headers.
1
|
|
You should be aware that this only works if your site is running over HTTPS. The middleware will check to see if req.secure
is set to true which is automatically populate by Express.
Hide X-Powered-By
By default, Express will add the X-Powered-By
header to each response. The actual response header will look like this:
1
|
|
While by itself it doesn’t cause any security holes, it does give potential attacker useful information. With this information they can focus their attack to exploit known vulnerabilities in Express and Node.
Helmet provides middleware to either strip out the X-Powered-By
header or set it to anything else you want.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
If you don’t want to remove the header, you can set it to anything else using.
1
|
|
Which results in the following response header.
1
|
|
Other
There are a few more things Helmet provides. The documentation on the GitHub repository does a good job of explaining them. If you want to learn more, I would suggest checking each of them out.
- IE, Restrict Untrusted HTML
- Don’t Infer The MIME Type
- Turn Off Caching (I would highly advise against this. Caching is very good for web performance.)
- Restrictive Crossdomain.xml
Wrap Up
Helmet is an amazing piece of Express middleware that can help you easily lock down your web application. I would suggest you take the time to better understand the types of attacks and fixes talked about so you can better secure your applications.
I have a lot more tutorials coming so be sure to subscribe to my RSS feed or follow me on Twitter. Also, if there are certain topics you would like me to write on, feel free to leave comments and let me know.