61254111e5
The (request-target) used by Pleroma is non-standard, but many HTTP signature implementations do it this way due to a misinterpretation of the draft 06 of HTTP signatures: "path" was interpreted as not having the query, though later examples show that it must be the absolute path with the query part of the URL as well. This behavior is kept to make sure most software (Pleroma itself, Mastodon, and probably others) do not break, but Pleroma now accepts signatures for a (request-target) containing the query, as expected by many HTTP signature libraries, and clarified in the draft 11 of HTTP signatures. Additionally, the new draft renamed (request-target) to @request-target. We now support both for incoming requests' signatures.
102 lines
3.2 KiB
Elixir
102 lines
3.2 KiB
Elixir
# Pleroma: A lightweight social networking server
|
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
|
import Plug.Conn
|
|
import Phoenix.Controller, only: [get_format: 1, text: 2]
|
|
require Logger
|
|
|
|
def init(options) do
|
|
options
|
|
end
|
|
|
|
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
|
conn
|
|
end
|
|
|
|
def call(conn, _opts) do
|
|
if get_format(conn) == "activity+json" do
|
|
conn
|
|
|> maybe_assign_valid_signature()
|
|
|> maybe_require_signature()
|
|
else
|
|
conn
|
|
end
|
|
end
|
|
|
|
defp validate_signature(conn, request_target) do
|
|
# Newer drafts for HTTP signatures now use @request-target instead of the
|
|
# old (request-target). We'll now support both for incoming signatures.
|
|
conn =
|
|
conn
|
|
|> put_req_header("(request-target)", request_target)
|
|
|> put_req_header("@request-target", request_target)
|
|
|
|
HTTPSignatures.validate_conn(conn)
|
|
end
|
|
|
|
defp validate_signature(conn) do
|
|
# This (request-target) is non-standard, but many implementations do it
|
|
# this way due to a misinterpretation of
|
|
# https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06
|
|
# "path" was interpreted as not having the query, though later examples
|
|
# show that it must be the absolute path + query. This behavior is kept to
|
|
# make sure most software (Pleroma itself, Mastodon, and probably others)
|
|
# do not break.
|
|
request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
|
|
|
# This is the proper way to build the @request-target, as expected by
|
|
# many HTTP signature libraries, clarified in the following draft:
|
|
# https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6
|
|
# It is the same as before, but containing the query part as well.
|
|
proper_target = request_target <> "?#{conn.query_string}"
|
|
|
|
cond do
|
|
# Normal, non-standard behavior but expected by Pleroma and more.
|
|
validate_signature(conn, request_target) ->
|
|
true
|
|
|
|
# Has query string and the previous one failed: let's try the standard.
|
|
conn.query_string != "" ->
|
|
validate_signature(conn, proper_target)
|
|
|
|
# If there's no query string and signature fails, it's rotten.
|
|
true ->
|
|
false
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
assign(conn, :valid_signature, validate_signature(conn))
|
|
else
|
|
Logger.debug("No signature header!")
|
|
conn
|
|
end
|
|
end
|
|
|
|
defp has_signature_header?(conn) do
|
|
conn |> get_req_header("signature") |> Enum.at(0, false)
|
|
end
|
|
|
|
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
|
|
|
|
defp maybe_require_signature(conn) do
|
|
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> text("Request not signed")
|
|
|> halt()
|
|
else
|
|
conn
|
|
end
|
|
end
|
|
end
|