Wie speichere ich Daten in S3 und erlaube dem Benutzer den sicheren Zugriff mit dem Rails API / iOS Client?

93

Ich bin neu im Schreiben von Rails und APIs. Ich benötige Hilfe bei der S3-Speicherlösung. Hier ist mein Problem.

Ich schreibe eine API für eine iOS-App, bei der sich die Benutzer mit der Facebook-API unter iOS anmelden. Der Server überprüft den Benutzer anhand der von Facebook an den iOS-Benutzer ausgegebenen Token und stellt ein temporäres Sitzungstoken aus. Ab diesem Zeitpunkt muss der Benutzer Inhalte herunterladen, die in S3 gespeichert sind. Dieser Inhalt gehört nur dem Benutzer und einer Teilmenge seiner Freunde. Dieser Benutzer kann S3 weitere Inhalte hinzufügen, auf die dieselbe Gruppe von Personen zugreifen kann. Ich denke, es ist ähnlich wie das Anhängen einer Datei an eine Facebook-Gruppe ...

Es gibt zwei Möglichkeiten, wie ein Benutzer mit S3 interagieren kann: Überlassen Sie es dem Server oder veranlassen Sie den Server, ein temporäres S3-Token auszustellen (nicht sicher, welche Möglichkeiten hier bestehen), und der Benutzer kann die Inhalts-URLs direkt an S3 abrufen. Ich fand diese Frage über die Ansätze, aber sie ist wirklich veraltet (vor 2 Jahren): Architektur- und Designfrage zum Hochladen von Fotos von der iPhone App und S3

Also die Fragen:

  • Gibt es eine Möglichkeit, einen Benutzer auf den Zugriff auf nur einige Inhalte in S3 zu beschränken, wenn ein temporäres Token ausgegeben wird? Wie kann ich das machen? Angenommen, es gibt ... sagen wir 100.000 oder mehr Benutzer.
  • Ist es eine gute Idee, das iOS-Gerät diesen Inhalt direkt abrufen zu lassen?
  • Oder sollte der Server alle Inhalte steuern lassen (dies löst natürlich die Sicherheit)? Bedeutet dies, dass ich alle Inhalte auf den Server herunterladen muss, bevor ich sie an die verbundenen Benutzer weitergebe?
  • Wenn Sie Schienen kennen ... kann ich Büroklammern und aws-sdk-Edelsteine ​​verwenden, um dieses Setup zu erreichen?

Ich entschuldige mich für mehrere Fragen und freue mich über jeden Einblick in das Problem. Vielen Dank :)

dineth
quelle
1
fand dies und dachte, ich würde für andere kommentieren, die docs.aws.amazon.com/AmazonS3/latest/dev/…
dibble

Antworten:

113

Mit dem Juwel aws-sdk können Sie eine temporär signierte URL für jedes S3-Objekt erhalten, indem Sie Folgendes aufrufen 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

Dadurch erhalten Sie eine signierte, temporäre Verwendungs-URL nur für dieses Objekt in S3. Es läuft nach 20 Minuten ab (in diesem Beispiel) und ist nur für dieses eine Objekt geeignet.

Wenn Sie viele Objekte haben, die der Client benötigt, müssen Sie viele signierte URLs ausgeben.

Oder sollte der Server alle Inhalte steuern lassen (dies löst natürlich die Sicherheit)? Bedeutet dies, dass ich alle Inhalte auf den Server herunterladen muss, bevor ich sie an die verbundenen Benutzer weitergebe?

Beachten Sie, dass dies nicht bedeutet, dass der Server jedes Objekt herunterladen muss, sondern nur bestimmte Clients authentifizieren und autorisieren muss, um auf bestimmte Objekte in S3 zuzugreifen.

API-Dokumente von Amazon: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth

ejdyksen
quelle
3
Danke dafür @ejdyksen. Die Lösung, die ich entwickelt habe, hat genau das verwendet (ich habe die Frage nicht mit meiner Antwort aktualisiert)! Meine Lösung bestand also darin, authentifizierte URLs für GET-Anforderungen zu erstellen. Wenn ein Benutzer jedoch Inhalte beisteuert, erstellt er mithilfe eines zusammengeschlossenen IAM-Tokens (temporäre Anmeldeinformationen, die ablaufen) Ressourcen mit der angehängten Richtlinie, um / buckel / user / * Schreibzugriff zu ermöglichen, Ressourcen für einen bestimmten Speicherort / Bucket / Benutzer / Objektname. Daher kann kein Benutzer im System den Inhalten anderer Benutzer Schaden zufügen. Scheint in Ordnung zu funktionieren. Schätzen Sie Ihre Antwort.
Dineth
5
Wenn Sie v2 des aws-sdk-ruby verwenden, beachten Sie, dass die Methoden etwas anders sind: docs.aws.amazon.com/sdkforruby/api/Aws/S3/…
vijucat
2
Ist es nicht ein Risiko, dass der Benutzer meinen Zugangsschlüssel sehen kann? Es gibt auch den geheimen Schlüssel (konvertiert)
user2503775
3
@ Tennis der springende Punkt ist, dass die Datei nicht auf Ihren Server treffen muss. Der Link enthält keine AWS-Anmeldeinformationen. Es könnte das enthalten ACCESS_KEY_ID(ich erinnere mich nicht auf Anhieb), aber das soll kein Geheimnis sein.
Ejdyksen
2
@ejdyksen Sie haben Recht, ich habe gerade überprüft, dass die URL nur die enthält AWS_ACCESS_KEY_ID. Ich dachte ursprünglich, dass AWS_SECRET_ACCESS_KEYdas auch angezeigt wurde, aber es ist nicht.
Dennis
46

Die obigen Antworten verwenden das alte Juwel aws-sdk-v1 anstelle der neuen Version 2 aws-sdk-resources.

Der neue Weg ist:

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

Dabei ist your_object_key der Pfad zu Ihrer Datei. Wenn Sie das nachschlagen müssen, würden Sie etwas verwenden wie:

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

Diese Informationen waren erstaunlich schwer zu finden, und ich gab fast einfach auf und benutzte den älteren Edelstein.

Referenz

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

Chaqke
quelle
1
expires_inerwartet die Daten in Form von Sekunden, stellen Sie einfach sicher, dass Sie diese zuerst konvertieren.
Rüschenbob
"ArgumentError (erwartet: expires_in wird eine Anzahl von Sekunden sein)"
Reedwolf