fix: restrict WebSocket CSP to request scheme
This commit is contained in:
parent
b8e122056e
commit
9b51d0807b
|
|
@ -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(" ")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user