add report uri and report to

This commit is contained in:
Alex S 2019-05-16 12:49:40 +07:00
parent 62516be9c4
commit aa11fa4864
5 changed files with 107 additions and 65 deletions

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `fetch_initial_posts` option - Configuration: `fetch_initial_posts` option
- Configuration: `notify_email` option - Configuration: `notify_email` option
- Configuration: Media proxy `whitelist` option - Configuration: Media proxy `whitelist` option
- Configuration: `report_uri` option
- Pleroma API: User subscriptions - Pleroma API: User subscriptions
- Pleroma API: Healthcheck endpoint - Pleroma API: Healthcheck endpoint
- Admin API: Endpoints for listing/revoking invite tokens - Admin API: Endpoints for listing/revoking invite tokens

View file

@ -61,6 +61,8 @@
config :pleroma, :app_account_creation, max_requests: 5 config :pleroma, :app_account_creation, max_requests: 5
config :pleroma, :http_security, report_uri: "https://endpoint.com"
try do try do
import_config "test.secret.exs" import_config "test.secret.exs"
rescue rescue

View file

@ -286,7 +286,8 @@ This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls start
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header * ``sts``: Whether to additionally send a `Strict-Transport-Security` header
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent * ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent
* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent * ``ct_max_age``: The maximum age for the `Expect-CT` header if sent
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`. * ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`
* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.
## :mrf_user_allowlist ## :mrf_user_allowlist

View file

@ -20,8 +20,9 @@ def call(conn, _options) do
defp headers do defp headers do
referrer_policy = Config.get([:http_security, :referrer_policy]) referrer_policy = Config.get([:http_security, :referrer_policy])
report_uri = Config.get([:http_security, :report_uri])
[ headers = [
{"x-xss-protection", "1; mode=block"}, {"x-xss-protection", "1; mode=block"},
{"x-permitted-cross-domain-policies", "none"}, {"x-permitted-cross-domain-policies", "none"},
{"x-frame-options", "DENY"}, {"x-frame-options", "DENY"},
@ -30,12 +31,27 @@ defp headers do
{"x-download-options", "noopen"}, {"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"} {"content-security-policy", csp_string() <> ";"}
] ]
if report_uri do
report_group = %{
"group" => "csp-endpoint",
"max-age" => 10_886_400,
"endpoints" => [
%{"url" => report_uri}
]
}
headers ++ [{"reply-to", Jason.encode!(report_group)}]
else
headers
end
end end
defp csp_string do defp csp_string do
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
static_url = Pleroma.Web.Endpoint.static_url() static_url = Pleroma.Web.Endpoint.static_url()
websocket_url = Pleroma.Web.Endpoint.websocket_url() websocket_url = Pleroma.Web.Endpoint.websocket_url()
report_uri = Config.get([:http_security, :report_uri])
connect_src = "connect-src 'self' #{static_url} #{websocket_url}" connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
@ -53,7 +69,7 @@ defp csp_string do
"script-src 'self'" "script-src 'self'"
end end
[ main_part = [
"default-src 'none'", "default-src 'none'",
"base-uri 'self'", "base-uri 'self'",
"frame-ancestors 'none'", "frame-ancestors 'none'",
@ -63,11 +79,14 @@ defp csp_string do
"font-src 'self'", "font-src 'self'",
"manifest-src 'self'", "manifest-src 'self'",
connect_src, connect_src,
script_src, script_src
if scheme == "https" do
"upgrade-insecure-requests"
end
] ]
report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: []
insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: []
(main_part ++ report ++ insecure)
|> Enum.join("; ") |> Enum.join("; ")
end end

View file

