5

我有一个 Rails 6 应用程序,注册用户(所有者)可以在 S3 上上传文件 -图像/视频,然后所有者可以向其他用户提供访问权限(邀请)以查看他们上传的内容。

有没有办法可以限制文件访问,以便只有所有者才能下载他上传的文件(图像/视频),从而对其他非所有者/受邀用户施加限制。. 视频/图像不应仅通过右键单击并轻松保存/下载它们来下载。

注意- 上传的文件还包括大型视频(mp4 和 HLS 流媒体),因此其他受邀用户可以查看它们但不能下载它,除非他们是所有者/上传者,因为文件来自 AWS Cloudfront 的视频和 S3,如果他们是图像。

关联的设置如下 -

User has one role
User has many images/videos, each residing in his own folder on s3(`bucket/user_id/image_slug/` or `bucket/user_id/video_slug/`)
User has many invitations(must be view only access to owners file)

不确定,什么是正确的方法,可以是 -

  • 如果文件被非所有者访问,则更新文件的 ACL 并使其只读?
  • 将所有上传的文件设为公开并禁用非所有者的公开访问,但这也将限制对文件的任何直接访问。

让我知道最适合这种方法的逻辑是什么。

4

2 回答 2

2

您要实现的目标需要在多个层面上打下基础:

  1. 根据S3 安全最佳实践,您应该在 S3 端将权限级别保持在最低限度,以便应用程序提供预期的行为。

  2. S3 允许您授予对用户特定文件夹的访问权限

  3. 您应该查看授予访问权限的 gem以涵盖服务器端限制。您还应该查看客户端限制。一种常见的技术是禁用鼠标右键单击。

有关的:

如何在不显示 S3 URls 的情况下下载文件

视频流媒体平台的 7 大安全功能

Netflix 如何保护其内容

于 2022-01-08T22:25:29.297 回答
1

一个可能的选择是为您的 s3 对象生成签名的 url,并在您的 rails 应用程序中具有“授权”逻辑。这应该是 ActiveStorage 的默认设置,否则如果您使用 Carrierwave,则需要设置 fog_public = false

选项1:

在显示下载文件按钮的视图中:

- if user.can_access_document?(document)
  = link_to 'View', document.attachment_url, target: :blank
- else
  Request an invite from the owner

在您的用户模型中:

class User < ApplicationRecord
  has_many :invitations 
  
  ...  

  def can_access_document?(document)
    # Check if there is an invitation entry for this user to access said file
    self.invitations.where(document_id: document.id).any?
  end
end

选项 2

您还可以让按钮点击特定端点/路由到您的一个控制器,例如Files#download_file,然后在那里进行签入并重定向到 s3 文件。

class FilesController < ApplicationController
  def download
    document = Document.find(params[:id])

    if current_user.can_access_document?(document)
      redirect_to document_url 
    else
      raise "You don't have access to this document"
    end 
  end
end

在您看来,只需将链接指向您的新控制器

= link_to 'View', download_files_url(document), target: :blank

由于文件的 url 已签名,用户将需要单击您网站上的按钮,并且不能一遍又一遍地使用相同的 url。这些签名的 url 也可以有一个动态的过期时间,例如,如果链接设置为 60 秒后过期,如果用户在 60 秒后点击下载文件,他/她将显示一个 s3 错误,说明链接已过期,如下所示:

<Error>
    <Code>AccessDenied</Code>
    <Message>Request has expired</Message>
    <Expires>2018-06-28T07:13:14Z</Expires>
    <ServerTime>2018-08-06T20:03:02Z</ServerTime>
    <RequestId>87E1D2CFAAA7F9A6</RequestId>
    <HostId>
    A9BEluTV2hk3ltdFkixvQFa/yUBfUSgDjptwphKze+jXR6tYbpHCx8Z7y6WTfxu3rS4cGk5/WTQ=
    </HostId>
</Error>
于 2022-01-09T21:27:58.823 回答