1

Ich habe eine Meteor Mobile App, die auf viele Fotos in meinem S3-Bucket zugreift. Diese Fotos werden vom Benutzer hochgeladen und ändern sich häufig. Ich möchte nicht, dass diese Fotos für Nutzer zugänglich sind, die meine App nicht verwenden. (dh: diese Fotos sind nur aus meiner Anwendung heraus sichtbar und werden direkt in einen Browser geladen, wenn sie in die URL geladen werden).Authentifizierung bei S3 von Meteor Mobile Application

Was ist der beste Weg, dies zu erreichen? AWS Cognito scheint die logische Wahl zu sein, aber es scheint nicht einfach zu implementieren zu sein, und ich bin mir nicht sicher, wie ich mich vom Client authentifizieren soll, sobald es eine Cognito-Identität erhält.

Mein anderer Gedanke war, einen Nur-Lese-AWS-Schlüssel auf jede URL zu setzen und auf diese Weise zu authentifizieren, aber das ist fast sinnlos. Es wäre wirklich leicht, den Schlüssel und das Geheimnis herauszufinden.

EDIT:

Um genau zu sein, die URLs für die Bilder sind in einer Sammlung Mongo und ich geben sie in eine Vorlage. Die S3-Ressourcen werden also nur mit einem Image-Tag (<img src=") geladen. Etwas wie AWS STS klingt nach einer großartigen Option, aber ich weiß nicht, wie ich die Tokens in den Headern weitergeben kann, wenn ich sie so lade. Sie als vorzeichenbehaftete Abfragezeichenfolge zu verwenden, scheint ineffizient zu sein.

Eine weitere Option ist die Beschränkung des Zugriffs mit der referrer Kopfzeile, like this issue. Aber wie Martijn gesagt hat, ist es nicht wirklich eine sichere Art, es zu tun.

+0

Sind in Ihrer App S3-Bildzugriffe authentifiziert? Wenn dies der Fall ist, können Sie diesem authentifizierten GET Zugriff gewähren und allen öffentlichen Zugriff auf Ihren Bucket verweigern. – Mikkel

+0

Nein, im Moment sind es nur öffentlich verfügbare Images während der Entwicklung, aber das möchte ich nicht in der Produktion. Ich weiß nicht wirklich eine Möglichkeit, Authentifizierung vom Client zu tun, außer den Schlüssel und das Geheimnis in der URL oder in der Kopfzeile, die beide zu sehen sind, zu übergeben. Ich denke, die Übergabe im Header wäre besser als URL, aber keine feste Lösung. – gkrizek

+0

Nein, Sie wollen auf keinen Fall die Schlüssel weitergeben, aber Sie können den Meteor-Server verwenden, um eine HTTP-Authentifizierungsanfrage zu senden (die vom Browser nicht sichtbar ist), um ein Authentifizierungs-Token (ich denke) zu erhalten in dem Bild GET-Anfragen – Mikkel

Antwort

0

Nach einigen Recherchen und Tests habe ich das selbst gelöst. Meine ultimative Lösung bestand darin, den Header referer zu verwenden, um den Zugriff auf meinen S3-Bucket zu beschränken. Ich habe eine sicherere und detailliertere Lösung erstellt (siehe unten), aber es kam mit einem Leistungseinbruch, der für meine App nicht funktionierte. Meine App basiert auf dem Anzeigen von Fotos und Videos, und es war nicht in den Karten, sie nicht sofort laden zu können. Obwohl ich denke, dass es eine ausreichende Lösung für die meisten Anwendungsfälle sein könnte. Da meine App nicht sehr empfindlich ist, reicht die referer Kopfzeile für mich aus. Here is how to use the http header referer to limit access to a bucket.

Lösung Amazon STS mit:

Zuerst müssen Sie sowohl auf die server das AWS SDK haben und die client. Es Meteor bisher Pakete nicht bis zur Verfügung steht, so habe ich meine eigenen. (Ich werde es in Kürze veröffentlichen und hier einen Link setzen, sobald ich das tue.)

Auf dem Server müssen Sie Anmeldeinformationen verwenden, die die Möglichkeit haben, eine Rolle zu übernehmen. Die zu übernehmende Rolle muss eine Vertrauensbeziehung mit dem Benutzer haben, der die Rolle übernimmt. Article on using IAM. - Article on using credentials with SDK

In der Datei server.js habe ich eine Meteor-Methode erstellt, die ich vom Client aus aufrufen kann. Es prüft zunächst, ob ein Benutzer angemeldet ist. Wenn dies der Fall ist, überprüft es, ob die aktuellen Anmeldedaten in den nächsten 5 Minuten ablaufen. Wenn dies der Fall ist, erstelle ich neue Anmeldeinformationen und schreibe sie entweder in das Benutzerdokument oder gebe sie als Rückruf zurück. Wenn sie in den nächsten 5 Minuten nicht ablaufen, gebe ich ihre aktuellen Temp-Credentials zurück.

Sie müssenMeteor.bindEnvironmentfür den Rückruf verwenden.See docs

Meteor.methods({ 
'awsKey': function(){ 
    if (Meteor.userId()){ 
    var user = Meteor.userId(); 
    var now = moment(new Date()); 
    var userDoc = Meteor.users.findOne({_id: user}); 
    var expire = moment(userDoc.aws.expiration); 
    var fiveMinutes = 5 * 60 * 1000; 
    var fut = new Future(); 

    if(moment.duration(expire.diff(now))._milliseconds < fiveMinutes){ 
     var params = { 
      RoleArn: 'arn:aws:iam::556754141176:role/RoleToAssume', 
      RoleSessionName: 'SessionName', 
      DurationSeconds: 3600 //1 Hour 
     }; 
     var sts = new AWS.STS(); 
     sts.assumeRole(params, Meteor.bindEnvironment((err, data) => { 
      if (err){ 
       fut.throw(new Error(err)); 
      }else{ 
       Meteor.users.update({_id: user}, {$set: {aws: {accessKey: data.Credentials.AccessKeyId, secretKey: data.Credentials.SecretAccessKey, sessionToken: data.Credentials.SessionToken, expiration: data.Credentials.Expiration}}}); 
       fut.return(data.Credentials); 
      } 
      })); 
      return fut.wait(); 
     }else{ 
     return userDoc.aws; 
     } 
     } 
    } 
    } 
    }); 

