[日本語Alexa] AudioPlayerで署名付きURL(Pre-Signed URL)を使用する
1 はじめに
Alexaでは、AudioPlayerインターフェースを使用してAudioファイル(音楽などのMP3)を再生することができます。
この時に指定するAudioファイルのURLは、一般に公開され外部からアクセス可能である必要があります。そして、AVS(Echo端末など)は、URLを元にAudioファイルを直接ダウンロードします。
Echoで再生されるのは問題ないが、ブラウザなどでダウンロード出来てしまうのはちょっと・・・というような場合、Audioファイルを公開状態で置くのは、ちょっと問題に感じるかも知れません。
そこで今回は、AudioファイルをS3に配置し、スキルから返すURLを、一時的にしか使用できない署名付きURLで指定してみました。
2 署名付きURL(Pre-Signed URL)
署名付きURL(Pre-Signed URL) とは、S3上のオブジェクトに期限付きURLを生成する機能です。アップロードとダウンロードの2種類がありますが、今回は、ダウンロードのみを使用します。
署名付きURLは、AWS-SDKで簡単に生成できます。
var AWS = require('aws-sdk'); var s3 = new AWS.S3({region: 'us-east-1'}); function getSignedUrl(key, bucket, expires, callback) { var params = { Bucket: bucket, Key: key, Expires: expires }; s3.getSignedUrl('getObject', params, function (err, url) { callback(url); }); }
上記を使用しているようすです。30秒だけアクセスできるURLを生成して、AudioPlayerディレクティブを返しています。
'PlayIntent': function () { const bucket = 'pre-signed-url-sample'; const key = 'sample.mp3'; const expires = 30; getSignedUrl(key, bucket, expires, url => { this.response.speak('音楽再生を開始します').audioPlayerPlay('REPLACE_ALL', url, url, null, 0); // urlをそのままtokenに使用しています。 this.emit(':responseReady'); }) }
Lambdaには、AmazonS3ReadOnlyAccessを追加する必要がありますが、S3上のバケットやファイル自体を公開する必要はありません。
3 CloudFront経由
次は、S3から直接ダウンロードさせるのではなく、CloudFrontを挟んだ場合です。
(1) CloudFrontの設定
CloudFront経由でのみS3にアクセスできる状態にしたところから設定してみます。
この状態では、CloudFlontのURLを使用して自由にアクセスできています。
(2) Behaviorsの設定
BehaviorsタブからRestrict Viwer Access(Use Signed URLs or Signed Cookies)をYesにする
StatusがDeployedになれば、CloudFlont経由のアクセスができなくなります。
(3) キーペアの作成
CloudFrontで署名付きURLを使用するためには、CloudFront用のキーペアが必要です。 (この処理はAWSアカウント(ルートアカウント)でないと作業できません)
アカウントのプルダウンメニューからセキュリテイ認証情報を選択します。
CloudFrontのキーペアから新しいキーペアの作成を行います。
秘密鍵(pk-xxxx.pem)をダウンロードして、Lambdaのコードと一緒に配置します。
(4) 紐付け
CloudFrontのキーペアは、CloudFrontのディストリビューションに紐付ける必要があります。
Behaviorの設定で、Restrict Viewer AccessをYesにします。
キーペアの所有者とディストリビューションを作成したアカウントが同じであればTrusted Signersは、Selfになります。また、別のAWSアカウントであればSpecify Accountsにチェックを入れ、該当のAWSアカウント番号を入力します。
CloudFrontの署名付きURLは、AWS-SDKで簡単に生成できます。
const cf = require('aws-cloudfront-sign') function getSignedUrl(keypairId, privateKeyPat, expires) { var options = { keypairId: keypairId, privateKeyPath: privateKeyPat, expireTime: (new Date().getTime() + expires * 1000) } // CloudFrontのURL return cf.getSignedUrl('https://xxxxxxxx.cloudfront.net/sample.mp3', options); }
上記を使用しているようすです。30秒だけアクセスできるURLを生成して、AudioPlayerディレクティブを返しています。
'PlayIntent': function () { const keypairId = 'XXXXX'; const privateKeyPath = './pk-XXXXX.pem'; const expires = 30; let url = getSignedUrl(keypairId, privateKeyPath, expires); this.response.speak('音楽再生を開始します').audioPlayerPlay('REPLACE_ALL', url, url, null, 0); this.emit(':responseReady'); }
4 SSMLのAudioタグでは、利用できない
2019/02/22 追記
以前、SSMLのAudioタグで、署名付きURL(Pre-Signed URL)が利用できない旨の記述をしておりましたが、私の検証ミスで、問題なく利用できるとのご指摘を頂きました。 訂正し、お詫び申し上げます。
5 最後に
今回は、AudioPlayerで使用するコンテンツの保護のため、URLを署名付きURLで指定する要領を試してみました。 結論として、問題なく使用できると言えます。
まったく制限の無いコンテンツ以外は、この要領で指定するのが安全なのでは?と感じてます。
まったく制限の無いコンテンツ以外は、この要領で使用した方が良いのでは?と感じています。
6 参考リンク
Discussion Forums - s3.getSignedUrl endpoint problem
[AWS]S3の期限付きURLを生成する[node]
【小ネタ】AWS CLIでS3のPre-Signed URLを生成できるようになっていました!
Boto3でS3のpre-signed URLを生成する
API Gateway + LambdaアプリケーションでS3に署名付きURLを生成しリダイレクトする