From d1b053f3ba4170021c511b0d06a41405d3ab07d3 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 12:57:30 +0400 Subject: [PATCH 1/6] Webfinger: Add test showing wrong webfinger behavior --- .../webfinger/graf-imposter-webfinger.json | 41 +++++++++++++++++++ test/pleroma/web/web_finger_test.exs | 15 +++++++ 2 files changed, 56 insertions(+) create mode 100644 test/fixtures/webfinger/graf-imposter-webfinger.json diff --git a/test/fixtures/webfinger/graf-imposter-webfinger.json b/test/fixtures/webfinger/graf-imposter-webfinger.json new file mode 100644 index 000000000..e7010f606 --- /dev/null +++ b/test/fixtures/webfinger/graf-imposter-webfinger.json @@ -0,0 +1,41 @@ +{ + "subject": "acct:graf@poa.st", + "aliases": [ + "https://fba.ryona.agenc/webfingertest" + ], + "links": [ + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", + "href": "https://fba.ryona.agenc/webfingertest" + }, + { + "rel": "self", + "type": "application/activity+json", + "href": "https://fba.ryona.agenc/webfingertest" + }, + { + "rel": "http://ostatus.org/schema/1.0/subscribe", + "template": "https://fba.ryona.agenc/contact/follow?url={uri}" + }, + { + "rel": "http://schemas.google.com/g/2010#updates-from", + "type": "application/atom+xml", + "href": "" + }, + { + "rel": "salmon", + "href": "https://fba.ryona.agenc/salmon/friendica" + }, + { + "rel": "http://microformats.org/profile/hcard", + "type": "text/html", + "href": "https://fba.ryona.agenc/hcard/friendica" + }, + { + "rel": "http://joindiaspora.com/seed_location", + "type": "text/html", + "href": "https://fba.ryona.agenc" + } + ] +} diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index be5e08776..6530fbc56 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -204,4 +204,19 @@ test "refuses to process XML remote entities" do assert :error = WebFinger.finger("pekorino@pawoo.net") 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!() + + Tesla.Mock.json(fake_webfinger) + + %{url: "https://fba.ryona.agency/.well-known/host-meta"} -> + {:ok, %Tesla.Env{status: 404}} + end) + + refute {:ok, _} = WebFinger.finger("graf@fba.ryona.agency") + end end From b15f8b06425edbfc3a7cef2a55c609b12ee14377 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 23 Aug 2023 13:10:19 -0500 Subject: [PATCH 2/6] Prevent webfinger spoofing --- lib/pleroma/web/web_finger.ex | 16 ++++++++ .../tesla_mock/gleasonator.com_host_meta | 4 ++ test/fixtures/tesla_mock/webfinger_spoof.json | 28 ++++++++++++++ test/pleroma/web/web_finger_test.exs | 38 +++++++++++-------- 4 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/tesla_mock/gleasonator.com_host_meta create mode 100644 test/fixtures/tesla_mock/webfinger_spoof.json diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 26fb8af84..a84a4351b 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -216,10 +216,26 @@ def finger(account) do _ -> {:error, {:content_type, nil}} end + |> case do + {:ok, data} -> validate_webfinger(address, data) + error -> error + end else error -> Logger.debug("Couldn't finger #{account}: #{inspect(error)}") error end end + + defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do + with %URI{host: request_host} <- URI.parse(url), + [_name, acct_host] <- String.split(acct, "@"), + {_, true} <- {:hosts_match, acct_host == request_host} do + {:ok, data} + else + _ -> {:error, {:webfinger_invalid, url, data}} + end + end + + defp validate_webfinger(url, data), do: {:error, {:webfinger_invalid, url, data}} end diff --git a/test/fixtures/tesla_mock/gleasonator.com_host_meta b/test/fixtures/tesla_mock/gleasonator.com_host_meta new file mode 100644 index 000000000..c1a432519 --- /dev/null +++ b/test/fixtures/tesla_mock/gleasonator.com_host_meta @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/test/fixtures/tesla_mock/webfinger_spoof.json b/test/fixtures/tesla_mock/webfinger_spoof.json new file mode 100644 index 000000000..7c2a11f69 --- /dev/null +++ b/test/fixtures/tesla_mock/webfinger_spoof.json @@ -0,0 +1,28 @@ +{ + "aliases": [ + "https://gleasonator.com/users/alex", + "https://mitra.social/users/alex" + ], + "links": [ + { + "href": "https://gleasonator.com/users/alex", + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html" + }, + { + "href": "https://gleasonator.com/users/alex", + "rel": "self", + "type": "application/activity+json" + }, + { + "href": "https://gleasonator.com/users/alex", + "rel": "self", + "type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + }, + { + "rel": "http://ostatus.org/schema/1.0/subscribe", + "template": "https://gleasonator.com/ostatus_subscribe?acct={uri}" + } + ], + "subject": "acct:trump@whitehouse.gov" +} diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index 6530fbc56..84a8e19d5 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -76,15 +76,6 @@ test "returns the ActivityPub actor URI for an ActivityPub user" do {:ok, _data} = WebFinger.finger(user) end - test "returns the ActivityPub actor URI and subscribe address for an ActivityPub user with the ld+json mimetype" do - user = "kaniini@gerzilla.de" - - {:ok, data} = WebFinger.finger(user) - - assert data["ap_id"] == "https://gerzilla.de/channel/kaniini" - assert data["subscribe_address"] == "https://gerzilla.de/follow?f=&url={uri}" - end - test "it work for AP-only user" do user = "kpherox@mstdn.jp" @@ -99,12 +90,6 @@ test "it work for AP-only user" do assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}" end - test "it works for friendica" do - user = "lain@squeet.me" - - {:ok, _data} = WebFinger.finger(user) - end - test "it gets the xrd endpoint" do {:ok, template} = WebFinger.find_lrdd_template("social.heldscal.la") @@ -203,6 +188,29 @@ test "refuses to process XML remote entities" do assert :error = WebFinger.finger("pekorino@pawoo.net") end + + test "prevents spoofing" do + Tesla.Mock.mock(fn + %{ + url: "https://gleasonator.com/.well-known/webfinger?resource=acct:alex@gleasonator.com" + } -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/webfinger_spoof.json"), + headers: [{"content-type", "application/jrd+json"}] + }} + + %{url: "https://gleasonator.com/.well-known/host-meta"} -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/gleasonator.com_host_meta") + }} + end) + + {:error, _data} = WebFinger.finger("alex@gleasonator.com") + end end test "prevents forgeries" do From 206ea92837f8016d66a2b87f7f7338d814735a92 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 12:59:10 +0400 Subject: [PATCH 3/6] Webfinger: Fix test --- test/pleroma/web/web_finger_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index 84a8e19d5..8a550a6ba 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -213,6 +213,7 @@ test "prevents spoofing" do end end + @tag capture_log: true test "prevents forgeries" do Tesla.Mock.mock(fn %{url: "https://fba.ryona.agency/.well-known/webfinger?resource=acct:graf@fba.ryona.agency"} -> @@ -225,6 +226,6 @@ test "prevents forgeries" do {:ok, %Tesla.Env{status: 404}} end) - refute {:ok, _} = WebFinger.finger("graf@fba.ryona.agency") + assert {:error, _} = WebFinger.finger("graf@fba.ryona.agency") end end From 4491e8c9a3e2cdeb1b8e9cb98015dc1d0435c65c Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 13:01:23 +0400 Subject: [PATCH 4/6] Add changelog --- changelog.d/fix-webfinger-spoofing.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/fix-webfinger-spoofing.fix diff --git a/changelog.d/fix-webfinger-spoofing.fix b/changelog.d/fix-webfinger-spoofing.fix new file mode 100644 index 000000000..7b3c9490a --- /dev/null +++ b/changelog.d/fix-webfinger-spoofing.fix @@ -0,0 +1 @@ +Fix webfinger spoofing. From 91c93ce3cd62a916c7d367979473f94e36cf1873 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 13:14:59 +0400 Subject: [PATCH 5/6] Changelog: Adjust changelog type --- ...fix-webfinger-spoofing.fix => fix-webfinger-spoofing.security} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{fix-webfinger-spoofing.fix => fix-webfinger-spoofing.security} (100%) diff --git a/changelog.d/fix-webfinger-spoofing.fix b/changelog.d/fix-webfinger-spoofing.security similarity index 100% rename from changelog.d/fix-webfinger-spoofing.fix rename to changelog.d/fix-webfinger-spoofing.security From 84bb854056e406d5235dd442c28127891a8a8a86 Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Wed, 22 May 2024 15:12:29 +0400 Subject: [PATCH 6/6] Webfinger: Allow managing account for subdomain --- lib/pleroma/web/web_finger.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index a84a4351b..e149d9247 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -230,7 +230,7 @@ def finger(account) do defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do with %URI{host: request_host} <- URI.parse(url), [_name, acct_host] <- String.split(acct, "@"), - {_, true} <- {:hosts_match, acct_host == request_host} do + {_, true} <- {:hosts_match_or_subdomain, String.ends_with?(request_host, acct_host)} do {:ok, data} else _ -> {:error, {:webfinger_invalid, url, data}}