[日本語Alexa] AudioPlayerで署名付きURL(Pre-Signed URL)を使用する

この記事は公開されてから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にする

StatusDeployedになれば、CloudFlont経由のアクセスができなくなります。

(3) キーペアの作成

CloudFrontで署名付きURLを使用するためには、CloudFront用のキーペアが必要です。 (この処理はAWSアカウント(ルートアカウント)でないと作業できません)

アカウントのプルダウンメニューからセキュリテイ認証情報を選択します。

CloudFrontのキーペアから新しいキーペアの作成を行います。

秘密鍵(pk-xxxx.pem)をダウンロードして、Lambdaのコードと一緒に配置します。

(4) 紐付け

CloudFrontのキーペアは、CloudFrontのディストリビューションに紐付ける必要があります。

Behaviorの設定で、Restrict Viewer AccessYesにします。

キーペアの所有者とディストリビューションを作成したアカウントが同じであれば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を生成しリダイレクトする