Merge branch 'webfinger-actual-fix' into 'develop'
Fix WebFinger for split-domain setups See merge request pleroma/pleroma!4405
This commit is contained in:
commit
d19b992417
4 changed files with 142 additions and 32 deletions
1
changelog.d/webfinger-actual-fix.fix
Normal file
1
changelog.d/webfinger-actual-fix.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix WebFinger for split-domain setups
|
||||
|
|
@ -195,7 +195,9 @@ defmodule Pleroma.Web.WebFinger do
|
|||
defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain}
|
||||
|
||||
@spec finger(String.t()) :: {:ok, map()} | {:error, any()}
|
||||
def finger(account) do
|
||||
def finger(account), do: do_finger(account, true)
|
||||
|
||||
defp do_finger(account, follow_redirects) do
|
||||
account = String.trim_leading(account, "@")
|
||||
|
||||
domain =
|
||||
|
|
@ -229,8 +231,15 @@ defmodule Pleroma.Web.WebFinger do
|
|||
{:error, {:content_type, nil}}
|
||||
end
|
||||
|> case do
|
||||
{:ok, data} -> validate_webfinger(address, data)
|
||||
error -> error
|
||||
{:ok, data} ->
|
||||
if follow_redirects do
|
||||
validate_webfinger(address, data)
|
||||
else
|
||||
{:ok, data}
|
||||
end
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
else
|
||||
error ->
|
||||
|
|
@ -241,10 +250,8 @@ defmodule Pleroma.Web.WebFinger do
|
|||
|
||||
defp validate_webfinger(request_url, %{"subject" => "acct:" <> acct = subject} = data) do
|
||||
with [_name, acct_host] <- String.split(acct, "@"),
|
||||
{_, url} <- {:address, get_address_from_domain(acct_host, subject)},
|
||||
%URI{host: request_host} <- URI.parse(request_url),
|
||||
%URI{host: acct_host} <- URI.parse(url),
|
||||
{_, true} <- {:hosts_match, acct_host == request_host} do
|
||||
{_, resolved_url} <- {:address, get_address_from_domain(acct_host, subject)},
|
||||
{_, true} <- {:url_match, resolved_webfinger_matches?(request_url, resolved_url, data)} do
|
||||
{:ok, data}
|
||||
else
|
||||
_ -> {:error, {:webfinger_invalid, request_url, data}}
|
||||
|
|
@ -252,4 +259,29 @@ defmodule Pleroma.Web.WebFinger do
|
|||
end
|
||||
|
||||
defp validate_webfinger(url, data), do: {:error, {:webfinger_invalid, url, data}}
|
||||
|
||||
defp resolved_webfinger_matches?(request_url, resolved_url, _data)
|
||||
when request_url == resolved_url do
|
||||
true
|
||||
end
|
||||
|
||||
defp resolved_webfinger_matches?(
|
||||
_request_url,
|
||||
_resolved_url,
|
||||
%{"subject" => "acct:" <> acct} = data
|
||||
) do
|
||||
with {:ok, %{"subject" => "acct:" <> new_acct} = new_data} <- do_finger(acct, false),
|
||||
true <- acct == new_acct,
|
||||
true <- webfinger_data_matches?(data, new_data) do
|
||||
true
|
||||
else
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp webfinger_data_matches?(%{"ap_id" => ap_id}, %{"ap_id" => ap_id}) when ap_id != "" do
|
||||
true
|
||||
end
|
||||
|
||||
defp webfinger_data_matches?(_data, _new_data), do: false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Web.WebFingerTest do
|
|||
{:ok, _data} = WebFinger.finger(user)
|
||||
end
|
||||
|
||||
test "it work for AP-only user" do
|
||||
test "it works for AP-only user" do
|
||||
user = "kpherox@mstdn.jp"
|
||||
|
||||
{:ok, data} = WebFinger.finger(user)
|
||||
|
|
@ -224,24 +224,84 @@ defmodule Pleroma.Web.WebFingerTest do
|
|||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/gleasonator.com_host_meta")
|
||||
}}
|
||||
|
||||
%{url: "https://whitehouse.gov/.well-known/webfinger?resource=acct:trump@whitehouse.gov"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
end)
|
||||
|
||||
{:error, _data} = WebFinger.finger("alex@gleasonator.com")
|
||||
end
|
||||
end
|
||||
|
||||
test "prevents forgeries" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://fba.ryona.agency/.well-known/webfinger?resource=acct:graf@fba.ryona.agency"} ->
|
||||
fake_webfinger =
|
||||
File.read!("test/fixtures/webfinger/graf-imposter-webfinger.json") |> Jason.decode!()
|
||||
test "prevents forgeries" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
url:
|
||||
"https://fba.ryona.agency/.well-known/webfinger?resource=acct:graf@fba.ryona.agency"
|
||||
} ->
|
||||
fake_webfinger =
|
||||
File.read!("test/fixtures/webfinger/graf-imposter-webfinger.json") |> Jason.decode!()
|
||||
|
||||
Tesla.Mock.json(fake_webfinger)
|
||||
Tesla.Mock.json(fake_webfinger)
|
||||
|
||||
%{url: "https://fba.ryona.agency/.well-known/host-meta"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
end)
|
||||
%{url: url}
|
||||
when url in [
|
||||
"https://poa.st/.well-known/webfinger?resource=acct:graf@poa.st",
|
||||
"https://fba.ryona.agency/.well-known/host-meta"
|
||||
] ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
end)
|
||||
|
||||
assert {:error, _} = WebFinger.finger("graf@fba.ryona.agency")
|
||||
assert {:error, _} = WebFinger.finger("graf@fba.ryona.agency")
|
||||
end
|
||||
|
||||
test "prevents forgeries even when the spoofed subject exists on the target domain" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url}
|
||||
when url in [
|
||||
"https://attacker.example/.well-known/host-meta",
|
||||
"https://victim.example/.well-known/host-meta"
|
||||
] ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
|
||||
%{
|
||||
url:
|
||||
"https://attacker.example/.well-known/webfinger?resource=acct:alice@attacker.example"
|
||||
} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:alice@victim.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => "https://attacker.example/users/alice"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
%{url: "https://victim.example/.well-known/webfinger?resource=acct:alice@victim.example"} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:alice@victim.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => "https://victim.example/users/alice"
|
||||
}
|
||||
]
|
||||
})
|
||||
end)
|
||||
|
||||
assert {:error, _} = WebFinger.finger("alice@attacker.example")
|
||||
end
|
||||
|
||||
test "works for correctly set up split-domain instances implementing host-meta redirect" do
|
||||
{:ok, _data} = WebFinger.finger("a@pleroma.example")
|
||||
{:ok, _data} = WebFinger.finger("a@sub.pleroma.example")
|
||||
end
|
||||
|
||||
test "works for correctly set up split-domain instances without host-meta redirect" do
|
||||
{:ok, _data} = WebFinger.finger("a@mastodon.example")
|
||||
{:ok, _data} = WebFinger.finger("a@sub.mastodon.example")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1229,7 +1229,8 @@ defmodule HttpRequestMock do
|
|||
{:ok, %Tesla.Env{status: 404, body: ""}}
|
||||
end
|
||||
|
||||
def get("https://mstdn.jp/.well-known/webfinger?resource=acct:kpherox@mstdn.jp", _, _, _) do
|
||||
def get("https://mstdn.jp/.well-known/webfinger?resource=acct:" <> acct, _, _, _)
|
||||
when acct in ["kpherox@mstdn.jp", "kPherox@mstdn.jp"] do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
|
|
@ -1526,14 +1527,6 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def get("https://mastodon.example/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 302,
|
||||
headers: [{"location", "https://sub.mastodon.example/.well-known/host-meta"}]
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://sub.mastodon.example/.well-known/host-meta", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
|
|
@ -1546,11 +1539,15 @@ defmodule HttpRequestMock do
|
|||
end
|
||||
|
||||
def get(
|
||||
"https://sub.mastodon.example/.well-known/webfinger?resource=acct:a@mastodon.example",
|
||||
url,
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
)
|
||||
when url in [
|
||||
"https://sub.mastodon.example/.well-known/webfinger?resource=acct:a@mastodon.example",
|
||||
"https://sub.mastodon.example/.well-known/webfinger?resource=acct:a@sub.mastodon.example"
|
||||
] do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
|
|
@ -1564,6 +1561,22 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://mastodon.example/.well-known/webfinger?resource=acct:a@mastodon.example",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 302,
|
||||
headers: [
|
||||
{"location",
|
||||
"https://sub.mastodon.example/.well-known/webfinger?resource=acct:a@mastodon.example"}
|
||||
]
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://sub.mastodon.example/users/a", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
|
|
@ -1609,11 +1622,15 @@ defmodule HttpRequestMock do
|
|||
end
|
||||
|
||||
def get(
|
||||
"https://sub.pleroma.example/.well-known/webfinger?resource=acct:a@pleroma.example",
|
||||
url,
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
)
|
||||
when url in [
|
||||
"https://sub.pleroma.example/.well-known/webfinger?resource=acct:a@pleroma.example",
|
||||
"https://sub.pleroma.example/.well-known/webfinger?resource=acct:a@sub.pleroma.example"
|
||||
] do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue