Como proteger os cabeçalhos HTTP no Apache


Última atualização em 2 de Março, 2021 por Vítor Fernandes.

Os cabeçalhos HTTP permitem que o cliente e o servidor passem informações adicionais com a solicitação ou a resposta. Um cabeçalho de solicitação consiste em seu nome que não diferencia maiúsculas de minúsculas seguido por dois pontos ‘:’ e, em seguida, por seu valor (sem quebras de linha). Espaço em branco à esquerda antes do valor ser ignorado.
developer.mozilla.org

Os nossos cabeçalhos HTTP já se encontram protegidos!

Para um maior esclarecimento, lê alguns exemplos em Permissions-Policy e Policy directives na página oficial. Verifica as nossa configurações e ajusta omáximo possível!

1 – Define os cabeçalhos (Header) com a opção “always”

Porque sem a opção (always) o envio da definição só irá em respostas com sucesso (2xx status code).

Header always set Connection Keep-Alive
Header always set Keep-Alive "timeout=5, max=1000"
Header always set X-Cache HIT
Header always set Alt-Svc h2=":443"
Header always set Content-Language pt-PT
Header always set Accept "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8"
Header always set Cache-Control "no-cache, no-store, must-revalidate"
Header always set Strict-Transport-Security "max-age=31536000; preload"
Header always set Permissions-Policy "autoplay=(self), camera=()"
Header always set Referrer-Policy strict-origin-when-cross-origin
Header always set Content-Security-Policy upgrade-insecure-requests
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options SAMEORIGIN

2 – Desativa opções desnecessárias

Header unset X-Powered-By
Header unset X-DNS-Prefetch-Control
Header unset Last-Modified
Header unset Expires
Header unset Link
Header unset Server
Header unset Via
Header unset P3P
Header unset Public-Key-Pins
Header unset Public-Key-Pins-Report-Only
Header unset X-AspNet-Version
Header unset X-AspNetMvc-version
Header unset X-Runtime
Header unset X-Version
Header unset X-UA-Compatible

Em alguns casos, esta configuração não é permitida porque certos alojamentos limitam o acesso a este tipo de configurações. Desta forma, então, utiliza uma opção “Workers” diponivél na plataforma CloudFlare.

Configuração “Workers” no painel Cloudflare

Em workers, manage workers, create a worker. Depois adiciona o código abaixo.

let securityHeaders = {
	"Content-Security-Policy" : "upgrade-insecure-requests",
	"Strict-Transport-Security" : "max-age=31536000; preload",
	"X-Xss-Protection" : "1; mode=block",
	"X-Frame-Options" : "SAMEORIGIN",
	"X-Content-Type-Options" : "nosniff",
	"Referrer-Policy" : "strict-origin-when-cross-origin",
	"Permissions-Policy" : "autoplay=(self), camera=()",
}

let sanitiseHeaders = {
	"Server" : "headers override",
}

let removeHeaders = [
        "Via",
        "P3P",
        "Link",
        "Server",
	"Expires",
        "Last-Modified",
        "Public-Key-Pins",
        "Public-Key-Pins-Report-Only",
	"X-Runtime",
        "X-Version",
	"X-Powered-By",
	"X-UA-Compatible",
        "X-AspNet-Version",
        "X-AspNetMvc-version",
	"X-DNS-Prefetch-Control",
]

addEventListener('fetch', event => {
	event.respondWith(addHeaders(event.request))
})

async function addHeaders(req) {
	let response = await fetch(req)
	let newHdrs = new Headers(response.headers)

	if (newHdrs.has("Content-Type") && !newHdrs.get("Content-Type").includes("text/html")) {
        return new Response(response.body , {
            status: response.status,
            statusText: response.statusText,
            headers: newHdrs
        })
	}

	Object.keys(securityHeaders).map(function(name, index) {
		newHdrs.set(name, securityHeaders[name]);
	})

	Object.keys(sanitiseHeaders).map(function(name, index) {
		newHdrs.set(name, sanitiseHeaders[name]);
	})

	removeHeaders.forEach(function(name){
		newHdrs.delete(name)
	})

	return new Response(response.body , {
		status: response.status,
		statusText: response.statusText,
		headers: newHdrs
	})
}