LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

CloudFront FunctionsでBasic認証ができるか試してみた

はじめに

結論を申し上げるとできませんでした。悲しみが深いです。
「用法用量を守って正しくお使いください」という言葉がぴったりです。
Basic認証をCloudFrontで実装したいならLambda@Edgeを使いましょう。

参考資料:

追記(2021/05/24)

コメントをいただき、base64でエンコードできる方法が見つかったため、
Basic認証を実行することができます。

なぜBasic認証ができないのか

これはCloudFront Functionsの1 msの起動時間制限に引っかかるためだと思います。

CloudFront Functionsの用途として非常に高速でシンプルな HTTP(S) リクエスト/レスポンス操作することを目的としています。そのため、非同期処理等の時間がかかる複雑なものは対象外(Lambda@Edge使えよ)だったのだと思います。

また、使える関数も少なく、パッケージもインポートできないため、非常にシンプルな実装しかできませんでした。

参考資料:

そのため、参考資料のAWSブログで書かれている4つの用途で使うのが適任だと感じました。

  • キャッシュキーの操作と正規化
  • URL の書き換えとリダイレクト
  • HTTP ヘッダーの操作
  • アクセス承認

またサンプルコードのGitHubリポジトリがあるので、こちらを参照すると実装のイメージが湧くと思います。

Basic認証実装コード

※できないけど、実際に実装したコードを晒そうと思います。このコードではCloudFront FunctionsでBasic認証できません

CloudFront Functionsのランタイムは JavaScript(ECMAScript 5.1 compliant) と古く、使えるBuild-in modulesもCryptoとQuery stringしかないっぽいです。

参考資料:

そのため、自前でBasic認証を実現するためにBase64にする処理を自前で書く必要があります(btoa関数もないので、文字列をBase64に変換することは無理)。

function base64Encode(...parts) {
  return new Promise(resolve => {
    var reader = new FileReader();
    reader.onload = () => {
      var offset = reader.result.indexOf(",") + 1;
      resolve(reader.result.slice(offset));
    };
    reader.readAsDataURL(new Blob(parts));
  });
}

Basic認証をさせたい場合は www-authenticate ヘッダの値に Basic をいれればすることが可能なので、認証が通らない場合はこのようにします。

参考資料:

var response401 = {
    statusCode: 401,
    statusDescription: 'Unauthorized',
    headers:
    {'www-authenticate': {value:'Basic'}}
};

CloudFront Funcitonsのメイン関数はLambdaやLambda@Edgeと似てますが、 contextcallback がなく event のみ受け取ります。

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

最終的にこのようなコードを書きました。
base64Encodeの部分はpromiseで処理をしているため、
await/asyncを使いたかったのですが、JavaScript のバージョン制約で使えなかったです。

base64Encodeの戻り値から処理を行いresponseに代入するようにしてますが、処理時間の制約事項のため promise {} しか返して来ずBase64のエンコードはできませんでした。

var response401 = {
    statusCode: 401,
    statusDescription: 'Unauthorized',
    headers:
    {'www-authenticate': {value:'Basic'}}
};

function base64Encode(...parts) {
  return new Promise(resolve => {
    var reader = new FileReader();
    reader.onload = () => {
      var offset = reader.result.indexOf(",") + 1;
      resolve(reader.result.slice(offset));
    };
    reader.readAsDataURL(new Blob(parts));
  });
}

function handler(event) {
    // NOTE: This example function is for a viewer request event trigger. 
    // Choose viewer request for event trigger when you associate this function with a distribution. 
    // Get request and request headers
    var request = event.request;
    var headers = request.headers;
    // Configure authentication
    var authUser = 'user';
    var authPass = 'pass';
    var response = base64Encode(authUser + ':' + authPass).then((encoded) => {
        var authString =  'Basic ' + encoded;
        if (typeof headers.authorization == 'undefined' || headers.authorization.value != authString) {
          return response401;
        }
        return request;
    });
    return response;
}

コード修正版 追記(2021/05/24)

btoa 関数を使わなくても toString('base64') でエンコードすることができました。

var response401 = {
    statusCode: 401,
    statusDescription: 'Unauthorized',
    headers:
    {'www-authenticate': {value:'Basic'}}
};

function handler(event) {
    // NOTE: This example function is for a viewer request event trigger. 
    // Choose viewer request for event trigger when you associate this function with a distribution. 
    // Get request and request headers
    var request = event.request;
    var headers = request.headers;
    // Configure authentication
    var authUser = 'user';
    var authPass = 'pass';
    var authString = 'Basic ' + (authUser + ':' + authPass).toString('base64');
    if (typeof headers.authorization == 'undefined' || headers.authorization.value != authString) {
        return response401;
    }

    return request;
}

おわりに

Basic認証ではなく、Digest認証だとCloudFront Functionsで実装できそうでしたが、そこまで頑張るならLambda@Edgeでいいかという結論に至りました。

CloudFront Functionsで気軽にBasic認証もできるので、Lambda@Edge以外の選択肢も視野に入れてみるといいかもしれません。
※コードを読んでお気づきかと思いますが、Basic認証は文字列を単純に結合して、base64でエンコードするだけなので、めちゃくめちゃセキュリティ的に弱いので、あまり使わない方がいいです

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
What you can do with signing up
2