Security headers
This guide covers the security headers a Tinka App sends. You will learn how to:
- Know what every response carries, and why
- Let a page be shown in a frame, by your App or a partner
- Keep browsers on HTTPS with HSTS, and extend it to subdomains
- Isolate your pages from the windows that open them
- Set your own Content Security Policy
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:
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:
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:
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:
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