diff --git a/changelog.d/c2s-update-verify.fix b/changelog.d/c2s-update-verify.fix
new file mode 100644
index 000000000..a4dfe7c07
--- /dev/null
+++ b/changelog.d/c2s-update-verify.fix
@@ -0,0 +1 @@
+Verify a local Update sent through AP C2S so users can only update their own objects
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index a08eda5f4..7ac0bbab4 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -482,7 +482,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
         |> put_status(:forbidden)
         |> json(message)
 
-      {:error, message} ->
+      {:error, message} when is_binary(message) ->
         conn
         |> put_status(:bad_request)
         |> json(message)
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index 35774d410..c509890f6 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -169,7 +169,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
          meta = Keyword.put(meta, :object_data, object_data),
          {:ok, update_activity} <-
            update_activity
-           |> UpdateValidator.cast_and_validate()
+           |> UpdateValidator.cast_and_validate(meta)
            |> Ecto.Changeset.apply_action(:insert) do
       update_activity = stringify_keys(update_activity)
       {:ok, update_activity, meta}
@@ -177,7 +177,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
       {:local, _} ->
         with {:ok, object} <-
                update_activity
-               |> UpdateValidator.cast_and_validate()
+               |> UpdateValidator.cast_and_validate(meta)
                |> Ecto.Changeset.apply_action(:insert) do
           object = stringify_keys(object)
           {:ok, object, meta}
@@ -207,9 +207,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
         "Answer" -> AnswerValidator
       end
 
+    cast_func =
+      if type == "Update" do
+        fn o -> validator.cast_and_validate(o, meta) end
+      else
+        fn o -> validator.cast_and_validate(o) end
+      end
+
     with {:ok, object} <-
            object
-           |> validator.cast_and_validate()
+           |> cast_func.()
            |> Ecto.Changeset.apply_action(:insert) do
       object = stringify_keys(object)
       {:ok, object, meta}
diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
index 1e940a400..aab90235f 100644
--- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
@@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
   use Ecto.Schema
 
   alias Pleroma.EctoType.ActivityPub.ObjectValidators
+  alias Pleroma.Object
+  alias Pleroma.User
 
   import Ecto.Changeset
   import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@@ -31,23 +33,50 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
     |> cast(data, __schema__(:fields))
   end
 
-  defp validate_data(cng) do
+  defp validate_data(cng, meta) do
     cng
     |> validate_required([:id, :type, :actor, :to, :cc, :object])
     |> validate_inclusion(:type, ["Update"])
     |> validate_actor_presence()
-    |> validate_updating_rights()
+    |> validate_updating_rights(meta)
   end
 
-  def cast_and_validate(data) do
+  def cast_and_validate(data, meta \\ []) do
     data
     |> cast_data
-    |> validate_data
+    |> validate_data(meta)
   end
 
-  # For now we only support updating users, and here the rule is easy:
-  # object id == actor id
-  def validate_updating_rights(cng) do
+  def validate_updating_rights(cng, meta) do
+    if meta[:local] do
+      validate_updating_rights_local(cng)
+    else
+      validate_updating_rights_remote(cng)
+    end
+  end
+
+  # For local Updates, verify the actor can edit the object
+  def validate_updating_rights_local(cng) do
+    actor = get_field(cng, :actor)
+    updated_object = get_field(cng, :object)
+
+    if {:ok, actor} == ObjectValidators.ObjectID.cast(updated_object) do
+      cng
+    else
+      with %User{} = user <- User.get_cached_by_ap_id(actor),
+           {_, %Object{} = orig_object} <- {:object, Object.normalize(updated_object)},
+           :ok <- Object.authorize_access(orig_object, user) do
+        cng
+      else
+        _e ->
+          cng
+          |> add_error(:object, "Can't be updated by this actor")
+      end
+    end
+  end
+
+  # For remote Updates, verify the host is the same.
+  def validate_updating_rights_remote(cng) do
     with actor = get_field(cng, :actor),
          object = get_field(cng, :object),
          {:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index d4175b56f..b627478dc 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -1644,6 +1644,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert json_response(conn, 403)
     end
 
+    test "it rejects update activity of object from other actor", %{conn: conn} do
+      note_activity = insert(:note_activity)
+      note_object = Object.normalize(note_activity, fetch: false)
+      user = insert(:user)
+
+      data = %{
+        type: "Update",
+        object: %{
+          id: note_object.data["id"]
+        }
+      }
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/outbox", data)
+
+      assert json_response(conn, 400)
+      assert note_object == Object.normalize(note_activity, fetch: false)
+    end
+
     test "it increases like count when receiving a like action", %{conn: conn} do
       note_activity = insert(:note_activity)
       note_object = Object.normalize(note_activity, fetch: false)