安全上传

在二月发布的 Discourse 2.4 版本 中新增了“安全上传”功能,该功能为 Discourse 实例内的所有上传文件(图片、视频、音频、文本、PDF、ZIP 等)提供更高程度的安全性。

前提条件

您的网站必须已启用 S3 上传,并且需要填写以下设置:

  • S3 访问密钥 ID (Access Key ID)
  • S3 秘密访问密钥 (Secret Access Key)
  • S3 区域 (Region)
  • S3 上传存储桶 (Upload Bucket)

此外,您必须使用一个没有公共存储桶策略的 S3 存储桶,并确保所有现有上传文件的 S3 ACL 均为 public-read。请参阅下方的“启用安全上传”部分。

满足这些前提条件后,您可以启用“安全上传”站点设置。

启用安全上传

:dragon: :warning: 此处有龙 (HERE BE DRAGONS) :warning: :dragon:

这是一个高级功能,除企业版 (Enterprise tier) 外,我们的支持将非常有限。仅当您是专家用户时才启用安全上传。


要启用安全上传,请遵循以下步骤:

  1. 确保已配置 S3 上传。
  2. 确认您的 S3 存储桶是否具有公共存储桶策略。如果是,则需要额外的步骤(步骤 4)。
  3. 运行 uploads:sync_s3_acls rake 任务。这将确保您所有的上传文件在 S3 中具有正确的 ACL。这非常重要;如果您在步骤 4 之前执行此操作,某些上传文件可能会在您的论坛上无法访问。
  4. 如果步骤 1 中存在公共存储桶策略,请将其从存储桶中移除。
  5. 启用“安全上传”站点设置。可选地启用“阻止匿名用户下载文件”站点设置,以阻止匿名用户从公开帖子中下载附件。从此时起的所有上传,根据以下条件可能会被标记为安全。
  6. 如果您希望所有历史上传文件都被分析并可能标记为安全,请运行 uploads:secure_upload_analyse_and_update rake 任务。

:exclamation: 关于 S3 存储桶策略的说明 :exclamation:

您需要确保正在上传的存储桶具有公共存储桶策略。公共存储桶策略的内容类似于:

{
    "Version": "2012-10-17",
    "Id": "ComputedBucketPolicy",
    "Statement": [
        {
            "Sid": "AllowWorldRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::your-bucket-name/*"
        }
    ]
}

这里的关键部分是允许 * 执行 GetObject 操作,这意味着允许任何人下载存储桶中的任何内容。如果策略是公共的,也会显示此标签:

此处不应修改这些设置。下图展示了“阻止公共访问”选项卡的理想状态:

功能说明

启用安全上传后,通过编辑器 (Composer) 上传的任何文件都将被标记为安全或不安全,具体取决于以下标准:

  • 如果您启用了“需要登录”站点设置,所有上传文件都将被标记为安全,匿名用户将无法访问。
  • 如果您在私人消息 (Personal Message) 中上传内容,该内容将被标记为安全。
  • 如果您在属于私有分类 (Category) 的主题 (Topic) 中上传内容,该内容将被标记为安全。

S3 上的上传文件将具有私有 ACL,因此直接链接到 S3 上的文件将返回 403 访问被拒绝错误。对安全上传的所有访问都将通过 S3 预签名 URL 进行。这对用户是隐藏的;如果上传是安全的,任何对它的引用都将通过 Discourse 的 /secure-uploads/meta/ URL 进行。

权限和访问控制

/secure-uploads/meta/ URL 将确定当前用户是否被允许访问该媒体,并在允许时提供访问。创建上传时,该上传首次出现的帖子将被设置为“访问控制帖子”,所有权限都将基于该帖子。

  • 如果您启用了“需要登录”站点设置,匿名用户访问该 URL 时始终会收到 404 错误。
  • 如果访问的媒体其访问控制帖子是私人消息,则用户必须是该私人消息主题的一部分才能访问该媒体,否则用户将收到 403 错误。
  • 如果访问的媒体其访问控制帖子位于私有分类的主题内,则用户必须有权访问该分类才能访问该媒体,否则用户将收到 403 错误。

在不同的帖子和主题之间复制 /secure-uploads/meta/ URL 是不明智的,因为不同用户在您的 Discourse 论坛中拥有不同的访问级别。新上传应始终通过编辑器 (Composer) 创建。Oneboxes 和热链接图片也将遵守安全上传规则。站点设置上传、表情符号和主题上传不受安全上传影响,因为它们必须是公开的。

