AWS WAF で Web エンドポイントを保護する

AWSBeginner
オンラインで実践に進む

はじめに

Web アプリケーションは、ヘルスチェックを利用可能に保ちながら、内部エクスポートパスをブロックする必要があります。パスルールを関連付け、リクエストがバックエンドへ到達するかをテストし、後片付けの前にルールを更新します。

先に LabEx で AWS を使い始める と レポートの読み取り役に最小権限を与える を完了してください。この新しい VM は、アプリケーションと独立した REST API ステージを提供します。以前の API やネットワークリソースは必要ありません。

認定試験との関連

このラボでは、次の試験トピックに関連する実践的な演習を行います。

Regional Web ACL を作成する

このステップでは、Web アクセスコントロールリスト(Web ACL) 内に AWS WAF のリクエストルールを定義します。WAF は、リクエストがアプリケーションへ到達する前に検査します。提供された REST API には、Regional Web ACL を関連付けられる名前付きステージがあります。この入口は、API/Cognito コースで使った HTTP API とは異なります。

Terminal の隣で AWS View を開き、ルール、ステージとの関連付け、各リクエストがバックエンドに到達するかを比較します。無関係な参照 Web ACL とステージを保持してください。

ルールは、一致条件とアクションを組み合わせます。デフォルトアクションは、どのルールにも一致しない場合に適用されます。/internal/ をブロックし、他のパスを許可します。

プロジェクトディレクトリに移動し、準備済みの操作担当者の識別情報を確認します。提供された stage-arn.txt には、認証情報ではなくアプリケーションステージの ARN が含まれています。コマンド置換は、後で関連付けるためにその値を保存します。

cd /home/labex/project
aws sts get-caller-identity --query Arn --output text
STAGE_ARN=$(cat stage-arn.txt)

labex-sec05-operator が返るはずです。Web ACL を関連付ける前は、合成データの内部エクスポートがバックエンドに到達します。curl は実際の HTTP リクエストを行います。-sS は接続エラーを表示したまま進捗出力を省き、-w はレスポンスのステータスを表示します。

curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/internal/export

HTTP 200 と、合成データのバックエンドレスポンスが返るはずです。引用符付きのヒアドキュメントでルールファイルを書き込みます。<<'EOF' と EOF の間の行が、そのまま JSON ファイルになります。UriPath はパスを選択し、STARTS_WITH は接頭辞に一致し、NONE は変換を行いません。数値の小さい優先度から実行されます。この ACL には、優先度 0 のルールが 1 つあります。この限定した演習では、可視性設定で任意のサンプルとメトリクスを無効にします。ACL 自体と後の更新で使うため、同じ設定を visibility.json に保存します。

cat > rules-internal.json <<'EOF'
[{
  "Name": "block-private-export",
  "Priority": 0,
  "Action": {"Block": {}},
  "Statement": {"ByteMatchStatement": {
    "SearchString": "/internal/",
    "FieldToMatch": {"UriPath": {}},
    "PositionalConstraint": "STARTS_WITH",
    "TextTransformations": [{"Priority": 0, "Type": "NONE"}]
  }},
  "VisibilityConfig": {"SampledRequestsEnabled": false, "CloudWatchMetricsEnabled": false, "MetricName": "labex-sec05-owned-export"}
}]
EOF

デフォルト Allow の Regional ACL を作成します。--cli-binary-format raw-in-base64-out は、AWS CLI v2 に対して、SearchString を base64 文字列ではなく、そのままの入力バイト列として解釈するよう指示します。file:// はルールドキュメントを読み込み、--query は結果の ACL ID だけを選択します。

cat > visibility.json <<'EOF'
{
  "SampledRequestsEnabled": false,
  "CloudWatchMetricsEnabled": false,
  "MetricName": "labex-sec05-owned-export"
}
EOF

ACL_ID=$(aws wafv2 create-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --default-action Allow={} \
  --visibility-config file://visibility.json \
  --rules file://rules-internal.json \
  --cli-binary-format raw-in-base64-out \
  --query Summary.Id \
  --output text)
ACL_ARN=$(aws wafv2 get-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --query WebACL.ARN \
  --output text)
aws wafv2 get-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --query 'WebACL.{Name:Name,Default:DefaultAction,Rules:Rules[].Name}'

所有 ACL、デフォルト Allow、block-private-export が返るはずです。ポリシーを作成しても、エンドポイントには関連付けられません。AWS View には、まだ対象との関連付けが表示されないはずです。

ACL を関連付けて実際のリクエストをテストする

このステップでは、ポリシーを提供された REST API ステージに接続し、公開リクエストと、一致するエクスポートリクエストを比較します。ステージは、デプロイされた API 環境を識別します。関連付けは、そのステージのリクエストに ACL を適用します。

バックエンドの前にある WAF

関連付けられた Web ACL は、内部パスがバックエンドへ到達する前に拒否します。

提供された対象 ARN には、自分の ACL だけを関連付けます。無関係な参照ステージには、すでに独自の参照 ACL があります。それを置き換えないでください。