@ -7,28 +7,89 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
alias Pleroma.Config alias Pleroma.Config
alias Plug.Conn alias Plug.Conn
test "it sends CSP headers when enabled", %{conn: conn} do describe "http security enabled" do
Config.put([:http_security, :enabled], true) setup do
enabled = Config.get([:http_securiy, :enabled])
conn = Config.put([:http_security, :enabled], true)
conn
|> get("/api/v1/instance")
refute Conn.get_resp_header(conn, "x-xss-protection") == [] on_exit(fn ->
refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] Config.put([:http_security, :enabled], enabled)
refute Conn.get_resp_header(conn, "x-frame-options") == [] end)
refute Conn.get_resp_header(conn, "x-content-type-options") == []
refute Conn.get_resp_header(conn, "x-download-options") == [] :ok
refute Conn.get_resp_header(conn, "referrer-policy") == [] end
refute Conn.get_resp_header(conn, "content-security-policy") == []
test "it sends CSP headers when enabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "x-xss-protection") == []
refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
refute Conn.get_resp_header(conn, "x-frame-options") == []
refute Conn.get_resp_header(conn, "x-content-type-options") == []
refute Conn.get_resp_header(conn, "x-download-options") == []
refute Conn.get_resp_header(conn, "referrer-policy") == []
refute Conn.get_resp_header(conn, "content-security-policy") == []
end
test "it sends STS headers when enabled", %{conn: conn} do
Config.put([:http_security, :sts], true)
conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == []
refute Conn.get_resp_header(conn, "expect-ct") == []
end
test "it does not send STS headers when disabled", %{conn: conn} do
Config.put([:http_security, :sts], false)
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == []
assert Conn.get_resp_header(conn, "expect-ct") == []
end
test "referrer-policy header reflects configured value", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "referrer-policy") == ["same-origin"]
Config.put([:http_security, :referrer_policy], "no-referrer")
conn =
build_conn()
|> get("/api/v1/instance")
assert Conn.get_resp_header(conn, "referrer-policy") == ["no-referrer"]
end
test "it sends `report-to` & `report-uri` CSP response headers" do
conn =
build_conn()
|> get("/api/v1/instance")
[csp] = Conn.get_resp_header(conn, "content-security-policy")
assert csp =~ ~r|report-uri https://endpoint.com; report-to csp-endpoint;|
[reply_to] = Conn.get_resp_header(conn, "reply-to")
assert reply_to ==
"{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}"
end
end end
test "it does not send CSP headers when disabled", %{conn: conn} do test "it does not send CSP headers when disabled", %{conn: conn} do
enabled = Config.get([:http_securiy, :enabled])
Config.put([:http_security, :enabled], false) Config.put([:http_security, :enabled], false)
conn = on_exit(fn ->
conn Config.put([:http_security, :enabled], enabled)
|> get("/api/v1/instance") end)
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "x-xss-protection") == [] assert Conn.get_resp_header(conn, "x-xss-protection") == []
assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
@ -38,46 +99,4 @@ test "it does not send CSP headers when disabled", %{conn: conn} do
assert Conn.get_resp_header(conn, "referrer-policy") == [] assert Conn.get_resp_header(conn, "referrer-policy") == []
assert Conn.get_resp_header(conn, "content-security-policy") == [] assert Conn.get_resp_header(conn, "content-security-policy") == []
end end
test "it sends STS headers when enabled", %{conn: conn} do
Config.put([:http_security, :enabled], true)
Config.put([:http_security, :sts], true)
conn =
conn
|> get("/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == []
refute Conn.get_resp_header(conn, "expect-ct") == []
end
test "it does not send STS headers when disabled", %{conn: conn} do
Config.put([:http_security, :enabled], true)
Config.put([:http_security, :sts], false)
conn =
conn
|> get("/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == []
assert Conn.get_resp_header(conn, "expect-ct") == []
end
test "referrer-policy header reflects configured value", %{conn: conn} do
Config.put([:http_security, :enabled], true)
conn =
conn
|> get("/api/v1/instance")
assert Conn.get_resp_header(conn, "referrer-policy") == ["same-origin"]
Config.put([:http_security, :referrer_policy], "no-referrer")
conn =
build_conn()
|> get("/api/v1/instance")
assert Conn.get_resp_header(conn, "referrer-policy") == ["no-referrer"]
end
end end