Quick Guide to Security Headers - Part Two

In our last post, we explored 3 of the most important security headers: Content-Security-Policy, Strict-Transport-Security and X-Frame-Options. In this post, we’ll review four additional security headers and then wrap up our discussion.

1. X-Content-Type-Options

This header only has one option: nosniff. Although it’s quite simple to implement (“X-Content-Type-Options: nosniff”), the logic behind why you’d want to incorporate this header is nuanced.

When a server returns a resource, it typically includes a “Content-Type” header, which tells the browser the type of resource to load. For example, “Content-Type: application/javascript”, “Content-Type: text/plain”, “Content-Type: text/html”, etc. In certain cases, if the browser believes the server specified the wrong content type, it can override the content type of the resource rendered in the browser. This is done through a process called MIME type sniffing, which just means the browser looks at the data sent by the server and ascertains the real content-type.

This behavior is super useful if the server delivers a resource with a mismatched content type. For example, if the server delivers a JavaScript resource, but calls it “Content-Type: text/plain”, it won’t render the JavaScript. However, with MIME type Sniffing enabled, the browser could determine the file is actually JavaScript and render it as JavaScript.

The trouble occurs when an attacker uploads a file, like malicious JavaScript, which is typically rendered as “Content-Type: text/plain”. Forcing user files to be rendered as “text/plain” largely avoids issues with user-uploaded content executing. However, if the browser MIME type Sniffs and renders the JavaScript, then the user visiting that webpage would be subject to whatever malicious JavaScript the attacker uploaded.

We take advantage of this type of behavior frequently when client web applications improperly sanitize user file uploads.

To prohibit this behavior, we need to block MIME type Sniffing via the X-Content-Type-Options header.

2. Referrer-Policy

When a link is followed on a webpage, whether by a user clicking a link or a resource loading in the background, the browser will send client request information to the target server. Of most concern is the “Referer” value. Yes, I know it’s spelled incorrectly. That’s just how it is. Interestingly, “Referrer-Policy” is spelled correctly. It’s annoying. Oh well.

Anyway, when your browser sends the target server your “Referer” information, it sends the URL that you’re making the request from.

The “Referer” header potentially leaks sensitive information. It’s possible just disclosing the URL you’re coming from is a security issue. A more troubling case is when the entire URL is sent and leaks important information, like a password reset token.

There are many different options for the “Referrer-Policy” to help combat these issues. The most strict option is “no-referrer”, which will omit the “Referer” header completely. Please review Mozilla’s documentation to determine which of the 8 options makes the most sense for your web application.


3. Feature-Policy

According to Mozilla’s documentation, this header is currently still in an experimental state and will change over to the name “Permissions-Policy”. The idea is to explicitly allow or deny the use of specific “Directives” in the browser. What is a “Directive”? There’s a long list located here.  As an example, the accelerometer, autoplay, camera, gyroscope and full screen features are all considered directives. To use the header to deny the use of the autoplay feature, for example, you set the policy: “Feature-Policy: autoplay ‘none’”.

Why is this important?

If this only applied to content directly coming from your site, it might not be that interesting. But the “Feature-policy” header allows developers to block any directive usage from within an embedded iframe. This protects your users by blocking iFrames from conducting potential privacy-violating activates such as enabling the user’s microphone or grabbing their geolocation information. This article breaks this feature into much greater detail.

Check out the documentation to see if these would be useful for your application.

4. X-XSS-Protection

If your Content Security Policy disables all inline JavaScript (via the “unsafe-inline” value), then you probably don’t need this header. However, users with older web browsers, especially those that don’t support a Content Security Policy, would benefit from the X-XSS-Protection header. Basically, this header instructs the browser to block a page from loading when it detects reflected Cross-Site Scripting. We recommend the setting: “X-XSS-Protection: 1; mode=block”. It’s super simple. QA your site after enabling it to make sure the XSS filter in your browser doesn’t mistake any of your legitimate code for a malicious XSS, since it will block the page from rendering.

The HTTP X-XSS-Protection response header is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks. Although these protections are largely unnecessary in modern browsers when sites implement a strong Content-Secu…


So, you know the headers you want to use. How do you implement them? Well, you have two options: within the web application or on the server. By far, implementing security headers on the server is more common. There are a ton of articles about how to specify content security policies and enforce strict transport security on Apache, Nginx and IIS servers. However, you can also implement these policies at the application layer. We’re big fans of Python Flask at FortyNorth Security and implementing security headers is simple. Here’s an example from a repo:

If for whatever technical reason you decide to implement security headers at the application level, ensure that any additional/replicating security headers at the server level don’t contradict your work within the application.

How can I tell how good our policy is?
Huge shout out to @Scott_Helme for creating the site SecurityHeaders.com. For nearly all of our web application assessments, we’ll run our client’s domain(s) through securityheaders.com and screenshot the site grade to provide our clients with a baseline for their security headers posture.

Pen testers, add this into your project checklist. Web application developers, throw your site in here to generate a baseline.

Concluding Thoughts

We’d be remiss if we don’t mention that although they are not headers per-se, consider other server and application security configurations, like setting cookies for secure and httpsOnly.

Generating HTTP security headers is an iterative and evolving process. Your first round of security headers might work today, but as your application grows and changes, you’ll need new/amended headers. Also, over the last few years, we’ve seen new types of security headers pop up and I guarantee we’ll see more in the future. Keep watch for new headers and evaluate whether they make sense for your web application.

And when in doubt, just contact us and we’d be happy to review your security header’s posture.

By: Joe Leon