mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-03-13 07:02:41 +00:00
.
This commit is contained in:
parent
edcd816730
commit
b469b9d9d3
6 changed files with 83 additions and 3 deletions
1
changelog.d/content-type-sanitize.security
Normal file
1
changelog.d/content-type-sanitize.security
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix content-type spoofing vulnerability that could allow users to upload ActivityPub objects as attachments
|
|
@ -65,7 +65,8 @@ config :pleroma, Pleroma.Upload,
|
||||||
proxy_remote: false,
|
proxy_remote: false,
|
||||||
filename_display_max_length: 30,
|
filename_display_max_length: 30,
|
||||||
default_description: nil,
|
default_description: nil,
|
||||||
base_url: nil
|
base_url: nil,
|
||||||
|
allowed_mime_types: ["image", "audio", "video"]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,19 @@ config :pleroma, :config_description, [
|
||||||
key: :filename_display_max_length,
|
key: :filename_display_max_length,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
description: "Set max length of a filename to display. 0 = no limit. Default: 30"
|
description: "Set max length of a filename to display. 0 = no limit. Default: 30"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.Plugs.Utils
|
||||||
|
|
||||||
@behaviour Plug
|
@behaviour Plug
|
||||||
# no slashes
|
# no slashes
|
||||||
|
@ -28,7 +29,9 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
||||||
|> Keyword.put(:at, "/__unconfigured_media_plug")
|
|> Keyword.put(:at, "/__unconfigured_media_plug")
|
||||||
|> Plug.Static.init()
|
|> 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
|
end
|
||||||
|
|
||||||
def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
|
@ -69,13 +72,23 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
||||||
|
|
||||||
defp media_is_banned(_, _), do: false
|
defp media_is_banned(_, _), do: false
|
||||||
|
|
||||||
|
defp set_content_type(conn, opts, filepath) do
|
||||||
|
real_mime = MIME.from_path(filepath)
|
||||||
|
clean_mime = Utils.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
|
defp get_media(conn, {:static_dir, directory}, _, opts) do
|
||||||
static_opts =
|
static_opts =
|
||||||
Map.get(opts, :static_plug_opts)
|
Map.get(opts, :static_plug_opts)
|
||||||
|> Map.put(:at, [@path])
|
|> Map.put(:at, [@path])
|
||||||
|> Map.put(:from, directory)
|
|> Map.put(:from, directory)
|
||||||
|
|> Map.put(:content_type, false)
|
||||||
|
|
||||||
conn = Plug.Static.call(conn, static_opts)
|
conn =
|
||||||
|
conn
|
||||||
|
|> set_content_type(opts, conn.request_path)
|
||||||
|
|> Plug.Static.call(static_opts)
|
||||||
|
|
||||||
if conn.halted do
|
if conn.halted do
|
||||||
conn
|
conn
|
||||||
|
|
14
lib/pleroma/web/plugs/utils.ex
Normal file
14
lib/pleroma/web/plugs/utils.ex
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.Utils do
|
||||||
|
@moduledoc """
|
||||||
|
Some helper functions shared across several plugs
|
||||||
|
"""
|
||||||
|
|
||||||
|
def 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
|
||||||
|
end
|
38
test/pleroma/web/plugs/uploaded_media_test.exs
Normal file
38
test/pleroma/web/plugs/uploaded_media_test.exs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.UploadedMediaTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: false
|
||||||
|
|
||||||
|
alias Pleroma.StaticStubbedConfigMock
|
||||||
|
alias Pleroma.Web.Plugs.Utils
|
||||||
|
|
||||||
|
setup do
|
||||||
|
Mox.stub_with(StaticStubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||||
|
|
||||||
|
{:ok, %{}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "content-type sanitization with Utils.get_safe_mime_type/2" do
|
||||||
|
test "it allows safe MIME types" do
|
||||||
|
opts = %{allowed_mime_types: ["image", "audio", "video"]}
|
||||||
|
|
||||||
|
assert Utils.get_safe_mime_type(opts, "image/jpeg") == "image/jpeg"
|
||||||
|
assert Utils.get_safe_mime_type(opts, "audio/mpeg") == "audio/mpeg"
|
||||||
|
assert Utils.get_safe_mime_type(opts, "video/mp4") == "video/mp4"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it sanitizes potentially dangerous content-types" do
|
||||||
|
opts = %{allowed_mime_types: ["image", "audio", "video"]}
|
||||||
|
|
||||||
|
assert Utils.get_safe_mime_type(opts, "application/activity+json") ==
|
||||||
|
"application/octet-stream"
|
||||||
|
|
||||||
|
assert Utils.get_safe_mime_type(opts, "text/html") == "application/octet-stream"
|
||||||
|
|
||||||
|
assert Utils.get_safe_mime_type(opts, "application/javascript") ==
|
||||||
|
"application/octet-stream"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue