如何在 S3 中存储数据并允许用户使用 rails API / iOS 客户端以安全的方式访问?

Posted

技术标签:

【中文标题】如何在 S3 中存储数据并允许用户使用 rails API / iOS 客户端以安全的方式访问?【英文标题】:How to store data in S3 and allow user access in a secure way with rails API / iOS client? 【发布时间】:2012-06-04 09:03:15 【问题描述】:

我是编写 Rails 和 API 的新手。我需要一些有关 S3 存储解决方案的帮助。这是我的问题。

我正在为 ios 应用程序编写一个 API,用户在 iOS 上使用 Facebook API 登录。服务器根据 Facebook 向 iOS 用户发出的令牌验证用户,并发出临时会话令牌。从这一点开始,用户需要下载存储在 S3 中的内容。此内容仅属于用户及其朋友的子集。该用户可以向 S3 添加更多内容,这些内容可以被同一群人访问。我想这类似于将文件附加到 Facebook 群组...

用户可以通过 2 种方式与 S3 交互...将其留给服务器或让服务器发出临时 S3 令牌(不确定此处的可能性),用户可以直接点击内容 URL到 S3。我发现这个问题是关于方法的,但是,它确实过时了(2 年前):Architectural and design question about uploading photos from iPhone app and S3

所以问题:

在颁发临时令牌时,是否有办法限制用户仅访问 S3 上的某些内容?我怎样才能做到这一点?假设有...比如说 100,000 或更多用户。 让 iOS 设备直接拉出这些内容是个好主意吗? 还是应该让服务器控制所有内容的传递(这当然解决了安全问题)?这是否意味着我必须先将所有内容下载到服务器,然后再将其传递给连接的用户? 如果您了解 rails...我可以使用回形针和 aws-sdk gem 来实现这种设置吗?

对多个问题深表歉意,感谢您对问题的任何见解。谢谢:)

【问题讨论】:

找到了这个并认为我会为其他寻找docs.aws.amazon.com/AmazonS3/latest/dev/…的人发表评论 【参考方案1】:

使用aws-sdk gem,您可以通过调用url_for获取任何S3对象的临时签名url:

s3 = AWS::S3.new(
  :access_key_id => 1234,
  :secret_access_key => abcd
)
object = s3.buckets['bucket'].objects['path/to/object']
object.url_for(:get,  :expires => 20.minutes.from_now, :secure => true ).to_s

这将为您提供一个仅用于 S3 中该对象的已签名临时使用 URL。它会在 20 分钟后过期(在本例中),它只对那个对象有效。

如果您有很多客户需要的对象,您将需要发布大量签名 URL。

或者应该让服务器控制所有内容传递(这当然解决了安全问题)?这是否意味着我必须先将所有内容下载到服务器,然后再将其传递给连接的用户?

请注意,这并不意味着服务器需要下载每个对象,它只需要验证并授权特定客户端访问 S3 中的特定对象。

来自亚马逊的 API 文档: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth

【讨论】:

感谢@ejdyksen。我设计的解决方案正是使用的(我没有用我的答案更新问题)!所以我的解决方案是为 GET 请求做经过身份验证的 URL。但是,当用户贡献内容时,他会使用联合 IAM 令牌(过期的临时凭证)将资源创建到特定的 /bucket/user/objectname 位置,并附加策略以允许 /bucket/user/* 写入访问。因此系统中的任何用户都不能损害其他用户的内容。似乎工作正常。感谢您的回答。 如果您使用的是 v2 的 aws-sdk-ruby,请注意方法有些不同:docs.aws.amazon.com/sdkforruby/api/Aws/S3/… 用户可以看到我的访问密钥不是有风险吗?还有秘钥(转换后的) @Dennis 重点是文件不必访问您的服务器。该链接不包含您的 AWS 凭证。它可能包含ACCESS_KEY_ID(我不记得我的头顶),但这不是秘密。 @ejdyksen 你是对的,我刚刚验证了 URL 只包含 AWS_ACCESS_KEY_ID。我原本以为AWS_SECRET_ACCESS_KEY也显示了,但事实并非如此。【参考方案2】:

以上答案使用旧的 aws-sdk-v1 gem 而不是新的 aws-sdk-resources 版本 2。

新方法是:

aws_resource = Aws::S3::Resource::new
aws_resource.bucket('your_bucket').object('your_object_key').presigned_url(:get, expires_in: 1*20.minutes)

your_object_key 是文件的路径。如果你需要查看它,你可以使用类似的东西:

s3 = Aws::S3::Client::new
keys = []
s3.list_objects(bucket: 'your_bucket', prefix: 'your_path').contents.each  |e| 
  keys << e.key

这些信息非常难以挖掘,我几乎只是放弃并使用了较旧的宝石。

参考

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method

【讨论】:

expires_in 需要以秒为单位的数据,请务必先转换。 "ArgumentError(预期 :expires_in 为秒数)"

以上是关于如何在 S3 中存储数据并允许用户使用 rails API / iOS 客户端以安全的方式访问?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取 Rails 应用程序的 CORS 标头以访问 aws s3 存储桶?

如何让用户直接把图片上传到对象存储 S3

Rails ActiveStorage 附加到现有 S3 文件

如何允许 cognito 身份验证的用户获得对 s3 存储桶的公共访问权限

如何在 Rails 5.x.x 应用程序中以 Zip 格式从 S3 下载多个文件?

Rails 6主动存储sidekiq文件在S3上上传背景图片