¿Cómo implementar encabezados seguros con Cloudflare Workers?

Una guía paso a paso para implementar encabezados HTTP seguros en sitios web con tecnología de Cloudflare utilizando Cloudflare Workers.

Hay muchas formas de implementar encabezados de respuesta HTTP para proteger los sitios de vulnerabilidades comunes, como XSS, Clickjacking, MIMI sniffing, inyección entre sitios y muchas más. Su práctica ampliamente adoptada y recomendada por OWASP.

Anteriormente, escribí sobre la implementación de encabezados en un servidor web como Apache, Nginx e IIS. Sin embargo, si usa Cloudflare para proteger y potenciar sus sitios, puede aprovechar Trabajadores de Cloudflare para manipular los encabezados de respuesta HTTP.

Cloudflare Workers es una plataforma sin servidor donde puede ejecutar JavaScript, C, C++, código Rust. Se implementa en todos los centros de datos de Cloudflare, que son más de 200 en todo el mundo.

La implementación es muy sencilla y flexible. Le brinda la flexibilidad de aplicar los encabezados en todo el sitio, incluido el subdominio o URI específico con un patrón coincidenten usando Regex.

Para esta demostración, usaré el código por Scott Helme.

Comencemos… 👨‍💻

  • Inicie sesión en Cloudflare y haga clic en Trabajadores (enlace directo)

  • Copie el código de worker.js de GitHub y pegar en el editor de scripts
const securityHeaders = {
        "Content-Security-Policy": "upgrade-insecure-requests",
        "Strict-Transport-Security": "max-age=1000",
        "X-Xss-Protection": "1; mode=block",
        "X-Frame-Options": "DENY",
        "X-Content-Type-Options": "nosniff",
        "Referrer-Policy": "strict-origin-when-cross-origin"
    },
    sanitiseHeaders = {
        Server: ""
    },
    removeHeaders = [
        "Public-Key-Pins",
        "X-Powered-By",
        "X-AspNet-Version"
    ];

async function addHeaders(req) {
    const response = await fetch(req),
        newHeaders = new Headers(response.headers),
        setHeaders = Object.assign({}, securityHeaders, sanitiseHeaders);

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

    Object.keys(setHeaders).forEach(name => newHeaders.set(name, setHeaders[name]));

    removeHeaders.forEach(name => newHeaders.delete(name));

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

addEventListener("fetch", event => event.respondWith(addHeaders(event.request)));

No guarde todavía; es posible que desee ajustar los siguientes encabezados para cumplir con el requisito.

Política de seguridad de contenido: si necesita aplicar su política de aplicación, puede hacerlo aquí.

Por ejemplo, si necesita obtener contenido a través de iFrame en varias URL, entonces puede aprovechar los ancestros de marcos como se muestra a continuación.

"Content-Security-Policy" : "frame-ancestors 'self' gf.dev kirukiru.es.com",

Lo anterior permitirá cargar el contenido desde gf.dev, kirukiru.es.com y el propio sitio.

X-Frame-Options: puede cambiar a SAMEORIGIN si tiene la intención de mostrar el contenido de su sitio en alguna página dentro del mismo sitio usando iframe.

"X-Frame-Options": "SAMEORIGIN",

Servidor: puede desinfectar el encabezado del servidor aquí. Pon lo que quieras.

"Server" : "kirukiru.es Server",

RemoveHeaders: ¿necesita eliminar algunos encabezados para ocultar las versiones y mitigar la vulnerabilidad de fuga de información?

Puede hacerlo aquí.

let removeHeaders = [
	"Public-Key-Pins",
	"X-Powered-By",
	"X-AspNet-Version",
]

Agregar nuevos encabezados: si necesita pasar algunos encabezados personalizados a sus aplicaciones, puede agregarlos en la sección securityHeaders como se muestra a continuación.

let securityHeaders = {
	"Content-Security-Policy" : "frame-ancestors 'self' gf.dev kirukiru.es.com",
	"Strict-Transport-Security" : "max-age=1000",
	"X-Xss-Protection" : "1; mode=block",
	"X-Frame-Options" : "SAMEORIGIN",
	"X-Content-Type-Options" : "nosniff",
	"Referrer-Policy" : "strict-origin-when-cross-origin",
        "Custom-Header"  : "Success",
}

Una vez que haya terminado de ajustar todos los encabezados que necesita, asigne un nombre al trabajador y haga clic en Guardar e implementar.

¡Excelente! el trabajador está listo y, a continuación, debemos agregarlo al sitio donde desea aplicar los encabezados. Aplicaré esto a mi sitio de laboratorio.

  • Vaya a la página de inicio/panel de control de Cloudflare y seleccione el sitio.
  • Vaya a la pestaña Trabajadores >> Agregar ruta.
  • Introduzca la URL en Ruta; puede aplicar el Regex aquí.
  • Seleccione los trabajadores recién creados y Guardar

Eso es todo; en un segundo, notará que todos los encabezados se implementan en el sitio.

Así es como se ve desde Chrome Dev Tools. También puede probar el encabezado a través de una herramienta de encabezado HTTP.

No sé por qué no se refleja el encabezado del servidor. Supongo que Cloudflare está anulando esto.

Verá, la implementación general lleva ~ 15 minutos, y no se requiere tiempo de inactividad ni reinicio como Apache o Nginx. Si planea aplicar esto a un sitio de producción, le sugiero que primero pruebe en un entorno más bajo, o con la ayuda de una ruta, puede aplicar en las páginas de prueba para verificar los resultados. Una vez satisfecho, empuja hacia donde quieras.

¡Esto es increíble!

Gracias a scott para el código.