HTTP Security plug: rewrite &csp_string/0

- Directives are now separated with ";" instead of " ;",
according to https://www.w3.org/TR/CSP2/#policy-parsing
the space is optional
- Use an IO list, which at the end gets converted to a binary as
opposed to ++ing a bunch of arrays with binaries together and joining
them to a string. I doubt it gives any significant real world advantage,
but the code is cleaner and now I can sleep at night.
- The static part of csp is pre-joined to a single binary at compile time.
Same reasoning as the last point.
This commit is contained in:
rinpatch 2020-05-27 20:27:30 +03:00
parent 8f6d428880
commit 455a402c8a
2 changed files with 31 additions and 23 deletions

View file

@ -31,7 +31,7 @@ defp headers do
{"x-content-type-options", "nosniff"}, {"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy}, {"referrer-policy", referrer_policy},
{"x-download-options", "noopen"}, {"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"} {"content-security-policy", csp_string()}
] ]
if report_uri do if report_uri do
@ -43,23 +43,35 @@ defp headers do
] ]
} }
headers ++ [{"reply-to", Jason.encode!(report_group)}] [{"reply-to", Jason.encode!(report_group)} | headers]
else else
headers headers
end end
end end
@csp_start [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'"
]
|> Enum.join(";")
|> Kernel.<>(";")
|> List.wrap()
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]) report_uri = Config.get([:http_security, :report_uri])
connect_src = "connect-src 'self' #{static_url} #{websocket_url}" connect_src = ["connect-src 'self' ", static_url, ?\s, websocket_url]
connect_src = connect_src =
if Pleroma.Config.get(:env) == :dev do if Pleroma.Config.get(:env) == :dev do
connect_src <> " http://localhost:3035/" [connect_src," http://localhost:3035/"]
else else
connect_src connect_src
end end
@ -71,27 +83,23 @@ defp csp_string do
"script-src 'self'" "script-src 'self'"
end end
main_part = [ report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
"default-src 'none'", insecure = if scheme == "https", do: "upgrade-insecure-requests"
"base-uri 'self'",
"frame-ancestors 'none'",
"img-src 'self' data: blob: https:",
"media-src 'self' https:",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'",
connect_src,
script_src
]
report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: [] @csp_start
|> add_csp_param("img-src 'self' data: blob: https:")
insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: [] |> add_csp_param("media-src 'self' https:")
|> add_csp_param(connect_src)
(main_part ++ report ++ insecure) |> add_csp_param(script_src)
|> Enum.join("; ") |> add_csp_param(insecure)
|> add_csp_param(report)
|> :erlang.iolist_to_binary()
end end
defp add_csp_param(csp_iodata, nil), do: csp_iodata
defp add_csp_param(csp_iodata, param), do: [[param, ?;] | csp_iodata]
def warn_if_disabled do def warn_if_disabled do
unless Config.get([:http_security, :enabled]) do unless Config.get([:http_security, :enabled]) do
Logger.warn(" Logger.warn("

View file

@ -67,7 +67,7 @@ test "it sends `report-to` & `report-uri` CSP response headers" do
[csp] = Conn.get_resp_header(conn, "content-security-policy") [csp] = Conn.get_resp_header(conn, "content-security-policy")
assert csp =~ ~r|report-uri https://endpoint.com; report-to csp-endpoint;| assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;|
[reply_to] = Conn.get_resp_header(conn, "reply-to") [reply_to] = Conn.get_resp_header(conn, "reply-to")