この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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を生成しリダイレクトする