LoginSignup
0

More than 1 year has passed since last update.

Amazon CloudFront Functionsでハッシュの検証(HMAC)

Last updated at Posted at 2022-12-17

こちらは『ゆるWeb勉強会@札幌 Advent Calendar 2022』の18日目の記事です。

はじめに

S3に保存しているコンテンツを保護したい場合、署名付きURLや署名付きCookieを利用すると思います。
もう少しライトにアクセスを制限出来ないかと思い、CloudFront Functionsで
ハッシュの検証を試してみました。

実現したいこと

クエリパラメータsに指定したハッシュ値が正しい場合のみ、S3に保存してある画像を表示するようにします。

ハッシュ化はCloudFront Functionsで用意されているビルトインモジュール(crypto)を利用します。
アルゴリズムはsha256を使用し、ハッシュベースのメッセージ認証コード (HMAC)を生成します。

今回リクエスト側の処理は省略します。
テスト用にPHPでハッシュ値を生成しました。

前提条件

  • 画像はS3にアップロード済
  • S3はCloudFront経由でのみアクセスする設定済
  • CloudFrontからS3への接続設定済

コード

CloudFront Functions用

JWT認証のコードを参考にしています。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-validate-token.html

ハッシュ化部分

var crypto = require('crypto');
crypto.createHmac('sha256', {シークレット値}).update({ハッシュ化したい文字列}).digest('hex')

crypto.createHmac(algorithm, secret key)
指定された algorithm と secret key を使用する HMAC オブジェクトを作成して返します。アルゴリズムは md5、sha1、sha256 のいずれかを使用します。

hmac.update(data)
指定された data を使用して HMAC コンテンツを更新します 。

hmac.digest([encoding])
hmac.update() を使用して渡されたすべてのデータのダイジェストを計算します。エンコードは hex、base64、base64url のいずれかを使用します。

cloudfront functionに設定
var crypto = require('crypto');

//バリデーションエラー時のレスポンス
var response401 = {
    statusCode: 401,
    statusDescription: 'Unauthorized'
};

//ハッシュ値のバリデーション
function verifyRequest(fwdUri, token, key, signingMethod, signingType) {
  // check token
  if (!token) {
    throw new Error('No token supplied');
  }

  if (!_verify(fwdUri, token, key, signingMethod, signingType)) {
    throw new Error('Signature verification failed');
  }

  return fwdUri;
}

function _verify(input, token, key, method, type) {
  if (type === "hmac") {
    return (token === _sign(input, key, method));
  }
  else {
    throw new Error('Algorithm type not recognized');
  }
}

function _sign(input, key, method) {
  return crypto.createHmac(method, key).update(input).digest('hex');
}

function handler(event) {
  var request = event.request;

  //Secret key used to verify token.
  //Update with your own key.
  var key = "penguin";
  var signingMethod = 'sha256';
  var signingType = 'hmac';

  // If no token, then generate HTTP redirect 401 response.
  if (!request.querystring.s) {
    console.log("Error: No token in the querystring");
    return response401;
  }

  var fwdUri = event.request.uri;
  var token = request.querystring.s.value;

  try {
    verifyRequest(fwdUri, token, key, signingMethod, signingType);
  }
  catch (e) {
    console.log(e);
    return response401;
  }

  //Remove the token from the query string if valid and return.
  delete request.querystring.s;
  console.log("Valid token");
  return request;
}

リクエストのテスト用ハッシュ作成

以下の内容で作成します。

参照S3:/images/sample.jpg
secret:penguin

makeHash.php
<?php
    $s3Path = "/images/sample.jpg";
    $hased_string = hash_hmac('sha256', $s3Path, 'penguin');
    print_r($hased_string.PHP_EOL);
$ php makeHash.php
fcf3f9b449720bfc1fd32f8fe3a2d0cb72ebe14e6ea86cb7d6fdc60e4b7dd521

CloudFront Functionsの作成

チュートリアルの通りに設定します。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html

  1. マネジメントコンソールからCloudFrontを開きます。
    https://console.aws.amazon.com/cloudfront/v3/home

  2. [関数を作成]ボタンをクリックします。
    image.png

  3. 関数名を入力し、[関数を作成]ボタンをクリックします。
    image.png

  4. 作成された関数の画面に遷移します。
    image.png

  5. 構築タブをクリックし、開発のテキストにコードを貼り付けます。

  6. [変更を保存]ボタンをクリックします。
    image.png

  7. 正常に発行されました。のメッセージが表示されます。
    発行タブをクリックします。
    image.png

  8. 関連付けられているディストリビューション欄の[関連付けを追加]ボタンをクリックします。
    image.png

  9. CloudFrontディストリビューションと紐づけます。
    イベントタイプは「Viewer Request」を選択します。
    image.png

  10. 紐づけ成功のメッセージが表示されます。
    image.png

  11. 関数の反映を待ちます。
    image.png
    反映されるとステータスが「デプロイ済み」になります。
    image.png

実行結果

正しくないハッシュ値の場合

s=hogeなどを渡すと、指定した401エラーが返却されます。
image.png

正しいハッシュ値を渡した場合

画像が表示されました!
image.png

まとめ

初めてCloudFront Functionsを触りましたが、Lambda@Edgeよりは簡単に実装できました。
例えば指定したサイズを含むハッシュ値を作成し、画像のリサイズ処理の前に検証すると良さそうです。
サイズを微妙に変更したリクエストが大量に届いても、Lambda@Edgeの実行前に制限できますね。

制限もありますので、クオータ紹介ページで確認しておきましょう!

P.S.ヘッダに認証キーを設定する方法が書かれている記事を見つけました。
クエリでハッシュ値を渡すよりおしゃれです。

参考資料

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0