Как хранить данные в S3 и разрешить пользователю безопасный доступ с помощью клиента rails API / iOS?

93

Я новичок в написании Rails и API. Мне нужна помощь с решением для хранения S3. Вот моя проблема.

Я пишу API для приложения iOS, где пользователи входят в систему с помощью API Facebook на iOS. Сервер проверяет пользователя по токену, который Facebook выдает пользователю iOS, и выдает временный токен сеанса. С этого момента пользователю необходимо загрузить контент, который хранится в S3. Этот контент принадлежит только пользователю и некоторой части его друзей. Этот пользователь может добавлять в S3 больше контента, доступ к которому может получить одна и та же группа людей. Я думаю, это похоже на прикрепление файла к группе Facebook ...

Есть два способа взаимодействия пользователя с S3 ... оставьте это на сервере или попросите сервер выдать временный токен S3 (не уверен в возможностях здесь), и пользователь может перейти по URL-адресам контента непосредственно к S3. Я обнаружил, что этот вопрос касается подходов, однако он действительно устарел (2 года назад): архитектурный и дизайнерский вопрос о загрузке фотографий из приложения для iPhone и S3.

Итак, вопросы:

  • Есть ли способ ограничить доступ пользователя только к некоторому контенту на S3 при выдаче временного токена? Как я могу это сделать? Предположим, есть ... скажем, 100 000 или более пользователей.
  • Разрешить устройству iOS извлекать этот контент напрямую?
  • Или следует позволить серверу контролировать передачу всего контента (это, конечно, решает проблему безопасности)? Означает ли это, что я должен загрузить весь контент на сервер, прежде чем передать его подключенным пользователям?
  • Если вы знаете рельсы ... могу ли я использовать скрепку и драгоценные камни aws-sdk для достижения такой настройки?

Приносим извинения за несколько вопросов, и я ценю любое понимание проблемы. Благодарность :)

Dineth
источник
1
нашел это и подумал, что прокомментирую других, ищущих docs.aws.amazon.com/AmazonS3/latest/dev/…
dibble

Ответы:

113

Используя гем aws-sdk , вы можете получить временный подписанный URL-адрес для любого объекта S3, вызвав url_for:

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

Это даст вам подписанный URL-адрес временного использования только для этого объекта в S3. Срок его действия истекает через 20 минут (в этом примере) и подходит только для этого одного объекта.

Если у вас есть много объектов, которые нужны клиенту, вам нужно будет выдать много подписанных URL-адресов.

Или следует позволить серверу контролировать передачу всего контента (это, конечно, решает проблему безопасности)? Означает ли это, что я должен загрузить весь контент на сервер, прежде чем передать его подключенным пользователям?

Обратите внимание, что это не означает, что сервер должен загружать каждый объект, ему нужно только аутентифицировать и авторизовать определенных клиентов для доступа к определенным объектам в S3.

Документы API от Amazon: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth

Ejdyksen
источник
3
Спасибо за это @ejdyksen. Решение, которое я разработал, использовало именно это (я не обновлял вопрос своим ответом)! Итак, моим решением было сделать аутентифицированные URL-адреса для запросов GET. Однако, когда пользователь вносит контент, он создает ресурсы в определенном месте / bucket / user / objectname, используя объединенный токен IAM (временные учетные данные, срок действия которых истекает) с присоединенной политикой, разрешающей / bucket / user / * доступ на запись. поэтому ни один пользователь в системе не может причинить вред контенту других пользователей. Вроде работает нормально. Оцените ваш ответ.
dineth 06
5
Если вы используете версию 2 для aws-sdk-ruby, обратите внимание, что методы несколько отличаются: docs.aws.amazon.com/sdkforruby/api/Aws/S3/…
vijucat
2
Разве это не риск, что пользователь увидит мой ключ доступа? Также есть секретный ключ (преобразованный)
user2503775 01
3
@Dennis, все дело в том, что файл не должен попадать на ваш сервер. Ссылка не содержит ваших учетных данных AWS. Он может содержать ACCESS_KEY_ID(не помню, с макушки), но это не должно быть секретом.
ejdyksen 09
2
@ejdyksen, вы правы, я только что проверил, что URL-адрес содержит только расширение AWS_ACCESS_KEY_ID. Сначала я думал, что AWS_SECRET_ACCESS_KEYэто тоже отображается, но это не так.
Деннис
47

В приведенных выше ответах используется старый гем aws-sdk-v1, а не новая версия 2 aws-sdk-resources.

Новый способ:

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

чак
источник
1
expires_inожидает данные в виде секунд, просто убедитесь, что вы преобразовали их в первую очередь.
frillybob
«ArgumentError (ожидается: expires_in будет числом секунд)»
Никита Федяшев