:warning: 如果删除了访问控制帖子,附加的上传文件将无法再被访问:warning:

移动带有安全上传的帖子

如果您将“访问控制帖子”在不同的安全上下文之间移动,则附加的上传文件可能会变为安全或不安全。以下情况可能会更改上传文件的安全性:

  • 更改主题分类。将遍历主题中的所有帖子,并相应地更新上传文件的安全状态。
  • 将主题在公开主题和私人消息之间切换。将执行与上述相同的操作。
  • 将帖子从主题移动到新主题或现有其他主题。将在目标主题上执行与上述相同的操作。

邮件中的安全上传

默认情况下,在邮件中嵌入安全图片已启用。您可以配置以下站点设置以进行更细粒度的控制:

  • secure_uploads_allow_embed_images_in_emails:禁用此项以在邮件中屏蔽安全图片。
  • secure_uploads_max_email_embed_image_size_kb:嵌入安全图片的大小上限,默认为 1MB,以防止邮件过大。最大值为 10MB。与 email_total_attachment_size_limit_kb 配合使用。

安全图片将作为邮件附件添加,并使用 cid: URL 格式嵌入,因为电子邮件客户端对 base64 URL 的支持仍然不稳定。

如果您未启用 secure_uploads_allow_embed_images_in_emails,或者图片开始超过大小限制,那么您将看到以下内容来代替安全图片(同样适用于未嵌入的安全音频和视频):

image

托管客户

目前,安全上传功能仅对我们的企业版 (Enterprise plan) 客户开放。如需更多详情,请 联系我们

51 个赞

关于此功能,@martin 应该添加大量警告,因为这是一个 :warning: 高级功能,不适合初学者,而且在我们企业版之外,我们对其提供的支持是有限的。请自带相关专业知识。

9 个赞

该安全机制从未设计用于覆盖头像,这不是我们计划中的使用场景。

13 个赞

建议:将 S3 存储桶设置为“阻止所有公共访问”是不正确的

@genachka @AntiMetaman @Hugh_Roberts @znedw @Thamer

我将修改原始帖子。在与我们的基础设施团队成员 @schleifer 沟通后,我确认之前建议启用“阻止所有公共访问”设置是错误的。请为您的 S3 存储桶关闭该设置,然后运行 uploads:sync_s3_acls 以确保 ACL 正确,之后再次尝试自定义头像。

此外,每个人都需要确保您正在上传的目标存储桶没有公共存储桶策略。公共存储桶策略通常如下所示:

{
    "Version": "2012-10-17",
    "Id": "ComputedBucketPolicy",
    "Statement": [
        {
            "Sid": "AllowWorldRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::your-bucket-name/*"
        }
    ]
}

这里的关键是我们允许 * 执行 GetObject 操作,这意味着允许任何人下载存储桶中的任何内容。如果策略是公开的,还会显示以下标签:

对此我深表歉意。@AntiMetaman@schleifer 和我都无法复现此错误:

如果您能提供有关您的 S3/AWS 设置的更多信息,将会非常有帮助。

7 个赞

@martin 谢谢。我的问题不是那个错误,而是匿名用户无法访问页面。如果我将上传存储桶设置为私有,那个错误就不再出现,我也能正常上传了。我的网站并没有开启“需要登录”的选项。

我一开始就没有为存储桶设置启用“阻止所有公共访问”。如果你告诉我匿名用户仍然应该能够访问主题、阅读内容并查看受保护的图片,那我可以再试一次。如果为媒体添加安全性会导致匿名用户无法查看这些图片,那我宁愿只保护附件。

如果有帮助的话,我使用的是 BackBlaze B2 搭配 BunnyCDN。目前我的上传存储桶设置是公开的:

4 个赞

是的,这基本上就是核心要点。任何需要私有的内容都会设置私有 ACL,除非使用预签名 URL,否则无法访问。请注意,存储桶策略不是公开的。是的,所有“阻止公开访问”的设置都应该取消勾选。如果您想了解我们如何判断上传是否安全,所有相关规则都在这里:discourse/lib/upload_security.rb at main · discourse/discourse · GitHub 以及这里:discourse/app/models/post.rb at main · discourse/discourse · GitHub

抱歉,我不熟悉 BackBlaze。我不确定图中显示的设置如何转换为存储桶策略。基本上,您不希望存储桶策略设置为“公开”,也不希望开启“阻止所有公开访问”。这样我们才能正确设置私有和公开 ACL。如果您没有设置“需要登录”,那么在非私信或非私密类别的话题中进行的任何上传都应该是公开的,并可供匿名用户访问。在匿名用户可访问的话题中,图片不应被设置为安全(私有)。