aws wafv2 associate-web-acl --web-acl-arn "$ACL_ARN" --resource-arn "$STAGE_ARN"
aws wafv2 get-web-acl-for-resource \
  --resource-arn "$STAGE_ARN" \
  --query WebACL.Name \
  --output text

labex-sec05-owned-export が返るはずです。/internal/ に一致しない公開ヘルスチェックパスをテストし、次に内部エクスポートパスをテストします。

curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/health
curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/internal/export

ヘルスチェックは HTTP 200、エクスポートは HTTP 403 と block-private-export を返すはずです。Block の判定は、バックエンド実行前にレスポンスを返します。AWS View には、ヘルスチェックリクエストに Executed、ブロックされたリクエストに Not reached が表示される必要があります。ネイティブな関連付けだけでは不十分です。これらの実際の HTTP 結果が、制御の適用を証明します。

AWS View の例:ヘルスチェックはバックエンドに到達し、内部エクスポートは実行前にブロックされる

ルールを更新して実際の動作を観察する

このステップでは、保護対象を admin 接頭辞へ移し、アプリケーションを再起動せずに動作の変化を示します。Web ACL の更新は、ルール一覧を置き換えます。ロックトークンは、同時に行われた変更の上書きを防ぎます。更新の直前に現在のネイティブな設定から取得し、変数に保持してください。

前のファイルの URI 接頭辞を置き換えて、新しいルールドキュメントを作成します。sed はテキストを変換し、> は元のルールドキュメントを保持したまま新しいファイルを書き込みます。

sed 's|/internal/|/admin/|' rules-internal.json > rules-admin.json
LOCK_TOKEN=$(aws wafv2 get-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --query LockToken \
  --output text)
aws wafv2 update-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --lock-token "$LOCK_TOKEN" \
  --default-action Allow={} \
  --visibility-config file://visibility.json \
  --rules file://rules-admin.json \
  --cli-binary-format raw-in-base64-out \
  --query NextLockToken \
  --output text

返されたロックトークンは、新しい ACL リビジョンを識別します。認証情報ではありません。以前の internal 接頭辞はデフォルト Allow アクションで通過し、admin エクスポートはブロックされるはずです。

curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/internal/export
curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/admin/export

HTTP 200、次に HTTP 403 が返るはずです。AWS View には、新しい /admin/ 接頭辞、以前にブロックされた internal リクエスト、現在の許可された internal とブロックされた admin の組が表示されるはずです。リクエストは現在のネイティブなルールを使うため、VM やアプリケーションの再起動は不要です。この限定された環境では、未対応のポリシー型は安全側に倒して拒否されるため、ここで学んだステートメントだけを使ってください。

接頭辞更新後の AWS View の例:internal エクスポートは許可され、admin エクスポートはブロックされる

所有 ACL だけを関連付け解除して削除する

このステップでは、ポリシーを削除し、通常のアプリケーションルーティングに戻ることを確認します。先に前のステップの機能確認を終えてください。関連付け解除は、このステージでの制御の適用を取り除き、その後 ACL を削除すると所有ポリシーリソースも削除されます。

対象ステージから自分の Web ACL の関連付けを解除し、成功したネイティブなクエリで関連付けを確認します。認証エラーを削除の証拠として扱うのではなく、関連付けが空であることを確認します。

aws wafv2 disassociate-web-acl --resource-arn "$STAGE_ARN"
aws wafv2 get-web-acl-for-resource --resource-arn "$STAGE_ARN" --query WebACL

以前にブロックされた admin エクスポートは、提供されたバックエンドに再び到達するはずです。

curl -sS -w '\nHTTP %{http_code}\n' http://127.0.0.1:8090/admin/export

HTTP 200 が返るはずです。現在のロックトークンを取得し、所有 ACL だけを削除して、残る ACL の一覧を正常に取得します。所有する名前は存在せず、labex-sec05-reference が残る必要があります。

LOCK_TOKEN=$(aws wafv2 get-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --query LockToken \
  --output text)
aws wafv2 delete-web-acl \
  --name labex-sec05-owned-export \
  --scope REGIONAL \
  --id "$ACL_ID" \
  --lock-token "$LOCK_TOKEN"
aws wafv2 list-web-acls --scope REGIONAL --query 'WebACLs[].Name'

提供された両方の API ステージと参照 ACL をそのまま保持してください。ローカルのルールドキュメントと可視性ドキュメントを削除し、使い捨て CLI プロファイルを削除する前に、このステップの検証を実行します。

rm -f rules-internal.json rules-admin.json visibility.json
rm -f /home/labex/.aws/credentials /home/labex/.aws/config
unset STAGE_ARN ACL_ID ACL_ARN LOCK_TOKEN

まとめ

Regional Web ACL を作成して REST API ステージに関連付け、実際の HTTP の許可・ブロック動作をテストしました。一致するリクエストはバックエンドの前で停止し、ヘルスチェックリクエストは通過し続けました。URI 接頭辞の更新で稼働中のルーティングが変わり、関連付け解除でエンドポイントが復元されました。所有 ACL だけを削除し、提供されたステージと参照ポリシーを保持しました。