fix(federation): enforce the signature policy on incoming requests
HTTPSignaturePlug checks the policy before the cryptographic check. A signed GET that fails it counts as unsigned; the inbox answers 401 to a POST that fails it instead of queueing a failed-signature retry, which only re-checks the signature. signature_policy: :log only logs. Based on Akkoma's HTTP signature hardening.
This commit is contained in:
parent
f57e97bf18
commit
e5481b4c28
7 changed files with 329 additions and 59 deletions
|
|
@ -386,18 +386,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
|
||||
if unsupported_http_message_signature?(conn) do
|
||||
reject_unsupported_http_message_signature(conn)
|
||||
else
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: conn.method,
|
||||
req_headers: conn.req_headers,
|
||||
request_path: conn.request_path,
|
||||
params: params,
|
||||
query_string: conn.query_string
|
||||
})
|
||||
cond do
|
||||
unsupported_http_message_signature?(conn) ->
|
||||
reject_unsupported_http_message_signature(conn)
|
||||
|
||||
json(conn, "ok")
|
||||
reason = conn.assigns[:signature_error] ->
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> json("error, invalid HTTP signature (#{reason})")
|
||||
|
||||
true ->
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: conn.method,
|
||||
req_headers: conn.req_headers,
|
||||
request_path: conn.request_path,
|
||||
params: params,
|
||||
query_string: conn.query_string
|
||||
})
|
||||
|
||||
json(conn, "ok")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -45,20 +45,52 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
|||
|
||||
defp maybe_assign_valid_signature(conn) do
|
||||
if has_signature_header?(conn) do
|
||||
# we replace the digest header with the one we computed in DigestPlug
|
||||
conn =
|
||||
case conn do
|
||||
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
|
||||
conn -> conn
|
||||
end
|
||||
conn = put_computed_digest(conn)
|
||||
|
||||
assign(conn, :valid_signature, Signature.validate_signature(conn))
|
||||
case check_signature_policy(conn) do
|
||||
:ok ->
|
||||
assign(conn, :valid_signature, Signature.validate_signature(conn))
|
||||
|
||||
{:error, reason} ->
|
||||
reject_signature(conn, reason)
|
||||
end
|
||||
else
|
||||
Logger.debug("No signature header!")
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
# we replace the digest header with the one we computed in DigestPlug
|
||||
defp put_computed_digest(%{assigns: %{digest: digest}} = conn),
|
||||
do: put_req_header(conn, "digest", digest)
|
||||
|
||||
defp put_computed_digest(conn), do: conn
|
||||
|
||||
defp check_signature_policy(conn) do
|
||||
with {:error, reason} <- Signature.check_signature_policy(conn) do
|
||||
if Pleroma.Config.get([:activitypub, :signature_policy], :enforce) == :log do
|
||||
Logger.warning(
|
||||
"HTTP signature policy violation (#{reason}) for #{conn.method} #{conn.request_path}, allowed by config"
|
||||
)
|
||||
|
||||
:ok
|
||||
else
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The request is treated as badly signed. The reason is kept so the inbox
|
||||
# can refuse it instead of queueing a failed-signature retry, which only
|
||||
# checks the cryptographic signature.
|
||||
defp reject_signature(conn, reason) do
|
||||
Logger.debug("Rejecting HTTP signature: #{reason}")
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> assign(:signature_error, reason)
|
||||
end
|
||||
|
||||
defp maybe_assign_actor_id(%{assigns: %{valid_signature: true}} = conn) do
|
||||
adapter = Application.get_env(:http_signatures, :adapter)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue