Come memorizzare i dati in S3 e consentire l'accesso degli utenti in modo sicuro con il client API / iOS di rails?


93

Sono nuovo nello scrivere Rails e API. Ho bisogno di aiuto con la soluzione di archiviazione S3. Ecco il mio problema.

Sto scrivendo un'API per un'app iOS in cui gli utenti accedono con l'API di Facebook su iOS. Il server convalida l'utente rispetto ai problemi del token Facebook per l'utente iOS ed emette un token di sessione temporaneo. Da questo punto l'utente deve scaricare i contenuti archiviati in S3. Questo contenuto appartiene solo all'utente e a un sottoinsieme dei suoi amici. Questo utente può aggiungere più contenuti a S3 a cui possono accedere lo stesso gruppo di persone. Immagino sia simile ad allegare un file a un gruppo Facebook ...

Ci sono 2 modi in cui un utente può interagire con S3 ... lasciarlo al server o convincere il server a emettere un token S3 temporaneo (non sono sicuro delle possibilità qui) e l'utente può raggiungere gli URL del contenuto direttamente su S3. Ho trovato questa domanda che parlava degli approcci, tuttavia, è davvero datata (2 anni fa): domanda architettonica e di design sul caricamento di foto dall'app per iPhone e S3

Quindi le domande:

  • C'è un modo per limitare un utente ad accedere solo ad alcuni contenuti su S3 quando viene emesso un token temporaneo? Come posso fare questo? Supponiamo che ci siano ... diciamo 100.000 o più utenti.
  • È una buona idea lasciare che il dispositivo iOS estragga direttamente questo contenuto?
  • O dovrebbe lasciare che il server controlli tutto il passaggio dei contenuti (questo ovviamente risolve la sicurezza)? Questo significa che devo scaricare tutto il contenuto sul server prima di consegnarlo agli utenti connessi?
  • Se conosci i binari ... posso usare graffetta e gemme aws-sdk per ottenere questo tipo di configurazione?

Mi scuso per molteplici domande e apprezzo qualsiasi approfondimento sul problema. Grazie :)


1
ho trovato questo e ho pensato di commentare per altri che guardano docs.aws.amazon.com/AmazonS3/latest/dev/…
dibble

Risposte:


113

Utilizzando aws-sdk gem , puoi ottenere un URL firmato temporaneo per qualsiasi oggetto S3 chiamando 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

Questo ti darà un URL di utilizzo temporaneo firmato solo per quell'oggetto in S3. Scade dopo 20 minuti (in questo esempio) ed è valido solo per quell'oggetto.

Se hai molti oggetti di cui il cliente ha bisogno, dovrai emettere molti URL firmati.

O dovrebbe lasciare che il server controlli tutto il passaggio dei contenuti (questo ovviamente risolve la sicurezza)? Questo significa che devo scaricare tutto il contenuto sul server prima di consegnarlo agli utenti connessi?

Tieni presente che questo non significa che il server debba scaricare ogni oggetto, deve solo autenticare e autorizzare client specifici ad accedere a oggetti specifici in S3.

Documenti API da Amazon: https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth


3
Grazie per questo @ejdyksen. La soluzione che ho escogitato ha utilizzato esattamente quella (non ho aggiornato la domanda con la mia risposta)! Quindi la mia soluzione era fare URL autenticati per le richieste GET. Tuttavia, quando un utente contribuisce al contenuto, crea risorse in una posizione specifica / bucket / user / objectname utilizzando il token IAM federato (credenziali temporanee che scadono) con la policy allegata per consentire l'accesso in scrittura a / bucket / user / *. quindi nessun utente nel sistema può danneggiare il contenuto di altri utenti. Sembra funzionare bene. Apprezzo la tua risposta.
Dineth

5
Se stai utilizzando la v2 di aws-sdk-ruby, tieni presente che i metodi sono leggermente diversi: docs.aws.amazon.com/sdkforruby/api/Aws/S3/…
vijucat

2
Non è un rischio che l'utente possa vedere la mia chiave di accesso? C'è anche la chiave segreta (convertita)
user2503775

3
@ Dennis il punto è che il file non deve raggiungere il tuo server. Il collegamento non contiene le tue credenziali AWS. Potrebbe contenere il ACCESS_KEY_ID(non ricordo dalla parte superiore della mia testa), ma non vuole essere un segreto.
ejdyksen

2
@ejdyksen hai ragione, ho appena verificato che l'URL contenga solo il file AWS_ACCESS_KEY_ID. Inizialmente pensavo che AWS_SECRET_ACCESS_KEYfosse anche visualizzato ma non lo è.
Dennis

46

Le risposte precedenti utilizzano la vecchia gemma aws-sdk-v1 invece della nuova versione 2 di aws-sdk-resources.

Il nuovo modo è:

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

dove your_object_key è il percorso del file. Se hai bisogno di cercarlo, dovresti usare qualcosa come:

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

Quell'informazione era sorprendentemente difficile da scoprire, e quasi mi arresi e usai la gemma più vecchia.

Riferimento

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


1
expires_insi aspetta i dati sotto forma di secondi, assicurati solo di convertirli prima.
frillybob

"ArgumentError (previsto: expires_in per un numero di secondi)"
Reedwolf
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.