これは フェンリル デザインとテクノロジー Advent Calendar 2021 12 日目の記事です。
GIMLE チームの野田です。
ウェブサービスを構築する際、公開前のサイトに対して、Basic 認証を使って一時的にアクセス制限をかけたいことがあります。 CDN に Amazon CloudFront を利用する場合、以前から Lambda@Edge や CloudFront Functions を利用して Basic 認証をかけることはできました。 ここでは 2021 年 3 月にリリースされた AWS WAF のカスタム応答機能 を利用して、AWS WAF の設定だけで Basic 認証をかけてみます。
Basic 認証のフロー
MDN のドキュメントより、HTTP 認証ではクライアントとサーバーの間で以下のようなチャレンジとレスポンスがおこなわれます。
- サーバーは少なくとも1回のチャレンジで、クライアントに 401 (Unauthorized) レスポンスステータスを返し、 WWW-Authenticate レスポンスヘッダーを含めて認証方法に関する情報を提供します。
- サーバーで自身を認証したいクライアントは Authorization リクエストヘッダフィールドに資格情報を含めることでそれを行うことができます。
- 通常、クライアントはユーザーにパスワードのプロンプトを表示し、正しい Authorization ヘッダーを含むリクエストを発行します。
このフローの 1. でサーバーから提供される WWW-Authenticate レスポンスヘッダーは、WWW-Authenticate: <type> realm=<realm>
のような形式になります。
Basic 認証を要求する場合は <type>
が Basic
となります。
フローの 2. でクライアントからサーバーに送る Authorization リクエストヘッダフィールドは、Authorization: <type> <credentials>
のような形式になります。
Basic 認証の場合、 <type>
が Basic
になります。
<credentials>
にセットする資格情報は、ユーザー ID とパスワードをコロン(:
)区切りで結合し、Base64 でエンコードした文字列になります。
(MDN のドキュメントにも記載されていますが、Base64 は可逆エンコードのため Basic 認証は安全でなく、HTTPS / TLS を組み合わせる必要があります)
AWS WAF のルールを組み立てる
Basic 認証のフローに合わせて、AWS WAF の Rule builder で独自のルールを作成します。 ここでは、基本的な方針として、Web ACL のデフォルトアクションを許可(Allow)とし、個別のルールにマッチした場合にブロックします。
ルールステートメントの設定
ルールステートメントを以下の通り設定し、Authorization
ヘッダーに対して特定の文字列が設定されているかを検証します。
- Inspect: Header
- Header field name:
Authorization
- Match type: Exactly matches string
- String to match:
Basic <credentials>
- Text transformation: None
String to match フィールドに指定する <credentials>
は、Basic 認証で使われる資格情報なので、base64
コマンドを利用して次のように変換できます。
echo
コマンドの -n
オプションは、改行文字を出力しないようにするための指定です。
$ echo -n aladdin:opensesame | base64 YWxhZGRpbjpvcGVuc2VzYW1l
正しい資格情報が送信され、ルールステートメントに一致した場合はアクセスを許可したいため、If a request には doesn't match the statement (NOT) を指定し、ルールステートメントに一致しなかった場合、アクセスを拒否するよう設定します。
ルールアクションの設定
ルールステートメントに一致しなかった場合のアクションを Block に設定し、Custom response の Enable 欄にチェックを入れて、以下の通り応答をカスタマイズします。
- Response code:
401
(Unauthorized) - Response headers:
- Key:
WWW-Authenticate
- Value:
Basic realm="<realm>"
- Key:
<realm>
には、認証が必要な領域であることを示すメッセージを設定しておきます。
確認
Rule builder で独自のルールを使用する CloudFront 用の Web ACL を作成し、CloudFront Distribution に割り当てます(Web ACL や CloudFront の設定は割愛します)。
ウェブブラウザで開発者コンソールを表示しながら CloudFront のエンドポイントにアクセスしてみると、HTTP ステータスコード 401 で www-authenticate ヘッダーが返ることが確認できました。
ユーザー名とパスワードを確認するダイアログが表示されるため、正しいユーザー名とパスワードを入力すると、サイトにアクセスできます。
AWS のサービス設計
AWS WAF には、Basic 認証そのものをサポートする機能や UI はありませんが、カスタム応答機能を利用することで Basic 認証が実現できました。 Basic 認証機能を提供するのではなく、レスポンスコードやレスポンスヘッダーをカスタマイズできるという、より汎用的な手段としてデザインされているところが、AWS らしくて良いなと思います。 利用する側としては、HTTP 認証のフローや Basic 認証で使われる HTTP ヘッダーについて知らないといけないので、多少の知識が要求されてしまうところではありますが。
なお、WAF のルールステートメントに設定する文字列は KMS による暗号化ができないため、資格情報の格納手段としてあまりセキュアではありません。
IAM ポリシーで WAF (wafv2) の読み込み権限が付与されていたり、wafv2:GetWebACL
や wafv2:GetRuleGroup
アクションが許可されていたりする場合は、設定した資格情報が参照できてしまう可能性があります。
そのため、この方法がアクセス権限制御等の要件に合うか、十分ご留意ください。