Dann können Sie diese Methode manuell oder in einem setInterval auf Meteor.startup aufrufen.

Meteor.setInterval(function(){ 
    if(Meteor.userId()){ 
     Meteor.call('awsKey', function(err, data){ 
     if (err){ 
      console.log(err); 
     }else{ 
      if(data.accessKey){ 
      Session.set('accessKey', data.accessKey); 
      Session.set('secretKey', data.secretKey); 
      Session.set('sessionToken', data.sessionToken); 
      }else{ 
      Session.set('accessKey', data.AccessKeyId); 
      Session.set('secretKey', data.SecretAccessKey); 
      Session.set('sessionToken', data.SessionToken); 
      } 
     } 
     }); 
    } 
    }, 300000); //5 Minute interval 

Auf diese Weise werden nur die Schlüssel in einer Session-Variable vom Rückruf festgelegt. Sie können dies tun, indem Sie das Dokument des Benutzers abfragen, um sie auch zu erhalten.

Dann können Sie diese temporären Anmeldeinformationen verwenden, um eine signierte URL für das Objekt zu erhalten, auf das Sie in Ihrem Bucket zugreifen möchten.

Ich habe dies in einem Template-Helfer durch den Namen in der Vorlage es Objekt übergeben:

{{getAwsUrl imageName}}

Template.templateName.helpers({ 

    'getAwsUrl': function(filename){ 
     var accessKey = Session.get('accessKey'); 
     var secretKey = Session.get('secretKey'); 
     var sessionToken = Session.get('sessionToken'); 
     var filename = filename; 
     var params = {Bucket: 'bucketName', Key: filename, Expires: 6000}; 
     new AWS.S3({accessKeyId: accessKey, secretAccessKey: secretKey, sessionToken: sessionToken, region: 'us-west-2'}).getSignedUrl('getObject', params, function (err, url) { 
     if (err) { 
     console.log("Error:" +err); 
     }else{ 
     result = url; 
     } 
    }); 
    return result; 
} 

}); 

das alles gibt es zu, es ist! Ich bin mir sicher, dass dies verfeinert werden kann, um besser zu sein, aber das ist genau das, was ich beim Testen davon wirklich schnell gefunden habe. Wie gesagt, es sollte in den meisten Anwendungsfällen funktionieren. Mein besonderer hat nicht. Aus irgendeinem Grund, wenn Sie versuchten, die visibility: visible|hidden; auf eine img src dieser signedURLs zu schalten, würden sie viel länger zum Laden als nur die URL direkt einstellen. Es muss sein, weil Amazon die signierte URL auf ihrer Seite entschlüsseln muss, bevor das Objekt zurückgegeben wird.

Dank Mikkel für die Richtung.

Verwandte Themen