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

96

我对编写Rails和API都很新手,需要一些有关S3存储方案的帮助。以下是我的问题:

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

用户有两种与S3交互的方式...要么留给服务器处理,要么获取服务器发放的临时S3令牌(不确定可能性)并让用户直接访问S3上的内容URL。我找到了这篇谈论方法的问题,但它非常过时(2年前):从iPhone应用和S3上传照片的架构和设计问题

所以问题是:

  • 是否有办法在发放临时令牌时限制用户仅访问S3上的某些内容?如何做到这一点?假设有... 100,000或更多用户。
  • 让iOS设备直接获取这些内容是个好主意吗?
  • 还是应该让服务器控制所有传递的内容(当然可以解决安全问题)?这是否意味着在将其传递给连接的用户之前必须将所有内容都下载到服务器上?
  • 如果您了解Rails...能否使用paperclip和aws-sdk gem来实现这种设置?

对于提出多个问题,我表示抱歉,并感谢任何有关此问题的见解。谢谢 :)


1
我发现这个链接并想为其他寻找相关信息的人留言:http://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURL.html - dibble
2个回答

114
使用 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中的特定对象。
Amazon的API文档: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth

3
谢谢@ejdyksen。我想出的解决方案正是这样做的(我没有更新问题,而是回答了它)! 因此,我的解决方案是为GET请求使用经过身份验证的URL。 但是,当用户贡献内容时,他使用联合IAM令牌(临时凭据)将资源创建到特定的/bucket/user/objectname位置,并附加策略以允许/bucket/user/*写入访问权限。 因此,系统中的任何用户都无法对其他用户的内容造成伤害。看起来效果还不错。感谢您的回答。 - dineth
5
如果你正在使用aws-sdk-ruby的v2版本,请注意方法与之前有所不同: http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method - vijucat
2
用户能看到我的访问密钥,这不是一个风险吗? 还有转换后的秘密密钥。 - user2503775
3
@Dennis,重点是文件不必上传到您的服务器。该链接不包含您的AWS凭证。它可能包含ACCESS_KEY_ID(我没有从头记住),但这并不打算保密。 - ejdyksen
2
@ejdyksen 你说得对,我刚刚验证了一下,URL只包含 AWS_ACCESS_KEY_ID。我最初以为 AWS_SECRET_ACCESS_KEY 也会显示出来,但实际上并没有。 - Dennis
显示剩余4条评论

47

上面的回答使用了旧版 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)

您的对象密钥是指向您文件路径的位置。如果您需要查找该位置,可以使用以下方式:

where your_object_key 是指向您文件路径的位置。如果您需要查找该位置,您会使用类似下面的命令:

s3 = Aws::S3::Client::new
keys = []
s3.list_objects(bucket: 'your_bucket', prefix: 'your_path').contents.each { |e| 
  keys << e.key
}
那些信息极其难以挖掘,我差点放弃并使用旧的 gem。
参考资料: http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method

1
expires_in 期望以秒为单位的数据格式,请确保首先进行转换。 - frillybob
参数错误(期望 :expires_in 是秒数)。 - BinaryButterfly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接