diff --git a/CHANGELOG.md b/CHANGELOG.md index 9033050a1..1108c60e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## Unreleased + +## Added + +## Changed + +## Fixed +- Critical security issue allowing Akkoma to be used as a vector for + (depending on configuration) impersonation of other users or creation + of bogus users and posts on the upload domain + ## 2024.02 ## Added diff --git a/config/config.exs b/config/config.exs index 1c531344c..723d173ec 100644 --- a/config/config.exs +++ b/config/config.exs @@ -65,7 +65,8 @@ link_name: false, proxy_remote: false, filename_display_max_length: 30, - base_url: nil + base_url: nil, + allowed_mime_types: ["image", "audio", "video"] config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" diff --git a/config/description.exs b/config/description.exs index e108aaae8..3ddd874f8 100644 --- a/config/description.exs +++ b/config/description.exs @@ -105,6 +105,19 @@ "https://cdn-host.com" ] }, + %{ + key: :allowed_mime_types, + label: "Allowed MIME types", + type: {:list, :string}, + description: + "List of MIME (main) types uploads are allowed to identify themselves with. Other types may still be uploaded, but will identify as a generic binary to clients. WARNING: Loosening this over the defaults can lead to security issues. Removing types is safe, but only add to the list if you are sure you know what you are doing.", + suggestions: [ + "image", + "audio", + "video", + "font" + ] + }, %{ key: :proxy_remote, type: :boolean, diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex index 300c33068..c0982b4af 100644 --- a/lib/pleroma/web/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -28,7 +28,9 @@ def init(_opts) do |> Keyword.put(:at, "/__unconfigured_media_plug") |> Plug.Static.init() - %{static_plug_opts: static_plug_opts} + allowed_mime_types = Pleroma.Config.get([Pleroma.Upload, :allowed_mime_types]) + + %{static_plug_opts: static_plug_opts, allowed_mime_types: allowed_mime_types} end def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do @@ -68,13 +70,28 @@ defp media_is_banned(_, {:url, url}), do: MediaProxy.in_banned_urls(url) defp media_is_banned(_, _), do: false + defp get_safe_mime_type(%{allowed_mime_types: allowed_mime_types} = _opts, mime) do + [maintype | _] = String.split(mime, "/", parts: 2) + if maintype in allowed_mime_types, do: mime, else: "application/octet-stream" + end + + defp set_content_type(conn, opts, filepath) do + real_mime = MIME.from_path(filepath) + clean_mime = get_safe_mime_type(opts, real_mime) + put_resp_header(conn, "content-type", clean_mime) + end + defp get_media(conn, {:static_dir, directory}, opts) do static_opts = Map.get(opts, :static_plug_opts) |> Map.put(:at, [@path]) |> Map.put(:from, directory) + |> Map.put(:set_content_type, false) - conn = Plug.Static.call(conn, static_opts) + conn = + conn + |> set_content_type(opts, conn.request_path) + |> Pleroma.Web.Plugs.StaticNoCT.call(static_opts) if conn.halted do conn