fix: restrict WebSocket CSP to request scheme

This commit is contained in:
SimpleTest 2026-07-20 21:28:30 +03:00
parent b8e122056e
commit 9b51d0807b
2 changed files with 23 additions and 11 deletions

View File

@ -9,15 +9,14 @@ defmodule WhoNeedHelpWeb.SecurityHeaders do
|> Application.fetch_env!(:map_tile_url) |> Application.fetch_env!(:map_tile_url)
|> origin() |> origin()
websocket_origins = websocket_origin =
[ case conn.scheme do
origin(%URI{scheme: "ws", host: conn.host, port: conn.port}), :http -> origin(%URI{scheme: "ws", host: conn.host, port: conn.port})
origin(%URI{scheme: "wss", host: conn.host, port: default_https_port(conn)}) :https -> origin(%URI{scheme: "wss", host: conn.host, port: conn.port})
] _other -> nil
|> Enum.reject(&is_nil/1) end
|> Enum.uniq()
connect_sources = sources(["'self'", tile_origin | websocket_origins]) connect_sources = sources(["'self'", tile_origin, websocket_origin])
image_sources = sources(["'self'", "data:", "blob:", tile_origin]) image_sources = sources(["'self'", "data:", "blob:", tile_origin])
policy = policy =
@ -39,9 +38,6 @@ defmodule WhoNeedHelpWeb.SecurityHeaders do
put_resp_header(conn, "content-security-policy", policy) put_resp_header(conn, "content-security-policy", policy)
end end
defp default_https_port(%Plug.Conn{port: 443}), do: 443
defp default_https_port(_conn), do: nil
defp sources(values), defp sources(values),
do: values |> Enum.reject(&is_nil/1) |> Enum.uniq() |> Enum.join(" ") do: values |> Enum.reject(&is_nil/1) |> Enum.uniq() |> Enum.join(" ")

View File

@ -13,6 +13,8 @@ defmodule WhoNeedHelpWeb.PageControllerTest do
refute content_security_policy =~ "script-src 'self' 'unsafe-inline'" refute content_security_policy =~ "script-src 'self' 'unsafe-inline'"
assert content_security_policy =~ "frame-ancestors 'none'" assert content_security_policy =~ "frame-ancestors 'none'"
assert content_security_policy =~ "https://tile.openstreetmap.org" assert content_security_policy =~ "https://tile.openstreetmap.org"
assert content_security_policy =~ "ws://www.example.com"
refute content_security_policy =~ "wss://www.example.com"
assert get_resp_header(conn, "permissions-policy") == [ assert get_resp_header(conn, "permissions-policy") == [
"geolocation=(self), camera=(), microphone=(), payment=(), usb=()" "geolocation=(self), camera=(), microphone=(), payment=(), usb=()"
@ -38,6 +40,20 @@ defmodule WhoNeedHelpWeb.PageControllerTest do
) == 1 ) == 1
end end
test "HTTPS content security policy permits only secure WebSockets", %{conn: conn} do
conn =
conn
|> Map.put(:scheme, :https)
|> Map.put(:host, "help.example")
|> Map.put(:port, 443)
|> WhoNeedHelpWeb.SecurityHeaders.put_content_security_policy([])
[content_security_policy] = get_resp_header(conn, "content-security-policy")
assert content_security_policy =~ "wss://help.example"
refute content_security_policy =~ "ws://help.example"
end
test "Phoenix logs filter authentication and handover secrets" do test "Phoenix logs filter authentication and handover secrets" do
assert Phoenix.Logger.filter_values(%{ assert Phoenix.Logger.filter_values(%{
"password" => "not-logged", "password" => "not-logged",