Basic security for web apps: HTTP headers👮‍♂️

av paul

Last Updated on Mon, Oct 07, 2019

06 mins read

Security is one of those topics most web developers don't really give much thought until we hear of a data breach! Here are some basic ideas you can use to secure your app now!

Security is a broad and mysterious subject, maybe that's why most software engineers including me dread this topic. When we hear of security we think of cryptography, ciphers and all other complex mathematical algorithms! But security can also be simple!

One report by the World Economic Forum lists Cyberattacks and Data fraud or theft as second and seventh respectively on the list of the top 10 global risks to doing business! As software engineers, we should be adding software security skills to the list of things we know. Threats like Injection and XSS can easily result in the compromise of a whole system while they would easily be mitigated. Let's get to work!

As a point to note, examples used in this article are in NodeJS/Express(and HTML) but they work similarly in other languages/frameworks.

Subresource Integrity

<!--bootstrap files served from the bootstrap CDN-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
// ...
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

What assures you that the above code will load the exact Bootstrap files?

It's very common to have CSS/JS dependencies loaded by a web app. Most of them are served from CDNs. What happens if the CDN server is compromised? Ever heard of the Coinhive miners?. MDN defines Subresource integrity as a security feature that enables the browser to verify that the resources they fetch are delivered without unexpected manipulation.

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

Subresource Integrity is achieved by adding the integrity attribute on the <script> or <link> tag. The attribute's value is the cryptographic hash of the resource. If the resource's hash(by the browser) does not match the value in the integrity attribute, the browser will not execute the script or apply the style!

HTTP Headers

As simple as it is! HTTP headers allow passing additional data between the client and the server! In those additional data, you can pass instructions on what the client can or can not do. Most HTTP headers are set on the server but they are others that you can set on the client using the <meta> tag!

Content-Security-Policy

Content-Security-Policy header allows you to control which resources the browser(user agent) is allowed to load. Simply put, CSP decides which sources should be trusted. This can help to detect and mitigate XSS and Injection attacks.

CSP has a very significant impact on the way your page will be rendered so it deserves(and will take😁) a lot of effort. CSP can be enabled on the server or the client.

app.use((req, res, next) => {
    res.set("Content-Security-Policy", "script-src 'self'; style-src stackpath.bootstrapcdn.com; img-src cloudinary.com");
    return next();
});

Or in an HTML file using the <meta> tag.

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src stackpath.bootstrapcdn.com; img-src cloudinary.com">

The above example sets a CSP for scripts, stylesheets, and images. script-src, style-src, and img-src are called policy-directives, They specify the trusted source for each type. Find other directives here. Following the above CSP, all script files should be loaded from our servers and JS in script tags won't be executed, while stylesheets will be loaded from stackpath.bootstrapcdn.com and images will be loaded from cloudinary. If our web page tries to load a resource from somewhere else, that request will fail.

HSTS header

Do you know the difference between HTTPS and HTTP? If not stop and read this article. Now that you do, HSTS stands for HTTP Strict-Transport-Security. With HSTS a website(or API) informs the browser(client) that it should be accessed using HTTPS, instead of using HTTP. Every attempt to access your website using HTTP will immediately use HTTPS👍

app.use((req, res, next) => {
    res.set('Strict-Transport-Security', 'max-age= 31536000; includeSUbdomains');
    return next();
});

The above example sets the HSTS header with max-age of one year and specifies that HSTS should be applied to subdomains too. max-age specifies for how long the bwoser should keep the record.

X-Frame-Options

Have you ever been in a situation where you are on a website and when you click on a button something totally different than what the button says happens? That kind of attack is called clickjacking and is possible when the attacker can load the web page in a frame. To protect your web application from clickjacking you need to enable X-Frame-Options. In the following example, the response header tells the browser that it should not load that website in a frame.

app.use((req, res, next) => {
    res.set('X-Frame-Options', 'deny');
    return next();
});

In modern browsers this header is similar to frame-ancestors directive in Content-Security-Policy.

app.use((req, res, next) => {
    res.set("Content-Security-Policy", "script-src 'self'; frame-ancestors 'none'");
    return next();
});

X-Content-Type-Options

When you do not set the correct MIME(media) types in response headers, the browser will try to guess the correct MIME type. The idea is nice, but that can leave your app vulnerable to XSS. Instead of allowing the browser to do the guesswork, it sould be your responsibility to set the correct headers.

app.use((req, res, next) => {
    res.set('X-Content-Type-Options', 'nosniff');
    return next();
});

HTTP secure cookies🍪

Web/browser cookies are used for transmitting data between the server and the client. One of the main reasons why cookies are used is for session management: to identify the user. By default, cookies can be read using document.cookie. If an attacker is able to inject JS code into your web application, that code can read all cookies and log them to the attacker's server.

httpOnly cookies can't be read using document.cookie and they are sent to the server only. All cookies with sensitive data should be flagged with both secure and httpOnly.

app.use((req, res, next) => {
    res.set("Set-Cookie", "name=paul; Expres= Fri Oct 04 2019 10:41:03 GMT; Secure; HttpOnly");
    return next();
});

If you enable the above headers, you will have laid out the foundation for a secure web app. One last piece of advice is to avoid using element.innerHTML because it is the easiest entry point for injections and XSS. If you use modern frameworks like React and Angular they are built with that idea in mind. Whenever you are about to use dangerouslySetInnerHTML, stop and think if it is the last option you have🤔 And before rendering user data/input make sure that those data/inputs are sanitized.

Thanks for reading this far! Go and build secure apps!

If you've enjoyed this article and would like to read others follow me on twitter. You will be the first to know when a new post goes out😀👏

If you have a comment, leave it here!

  back home