Discuss: Signed Fetches and Redirects #731
Labels
No labels
approved, awaiting change
bug
configuration
documentation
duplicate
enhancement
extremely low priority
feature request
Fix it yourself
help wanted
invalid
mastodon_api
needs docs
needs tests
not a bug
planned
pleroma_api
privacy
question
static_fe
triage
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: AkkomaGang/akkoma#731
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When we fetch AP objects we sign the initial request¹ using the URL we’re fetching from. (At least when
:activitypub, :sign_object_fetches
is enabled, but i presume no production server disables it.) But before we actually receive the object we may follow redirects which change the URL, yet we never change the request signature. This means we can end up fetching with a signature not actually valid for the final URL.An example of such redirects are indeed our very own status display URLs
https://instance.example/notice/CreateActivityID
which first redirect to the canonical location of the not e objecthttps://instance.example/objects/some-unrelated-uid
. In theory other server implementations or alternative frontends may also set up redirects.In practice we can easily see fetching other Akkoma display URLs does in fact work, but it’s not because we somehow resign after all, but because when verifying the signature, we currently generate a list of all known aliases and accept if the signature is valid for any of them. Other implementations may also not be lenient about signatures for aliases.
def route_aliases(%{path_info: ["objects", id], query_string: query_string}) do
ap_id = url(~p[/objects/#{id}])
with %Activity{} = activity <- Activity.get_by_object_ap_id_with_object(ap_id) do
[~p"/notice/#{activity.id}", "/notice/#{activity.id}?#{query_string}"]
else
_ -> []
end
end
This seems a bit iffy to me and constitutes a coupling between front- and backend since only aliases known to the backend can be used in frontends as display URLs. It also makes verification more costly since we need to query the database for the related Create activity.
In an ideal setup, we’d both resign our fetches on redirects and not ponder any aliases during verification.
In practice stopping to permit any aliases during verification comes with some struggles though.
Variants with and without query parameters are both considered due to a disagreement between spec and Mastodon’s implementation practice on whether signatures should include them. Mastodon never includes them while spec requires inclusion and historically GTS strictly adhered to spec. Now, GTS will attempt both versions preferring the variant with query parameters included.
Furthermore, I’m told Misskey and Mastodon currently also never resign their fetches on redirects. Presumably this is why they directly serve AP objects under their display URL instead of redirecting to its canonical location. With “directly serve AP” being their workaround for a missing resign instead of our “accept signatures for any alias”.
Unless we’re willing to break lookup of Akkoma posts by search on Mastodon and Misskey-derivatives, we probably also can’t drop aliases unrelated to query-parameters.
Leaving resigning our own requests on redirects as the only currently viable improvement. If noone else is known to do redirects and we need to keep accepting aliases anyway though, this calls into question whether this is actually useful (apart from a faint hope of future Mastodon/Misskey improvements and an agreement wrt query parameters)
Welp, I titled this “Discuss” but there isn’t really any open points to discuss and it turned more into a documentation/lamenting of why things are the way they are.
If you have any further insight, strong opinions or know of other implementations which either employs redirects or resign its requests on encountering redirects this might change things though (if nothing else to at least motivate resigning our own requests)