This means that if we pass anything sensitive, it’ll be cached. We can also fingerprint the server since it shows us that it’s running Express, and we can see there are no clickjacking protections available. So we have some attack vectors and intelligence gathering techniques available to us.
As a quick example, here’s some simple page source to demonstrate clickjacking. This just takes the entire page and embeds it in an iframe:
In reality, this could be a complex system of layers that can trick a user into clicking somewhere they don’t intend. And here is how it manifests itself as generated content:
So let’s implement Helmet here. In the server.js file, we just add the require statement for Helmet and select the modules we want to use. In this case, we’re implementing the XSSFilter, the cache-control headers, the no-sniff option, X-Frame-Options, and we’re hiding the X-Powered-By header to reduce the attack surface and take out the easy fingerprinting for attackers ( https://github.com/relotnek/nodecellar/blob/xss-dev/server.js#L13-L17):
With the options implemented, here’s what the response looks like.
You can see that we have some extra headers set here. Our X-XSS-Protection header is on and locked down for some light XSS protection, we have all of our cache-control headers set correctly, we have our nosniff option set, and we’ve pulled out that pesky X-Powered-By header. With the SAMEORIGIN X-Frame-Options header set, we are no longer vulnerable to clickjacking (if the browser appropriately obeys that header). If we use the same simple test, we can see the page fails to load in the iframe:
So now you can fiddle with the Helmet options and observe them in BurpSuite to see how the headers affect the traffic. Hopefully, it’s helpful in implementing some protections with minimal effort.
All right, so I was about to send this post to the presses, but I ran into something in our open source application and felt like I had to mention it. Node gives us some pretty low-level access when it comes to requests and responses, and because of that, it’s very prone to cross-site scripting and injection attacks. It doesn’t really give us the automatic protection some other frameworks do.
Here’s an example in our demo application; it’s pretty straightforward. If I edit or add a new wine in Node Cellar, I can set any value I want, and there’s no real validation on the front end. Even if it did have some UI validation, it doesn’t prevent me from sending my value straight to the server with an API call. So if I drop a payload such as
<script>alert('node cellar xss')</script>
next to the Region input like so:
then every time this attribute gets rendered, it’s going to show up on the page. And this particular object is loaded on the first page of the Browse Wines page.
So what do we do? Well, the common recommendation is input encoding and output escaping, neither of which are being done here. So let’s look at a simple fix. What if every time I went to update a wine, the dirty input was cleaned prior to being saved to the database?
For that we’re going to require sanitizer, a module which simplifies an implementation of Google Caja. Like before, we add a require statement for sanitizer ( https://github.com/relotnek/nodecellar/blob/xss-dev/routes/wines.js#L2):
For the update function, we iterate through the parameters and clean them with the for loop below, before the data get saved. It’s a rough implementation, but it gets the job done ( https://github.com/relotnek/nodecellar/blob/xss-dev/routes/wines.js#L59-L61):
Now we can see that when we save the Region name (or any attribute for that matter), it’s cleaned with the sanitizer prior to updating. After I click the save button with the new code, the script tags and its contents are removed.
This takes care of scrubbing the html and encoding it on input, but what if someone is able to get dirty data into the database without using my interface? Then we’d still be in trouble. To combat this. we have to leverage some output escaping as well. The output escaping can differ depending on where and how the data is displayed. Since this small side-step into XSS was part of the bonus section, that’s just slightly beyond the scope of this article, but keep an eye out for more. I plan on digging into that next.