Macaw.Plug.SecurityHeaders (macaw_plug v0.1.4)

Copy Markdown View Source

A strict Content-Security-Policy and the standard security headers.

plug Macaw.Plug.SecurityHeaders

The default policy is closed: everything from the app's own origin, nothing inline, nothing evaluated, nothing framed:

default-src 'self'; script-src 'self'; style-src 'self';
img-src 'self' data:; font-src 'self'; connect-src 'self';
object-src 'none'; base-uri 'none'; form-action 'self';
frame-ancestors 'none'

A host changes a directive only by naming it, csp: [img_src: "'self'"], so every loosening is visible in the host's code.

A host's own inline script is allowed by its SHA-256 hash, never by 'unsafe-inline'. The policy is written just before the response is sent, so a controller that renders such a script adds its hash on the way:

conn |> Macaw.Plug.SecurityHeaders.put_script_hash(script_body)

Also set: x-content-type-options: nosniff, referrer-policy: strict-origin-when-cross-origin, x-frame-options: DENY (for browsers that predate frame-ancestors), cross-origin-opener-policy: same-origin, a permissions-policy that turns off camera, microphone, geolocation, payment and USB, and over https strict-transport-security: max-age=31536000. Each can be replaced with headers: %{"name" => value}, or removed with nil. This replaces Phoenix's put_secure_browser_headers.

Summary

Functions

A script body's CSP hash source, 'sha256-…'.

Allows an inline script on this response by its SHA-256 hash. Takes the script's body (the text between <script> and </script>), exactly.

Functions

hash(body)

@spec hash(String.t()) :: String.t()

A script body's CSP hash source, 'sha256-…'.

policy(directives \\ [default_src: "'self'", script_src: "'self'", style_src: "'self'", img_src: "'self' data:", font_src: "'self'", connect_src: "'self'", object_src: "'none'", base_uri: "'none'", form_action: "'self'", frame_ancestors: "'none'"], hashes \\ [])

@spec policy(keyword(String.t()), [String.t()]) :: String.t()

The policy string for directives (the defaults, or as init/1 merged them) and script hashes, which are added to script-src.

put_script_hash(conn, body)

@spec put_script_hash(Plug.Conn.t(), String.t()) :: Plug.Conn.t()

Allows an inline script on this response by its SHA-256 hash. Takes the script's body (the text between <script> and </script>), exactly.