tinka alpha
tinkaalpha

Security headers

This guide covers the security headers a Tinka App sends. You will learn how to:

Overview

A browser runs your pages next to every other site its user visits, so a few response headers decide what those other sites can do with your pages: show them in an invisible frame to trick a click, downgrade a visit to plain HTTP, or make the browser guess a file is a script. A Tinka App sends safe answers to all of these on every response, before your middleware runs, and HTTPConfig.Security changes them.

Header Default Stops
Content-Security-Policy: frame-ancestors 'none' On Other sites showing your pages in a frame (clickjacking)
Strict-Transport-Security: max-age=31536000 On, over HTTPS only A later visit being downgraded to plain HTTP
X-Content-Type-Options: nosniff On The browser treating an upload or a JSON response as HTML or a script
Referrer-Policy: strict-origin-when-cross-origin On Full URLs, with their IDs and tokens, leaking to other sites
Cross-Origin-Opener-Policy Off Windows your pages open, or that open them, reaching into them

Requests that change data from another site's page are refused too, before any middleware, as App reference describes, unless CORS lists the site's origin.

Allow framing

No site can show your pages in a frame, yours included. A page meant to be embedded, such as a widget or a preview, lists who may frame it with FrameAncestors, as Content Security Policy sources:

config/http.go
package config

import (
	"strconv"

	"github.com/mucolabs/tinka"
	"github.com/mucolabs/tinka/env"
)

func httpConfig(e *env.Env) tinka.HTTPConfig {
	return tinka.HTTPConfig{
		// Addr is where the App listens. act serve listens for it in
		// development.
		Addr: ":" + strconv.Itoa(e.Int("PORT", 3000)),

		// Debug shows unexpected errors' messages and stacks in responses.
		// It's off unless APP_DEBUG=true, which .env sets on your machine:
		// production reads no .env file, so it stays off there even when
		// APP_ENV is forgotten.
		Debug: e.Bool("APP_DEBUG", false),
		Security: tinka.SecurityConfig{
			FrameAncestors: []string{"'self'", "https://partner.example.com"},
		},
	}
}

'self' lets your own pages frame each other.

Keep browsers on HTTPS

Once a browser has reached your App over HTTPS, Strict-Transport-Security tells it to use HTTPS for the host for a year, even when a link or a typed address says http://. Without it, the first request of a later visit can go over plain HTTP, where anyone on the network can redirect it.

The App sends it only when the request came over HTTPS, either on a TLS connection or through a proxy in TrustedProxies that set X-Forwarded-Proto: https, because browsers ignore it over plain HTTP. It never sends it to localhost, so development over HTTP keeps working.

HSTS changes how long it lasts, and a negative value turns it off. HSTSSubdomains extends it to every subdomain, and HSTSPreload marks the host for the browsers' preload list:

config/http.go
		Security: tinka.SecurityConfig{
			HSTSSubdomains: true,
			HSTSPreload:    true,
		},

Isolate your pages from other windows

Cross-Origin-Opener-Policy: same-origin cuts the link between your pages and windows from other sites that open them, or that they open. It's off by default, because the same link is how a sign-in popup, such as an OAuth provider's, hands its result back to your page. Turn it on when your App opens no such popups:

config/http.go
		Security: tinka.SecurityConfig{
			OpenerPolicy: "same-origin",
		},

Set your own Content Security Policy

A full Content Security Policy lists where your pages may load scripts, styles and images from, so a script an attacker injects doesn't run. Only you know what your pages load, so the App sets none beyond frame-ancestors. Set yours in middleware, and include frame-ancestors in it, because your header replaces the App's:

app/middleware/csp.go
package middleware

import "github.com/mucolabs/tinka"

// ContentSecurityPolicy lets pages load scripts, styles and images from
// the App itself only.
func ContentSecurityPolicy(c *tinka.Ctx, next tinka.Next) error {
	c.Header().Set("Content-Security-Policy", "default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'")
	return next(c)
}

The App's own HTML error pages carry a strict policy of their own, which allows their one style and nothing else.

See also: Encryption, Middleware, Request, App reference