Security is always an essential aspect of any web application. HTTP security headers play an important role to tighten up the security of web application. It acts as an additional security layer to defend many common vulnerabilities and attacks like clickjacking, cross-site scripting (XSS), cross-site injection, MIME Sniffing, Protocol Downgrading etc. Actually, it does a lot at the cost of few lines of configuration in Web Server (e.g. Nginx, Apache, Lighttpd etc.) or code depending on how you are going to implement it.

For this site, I have used serverless Cloudflare Workers to add security headers in response.

How it works?

When a client (Browser) communicates with server over HTTP protocol, server pass additional information / meta data (Response Headers) along with HTTP Response Message. Now browser may utilize those headers to process the response the way Server wants it to process. So it is at the discretion of the browser used to make that HTTP request.

Security Response Headers

Following list of HTTP Security Headers are taken from OWASP Secure Headers Project page. Everything is well documented there along with a browser compatibility matrix.

  • HTTP Strict Transport Security (HSTS)
  • Public Key Pinning Extension for HTTP (HPKP)
  • X-Frame-Options
  • X-XSS-Protection
  • X-Content-Type-Options
  • Content-Security-Policy
  • X-Permitted-Cross-Domain-Policies
  • Referrer-Policy
  • Expect-CT
  • Feature-Policy

Scan your web site

A bunch of tools are listed in "Technical Resources" section of OWASP Secure Headers Project page. Pick one that suits your requirement and scan your site. For illustration purpose, I am going to use https://securityheaders.com/ to scan SrcCodes.com

And security report summary looks pretty bad.

securityheaders security report summary

Note all the findings from the scan report. In my case, I need to harden following security headers.

  • HTTP Strict Transport Security (HSTS)
  • X-Content-Type-Options
  • Content-Security-Policy
  • Referrer-Policy
  • Feature-Policy

How?

It depends on which server you are using and how you prefer to implement.

For example,
Use ngx_http_headers_module for Nginx server.
Use mod_headers module for Apache HTTP Server.

For my site, I have used serverless Cloudflare Workers to modify the response headers. Concept is same. It is just different implementation approach.

serverless Cloudflare Workers

Below code gets executed before each response ('content-type' is 'text/html') is served to web browser.

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request));
  })
  
async function handleRequest(request) {
    const response = await fetch(request);
    return getSecuredResponse(response);
}

const securityHeaderMap = {
    "Strict-Transport-Security" : "max-age=2592000; includeSubDomains; preload",
    "Content-Security-Policy" : "upgrade-insecure-requests",
    "Feature-Policy" : "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'",
    "Referrer-Policy" : "strict-origin-when-cross-origin",
    "X-Content-Type-Options" : "nosniff",
    "X-Frame-Options" : "DENY",
    "X-Xss-Protection" : "1; mode=block" 
};

const headerDeletionList = [
    "X-Powered-By"
];

async function getSecuredResponse(response) {
    let responseHeaders = new Headers(response.headers);

    // Return immediately if response is not 'text/html'
    if(isResponseContentTypeNotHtml(responseHeaders)) {
        return response;
    }

    // Add security headers to response.
    Object.entries(securityHeaderMap).map(([name, value]) => responseHeaders.set(name, value));

    // Delete headers from response.
    headerDeletionList.forEach(name => responseHeaders.delete(name));

    // Return response with modified headers.
    return new Response(response.body , {
        status: response.status,
        statusText: response.statusText,
        headers: responseHeaders
    });
}

function isResponseContentTypeNotHtml(headers) {
    const contentType = headers.get('content-type');
    return !contentType || !contentType.includes("text/html");
}
Cloudflare Workers Code

Note: I have deleted "X-Powered-By" header as it exposes underlying server technology.

Let's scan once again. But this time it looks pretty good.

securityheaders security report summary

References