5 个赞

安全媒体实现与 Backblaze 不兼容。他们是否支持预签名 URL?

1 个赞

@riking

是的,支持预签名 URL:https://help.backblaze.com/hc/en-us/articles/360047815993-Does-the-B2-S3-Compatible-API-support-Pre-Signed-URLs-

@martin

是的,将存储桶从公开更改为私有即可实现此功能。我这样操作后可以上传,但作为匿名用户我无法查看主题。

4 个赞

@martin 感谢您的澄清。我可以确认,将 S3 设置保持为我截图中所示的“公开”,并且所有选项均未勾选(正如您所建议的),自定义头像/个人资料图片现在可以正常工作,同时受保护的帖子上传功能也依然正常。谢谢!

4 个赞

我本周合并了这个 PR,并将这些细节添加到原帖中:


如果您希望在电子邮件中嵌入安全图片,可以配置以下站点设置:

  • secure_media_allow_embed_images_in_emails:如果启用,我们将在电子邮件中嵌入安全图片,而不是将其屏蔽。
  • secure_media_max_email_embed_image_size_kb:嵌入的安全图片大小上限,默认为 1MB,以防止电子邮件过大。最大值为 10MB。此设置与 email_total_attachment_size_limit_kb 配合使用。

安全图片将作为电子邮件附件添加,并使用 cid: URL 格式嵌入,因为电子邮件客户端对 base64 URL 的支持尚不稳定。

如果您未启用 secure_media_allow_embed_images_in_emails,或者图片大小超过限制,您将看到以下内容替代安全图片(同样适用于未嵌入的安全音频和视频):

image

10 个赞

这是我之前帖子的补充说明;来自此 PR:

我们现在默认启用安全的媒体图片嵌入功能。

5 个赞

在按照 OP 指南设置安全媒体上传后,一切运行正常(附件、图片等),但非主题附件的上传(如站点标志、个人资料头像)会在 Discourse 中弹出“访问被拒绝”的提示。
我是否在某些设置上出了问题?

请阅读该主题,您的问题在上文几条帖子中已得到解答。

3 个赞

我已仔细阅读了所有内容。请具体说明您上面提到的几条帖子是什么意思,我没有看到这个 issue。
编辑:
我试着解读一下言外之意,发现这个帖子以前更长,但其中一些关键回复被删除了。我看到了一些对不存在的回复的引用,以及提到了帖子中并未出现的截图。
不过,如果我的理解正确,建议是将 S3 存储桶设置为阻止任何公共访问。我只想确认这一点,并询问这样做是否安全?

1 个赞

正确。

原帖确实已删除,但问题和解决方案依然存在。

6 个赞

我注意到在使用安全媒体时存在一个 bug,详见 https://meta.discourse.org/t/knowledge-base-plugin/115288
知识库中的附件链接无法打开(会重定向到 404 页面),除非强制在新窗口中打开。
更奇怪的是,同一个附件可以从与知识库条目关联的 Discourse 主题中正常打开。

编辑:
当有人将附件链接从一个回复复制到另一个回复时,也会出现同样的 bug。链接本身完全相同,但只有在原始回复中才能正常打开,除非强制在新窗口中打开。

1 个赞

如果不使用 S3,如何实现安全媒体?
目前看来,只要拥有上传文件的直接链接,即可无需登录下载该文件。这似乎存在安全问题……

安全媒体必须使用 S3。
开发整个功能的目的就是为了杜绝直接链接的分享和访问。

11 个赞

这是一个非常出色的功能。感谢你们开发它。MinIO 作为 S3 的免费即插即用替代品,不支持 ACL,而且将来也不会支持。他们的理由很有道理:ACL 并非实用功能,且会因需要额外的写入操作而降低性能。安全媒体上传在 Discourse 中可以正常工作——uploads:secure_upload_analyse_and_update 任务在最后一步会报错,但似乎可以忽略该错误。不过,Discourse 是否有任何理由要调用 ACL 呢?

3 个赞

是的,因为根据 OP 中的设置,S3 存储桶是私有的,任何非安全上传都需要将其 ACL 设置为公开,而安全上传将具有私有 ACL,以便可以直接访问 S3 URL。我认为我们目前没有计划修改此工作方式;我认为若要完全避免在 S3 替换中使用 ACL,将需要大量的工作。

7 个赞