- Add organization membership check before API token creation - Fix admin security allowlist form reading current_user instead of current_scope.user - Use recursive File.ls instead of Path.wildcard to include hidden files in MIB validation - Add upload size check before ZIP extraction in MIB controller - Centralize CSP in SecurityHeaders plug with per-request nonces instead of 'unsafe-inline'
45 lines
1.4 KiB
Elixir
45 lines
1.4 KiB
Elixir
defmodule ToweropsWeb.Plugs.SecurityHeaders do
|
|
@moduledoc """
|
|
Adds security headers to all responses.
|
|
|
|
Headers added:
|
|
- Content-Security-Policy: Restricts resource loading to prevent XSS
|
|
- X-Frame-Options: Prevents clickjacking attacks
|
|
- X-Content-Type-Options: Prevents MIME type sniffing
|
|
- Referrer-Policy: Controls referrer information leakage
|
|
- Permissions-Policy: Disables browsing-topics API
|
|
"""
|
|
|
|
import Plug.Conn
|
|
|
|
@nonce_bytes 16
|
|
|
|
def init(opts), do: opts
|
|
|
|
def call(conn, _opts) do
|
|
nonce = @nonce_bytes |> :crypto.strong_rand_bytes() |> Base.encode64(padding: false)
|
|
|
|
conn
|
|
|> put_private(:csp_nonce, nonce)
|
|
|> put_resp_header("permissions-policy", "browsing-topics=()")
|
|
|> put_resp_header("content-security-policy", csp_header(nonce))
|
|
|> put_resp_header("x-frame-options", "DENY")
|
|
|> put_resp_header("x-content-type-options", "nosniff")
|
|
|> put_resp_header("referrer-policy", "strict-origin-when-cross-origin")
|
|
end
|
|
|
|
defp csp_header(nonce) do
|
|
Enum.join(
|
|
[
|
|
"default-src 'self' data:",
|
|
"script-src 'self' 'nonce-#{nonce}' https://a.w5isp.com",
|
|
"style-src 'self' 'unsafe-inline' 'unsafe-hashes'",
|
|
"img-src 'self' data: https:",
|
|
"font-src 'self' data:",
|
|
"connect-src 'self' ws: wss: https://a.w5isp.com https://*.tile.openstreetmap.org",
|
|
"frame-ancestors 'none'"
|
|
],
|
|
"; "
|
|
)
|
|
end
|
|
end
|