Security Headers

A Practical View

I see specifically in DPO circles there is a lot of talking about Security Headers, where there are nice tools to assess the security of your website, specifically in this context: SecurityHeaders.com

Given that I see no real aboundance of technical advice on how to "get green in there", it's no wonder that a lot of prominent websites do perform quite badly in this respect.

I'd have to say this depends also on sometimes unrealistical expectation by a lot of websites that put the eye candy first, and the actual value last, with the (un)obvious result being bad, and sometimes very bad security .

I thought I'd give some short practical advice from a development point of view, if you want to really address the issue and close those gaps.

What are Security Headers

Security Headers are HTTP headers set in customer-facing, web applications to configure security defenses in the web browser.

When those headers are set, the browser makes harder to exploit client-side vulnerabilities such as Cross-Site Scripting and Clickjacking.

The Security Headers can also be used to require the browser to only allow valid TLS communication and valid certificates, and enforce the usage of a specific server certificate.

Regarding what Allen Woods was mentioning about the Security Headers.

Code side

The following implementations, beside being good default options, will give you green marks on https://securityheaders.com/

i.e.: you can just check some random domain in order to see that actually a lot of important websites have a pretty bad score in here.

The following middleware will help out for Golang applications

package middleware

import (
	"github.com/gin-gonic/gin"
)

func SecurityHeader() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Header("Strict-Transport-Security", "max-age=31622400;includeSubDomains")
		c.Header("Content-Security-Policy", "script-src 'self'")
		c.Header("X-Frame-Options", "SAMEORIGIN")
		c.Header("X-Content-Type-Options", "nosniff")
		c.Header("Referrer-Policy", "no-referrer")
		c.Header("Permissions-Policy", "fullscreen=(), geolocation=()")

		c.Next()
	}
}

and the below for Java applications, where the framework of the day doesn't provide one by default (see: Spring)

package net.lulli.filters;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SecurityHeadersFilter implements Filter {
    public SecurityHeadersFilter() {
    }

    public void doFilter(
			ServletRequest req, 
			ServletResponse res, 
			FilterChain chain
		) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)res;
        ((HttpServletRequest)req).getHeader("host");
        resp.setHeader("Strict-Transport-Security", "max-age=31622400;includeSubDomains");
        resp.setHeader("Content-Security-Policy", "script-src 'self'");
        resp.setHeader("X-Frame-Options", "SAMEORIGIN");
        resp.setHeader("X-Content-Type-Options", "nosniff");
        resp.setHeader("Referrer-Policy", "no-referrer");
        resp.setHeader("Permissions-Policy", "fullscreen=(), geolocation=()");
        chain.doFilter(req, resp);
    }
}

I assume that if you are using a framework (otherwise, what is the point of using one?) it also provides a default way to address those issues. I. e. Spring

But a healthy thing to do is to check, for example using curl, you really want to see something like this:

$ curl --head https://kevwe.com
HTTP/2 200 
strict-transport-security: max-age=31622400;includeSubDomains
content-security-policy: script-src 'self'
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
referrer-policy: no-referrer
permissions-policy: fullscreen=(), geolocation=()
content-type: text/html;charset=UTF-8
content-length: 6001
date: Thu, 25 Jul 2024 11:59:31 GMT

If you somehow miss the headers:

strict-transport-security
content-security-policy
x-frame-options
x-content-type-options
referrer-policy
permissions-policy

you probably need to take some action.

If you are going to the length of automating this kind of checks, maybe over a period, or you have a number of domains/subdomains that you need to keep watching, you might want to install the Python library shcheck:

pip3 install shcheck 

You can use it like this:

shcheck.py -j https://kevwe.com 

Needless to say: just replace the url above with the one from the domains you want to keep in check.

If you have jq installed, you can pipe the command into jq in order to get more readable output:

$ shcheck.py -j https://kevwe.com | jq 
{
  "https://kevwe.com": {
    "present": {
      "X-Frame-Options": "SAMEORIGIN",
      "X-Content-Type-Options": "nosniff",
      "Strict-Transport-Security": "max-age=31622400;includeSubDomains",
      "Content-Security-Policy": "script-src 'self'",
      "Referrer-Policy": "no-referrer",
      "Permissions-Policy": "fullscreen=(), geolocation=()"
    },
    "missing": [
      "Cross-Origin-Embedder-Policy",
      "Cross-Origin-Resource-Policy",
      "Cross-Origin-Opener-Policy"
    ]
  }
}

If a fix in the source code is for some reason not viable for you, you can just keep reading and see if a "sysadmin fix" is for you the way to go.

For System Administrators

In general it is a good idea to front your web application with a proxy or balancer, whether you have a need to spread the load or not. A product like HaProxy works very well for TLS termination, for example, this allows you to serve HTTPS sessions on top of application that only handle HTTP traffic or for which the certificate handling is more cumbersome.

In some instances, this is the case for Java applications, which can be quite nuanced for system administrators that is not used to them and / or simply do not need to dig into the details and they are rather interested in having a general solution for most cases, regardleless of the technology.

Products like HaProxy, Nginx, Apache, Caddy all provide a way to set custom headers, as you can see:


A Helping hand

This all matter is nuanced and can get difficult depending also on the type of frontend that you are running and other factors.

If you feel like you need help with this, you can just Reach Out for a quick assessment.



[python] [java] [security] [golang] [git] [certificate]