1

私はS3バケットに格納された多くの写真にアクセスするMeteor Mobileアプリを持っています。これらの写真はユーザーがアップロードし、頻繁に変更されます。私はこれらの写真が私のアプリを使用していない人にはアクセスできないようにしたい。 (つまり、これらの写真は自分のアプリケーションからしか見ることができず、ブラウザーでURLに直接アクセスすると読み込まれません)。MeteorモバイルアプリケーションからS3への認証

これを達成する最も良い方法は何ですか? AWS Cognitoは論理的な選択肢だと思われますが、実装が簡単ではないようで、Cognito IDを取得したら、クライアントからAWSを認証する方法が正確にはわかりません。

私の他の考えは、すべてのURLに読み取り専用のAWS Keyを置き、その方法で認証していましたが、それはほとんど無意味です。鍵と秘密を見つけることは本当に簡単でしょう。

EDIT:

。具体的には、画像のURLはMongoのコレクションであり、私はテンプレートに渡します。したがって、S3リソースにはイメージタグ(<img src=")がロードされます。 AWS STSのようなものは素晴らしい選択肢のように聞こえるが、私がこのように読み込んだときにヘッダーにトークンを渡す方法はわからない。あらかじめ署名されたクエリ文字列としてそれらを実行するのは非効率的です。

もう1つの方法は、referrerヘッダーlike this issueでアクセスを制限することです。しかし、Martijnのように、それは本当に安全な方法ではありません。

+0

アプリでは、S3画像のアクセスが認証されていますか?もしそうであれば、認証されたGETへのアクセスを許可し、あなたのバケットへのパブリックアクセスを拒否することができます。 – Mikkel

+0

いいえ、今は開発中の公的に利用可能なイメージですが、私は実際にクライアントからの認証を行う方法を知らず、URLとヘッダーの両方に鍵と秘密を渡します。両方とも見ることができます。ヘッダーを渡すのはURLよりはいいが、固い解決策ではないだろうと思う。 – gkrizek

+0

鍵を渡したくないのは間違いありませんが、Meteorサーバーを使用してHTTP認証要求(ブラウザからは表示されません)を送信して、渡すことができる認証トークンを取得できます画像のGETリクエスト – Mikkel

答えて

0

いくつかの研究とテストの後、私はこれを自分で解決しました。私の究極の解決策は、Sヘッダーを使用してS3バケットへのアクセスを制限することでした。私はより安全で詳細なソリューションを作成しましたが(下記参照)、それは私のアプリケーションではうまくいかないパフォーマンスヒットを伴いました。私のアプリは、写真やビデオを見ることに基づいており、即座に近くで負荷をかけられないようにすることはできませんでした。しかし、ほとんどのユースケースでは十分な解決策になると思います。私のアプリは非常に敏感ではないので、refererヘッダーで十分です。 AmazonのSTSを使用してHere is how to use the http header referer to limit access to a bucket.

ソリューション:

まず、あなたはserverclient.そこには、最大利用可能流星の日付パッケージになかったので、私は自分自身を作成し​​、両方にAWS SDKを持っている必要があります。

サーバーでは、役割を引き受ける権限を持つ資格情報を使用する必要があります。想定される役割は、その役割を担っているユーザーとの信頼関係を持っていなければなりません。 Article on using IAM. - Article on using credentials with SDK

server.jsファイルでは、クライアントから呼び出せるメテオメソッドを作成しました。最初にユーザーがログインしているかどうかを確認します。それが真の場合、次の5分で現在の一時的な資格情報が期限切れになっているかどうかを確認します。もしそうであれば、私は新しい証明書を発行し、それらをユーザー文書に書き込んだり、コールバックとして返す。次の5分間で期限切れになっていない場合は、現在の身分証明書を返します。

コールバックにはMeteor.bindEnvironmentを使用する必要があります。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; 
     } 
     } 
    } 
    } 
    }); 

その後、手動またはMeteor.startupsetIntervalでこのメソッドを呼び出すことができます。

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 

この方法では、セッション変数のキーをコールバックから設定するだけです。これを行うには、ユーザーのドキュメントを照会して取得することもできます。

次に、これらの一時的な認証情報を使用して、バケットでアクセスしようとしているオブジェクトの署名付きURLを取得できます。

私はテンプレートでそれにオブジェクト名を渡すことで、テンプレートのヘルパーでこれを置く:これだけです

{{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; 
} 

}); 

を!私はこれがより良くなるよう洗練されていると確信していますが、これは本当に速いテストで思いついたものです。私が言ったように、それはほとんどのユースケースでうまくいくはずです。私の特定の人はそうしなかった。なんらかの理由で、visibility: visible|hidden;img srcのsignedURLで切り替えると、URLを直接設定するだけではなく、読み込みに時間がかかります。 Amazonがオブジェクトを返す前に署名されたURLを復号化しなければならないからです。

方向のためにMikkelのおかげで。

関連する問題