CloudFormationを使用して、AWS WAFを作成して、XSSを防ぐ
AWS WAFでは多くの攻撃からアプリケーションを防御するためのルールを作成することができます。
本ページではWAFを使用して、XSS(Cross Site Scripting)用のルールを作成します。
XSS (クロスサイトスクリプティング) 攻撃ステートメントは、ウェブリクエストコンポーネント内の悪意のあるスクリプトを検査します。XSS 攻撃では、攻撃者は、悪意のあるクライアントサイトスクリプトを他の正当なウェブブラウザに挿入するための手段として、悪意のないウェブサイトの脆弱性を利用します。
クロスサイトスクリプティング攻撃ルールステートメント
特に今回取り上げるXSSは頻繁に用いられる攻撃手法の1つです。
WAFを使用することで、XSS等の攻撃からアプリケーションを防御し、アプリケーションのセキュリティを向上させることができます。
構築する環境
REST APIタイプのAPI Gatewayを作成します。
API Gatewayには2つのステージを作成します。
API Gatewayの全面にWAFを配置します。
XSS用ルールを作成します。
検査対象はクエリ文字列とします。
一方のAPI GatewayステージにWAFを適用します。
API GatewayのバックエンドリソースとしてLambda関数を指定します。
Lambda関数のランタイム環境はPython3.12とします。
検証のシナリオ
2つのステージに対して、以下のパターンでリクエストします。
- 通常のクエリ文字列を含むリクエスト
- XSS的なクエリ文字列を含むリクエスト
なお今回の検証は以下のページを参考に進めました。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
テンプレートファイルのポイント解説
クエリ文字列を扱うLambda関数
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Architectures:
- !Ref Architecture
Code:
ZipFile: |
import os
ID = os.environ['ID']
html = '''
<!DOCTYPE html>
<html lang="ja">
<head>
<title>{id}</title>
<meta charset="utf-8"/>
</head>
<body>
<h1>{id}</h1>
{query}
</body>
</html>'''
def lambda_handler(event, context):
query = event['queryStringParameters']['query']
return {
'statusCode': 200,
'headers': {'Content-Type': 'text/html'},
'body': html.format(id=ID, query=query)
}
Environment:
Variables:
ID: !Ref Prefix
FunctionName: !Sub "${Prefix}-function"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
実行するコードをインライン形式で記述する形でLambda関数を作成します。
CloudFormationを使用してLambda関数を作成する方法に関しては、以下のページをご確認ください。
ポイントは、実行するコード内でクエリ文字列を取得し、それをHTML用文字列に埋め込んでいる点です。
これはクエリ文字列で渡された値を確認することを意図しています。
この関数はContent-Typeに「text/html」を設定した上で、作成されたHTML用文字列を返しますから、動的なHTMLファイルを返すことになります。
API Gatewayではテスト用に2つのステージを作成する
Resources:
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
EndpointConfiguration:
Types:
- EDGE
Name: !Ref Prefix
Deployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- Method
Properties:
RestApiId: !Ref RestApi
Resource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt RestApi.RootResourceId
PathPart: !Sub "${Prefix}-resource"
RestApiId: !Ref RestApi
Stage1:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref Deployment
RestApiId: !Ref RestApi
StageName: !Sub "${Prefix}-${StageName1}"
Stage2:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref Deployment
RestApiId: !Ref RestApi
StageName: !Sub "${Prefix}-${StageName2}"
Method:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
ConnectionType: INTERNET
Credentials: !GetAtt ApiGatewayRole.Arn
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionArn}/invocations"
ResourceId: !Ref Resource
RestApiId: !Ref RestApi
ApiGatewayRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- apigateway.amazonaws.com
Policies:
- PolicyName: !Sub "${Prefix}-InvokeFunctionPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Ref FunctionArn
Code language: YAML (yaml)
API Gatewayにはいくつか種類がありますが、WAFを適用できるタイプはREST APIタイプです。
ですから今回は同タイプのAPI Gatewayを作成します。
REST APIタイプのAPI Gatewayに関する基本的な事項については、以下のページをご確認ください。
ポイントはREST APIタイプのAPI Gatewayを構成するリソースの1つであるステージです。
今回は以下の2つのステージを作成します。
- saa-03-006-stage1
- saa-03-006-stage2
一方は後述のXSS用ルールを含むWAFを適用します。
XSS用ルール作成時はキャパシティに注意
Resources:
RuleGroup:
Type: AWS::WAFv2::RuleGroup
Properties:
Capacity: 90
Name: !Sub "${Prefix}-XssRuleGroup"
Rules:
- Action:
Block: {}
Name: !Sub "${Prefix}-XssRule"
Priority: 0
Statement:
XssMatchStatement:
FieldToMatch:
QueryString: {}
TextTransformations:
- Priority: 0
Type: NONE
- Priority: 1
Type: LOWERCASE
- Priority: 2
Type: HTML_ENTITY_DECODE
- Priority: 3
Type: COMPRESS_WHITE_SPACE
- Priority: 4
Type: CMD_LINE
- Priority: 5
Type: URL_DECODE
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-XssRule"
SampledRequestsEnabled: false
Scope: REGIONAL
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-XssRuleGroup"
SampledRequestsEnabled: false
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
DefaultAction:
Allow: {}
Name: !Sub "${Prefix}-WebACL"
Rules:
- Name: !Sub "${Prefix}-WebACL-Xss"
OverrideAction:
None: {}
Priority: 0
Statement:
RuleGroupReferenceStatement:
Arn: !GetAtt RuleGroup.Arn
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-WebACL-Xss"
SampledRequestsEnabled: false
Scope: REGIONAL
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Ref Prefix
SampledRequestsEnabled: false
WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
ResourceArn: !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/${RestApi}/stages/${Stage2}"
WebACLArn: !GetAtt WebACL.Arn
Code language: YAML (yaml)
XSS用ルールを含むWAFを作成します。
WAFに関する基本的な事項は以下のページをご参照ください。
またAPI GatewayにWAFを適用する方法については、以下のページもご確認ください。
ポイントは2点です。
1点目はXssMatchStatementプロパティです。WAFでは様々な攻撃用のルールを作成することができますが、XSS用のルールを作成する場合は、このプロパティを使用します。
今回はクエリ文字列を検査の対象としますので、FieldToMatchプロパティにQueryStringを指定します。
TextTransformationsプロパティで検査対象のテキスト変換に関する設定が行えます。詳細は以下のページに詳しいですが、シナリオの項目でご紹介したページに従い、6つの項目を指定しています。
今回の設定をまとめると、クエリ文字列を以下の順番で検査することになります。
- 何も変換せずに検査する。
- 大文字を全て小文字に変換した上で検査する。
- HTMLエンコードされた文字列を変換した上で検査する。
- タブや改行コード等を空白文字列に変換した上で検査する。
- OSのコマンドラインに関する文字列を変換した上で検査する。
- URLエンコードされた文字列を変換した上で検査する。
2点目のポイントはキャパシティです。
XSSルールに関するキャパシティは以下の通りに説明されています。
WCU- 40 WCU (基本コストとして)。All query parameters のリクエストコンポーネントを使用する場合、10 WCU を追加します。[JSON body] (JSON 本文) のリクエストコンポーネントを使用する場合、基本コストの WCU を倍増させます。適用する各テキスト変換について、10 WCU を追加します。
クロスサイトスクリプティング攻撃ルールステートメント
クエリ文字列を対象としたXSSルールを作成し、1つのテキスト変換を定義した時点でWCUを40消費します。そしてさらに5つのテキスト変換を追加していますから、10 * 5 = 50WCUも消費します。ですから合計40 + 50 = 90WCUとなります。Capacityプロパティでは、90以上を指定する必要があることに注意してください。
WebACLAssociationリソースを作成して、このWAFをAPI Gatewayに関連付けます。
この時にステージ2(saa-03-006-stage2)を指定します。
つまり同じAPI Gatewayでもステージ1の方はWAFが適用されず、ステージ2のみWAFが適用されるということになります。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
AWS Management ConsoleからAPI Gatewayを確認します。
確かに1つのAPI Gatewayに2つのステージが作成されています。
一方はWAFが適用されており、もう一方は適用されていません。
WAFを確認します。
正常にWAFが作成されています。
ルールを見ると、確かにXSSに関するルールが用意されています。
動作確認
準備が整いましたので、2つのステージに対してアクセスします。
通常のクエリ文字列
まず通常のクエリ文字列を含むリクエストを送ります。
両ステージへのリクエストに対して、正常にレスポンスがありました。
つまり正常なクエリ文字列を含むリクエストであれば、WAFによってブロックされることなく、正常に通過できるということです。
XSS的なクエリ文字列
続いて両ステージに対してXSS的なクエリ文字列を含むリクエストを送ります。具体的にはscriptタグを含む文字列で、cookieの内容を出力する内容です。
WAFが適用されていないステージ1の方ではダイアログが表示されました。(Cookieが空だったため具体的には何も出力されませんでしたが。)
対してWAFが適用されているステージ2の方では「Forbidden」が表示されました。
つまりWAFによってXSS的な文字列を含むリクエストはブロックされたということです。
WAFのコンソール画面
最後にWAFのコンソール画面を確認します。
リクエストが2回あり、内1回はブロックされていることがわかります。
グラフを見ると、XSSによる攻撃があり、これがこのWAFによってブロックされたことがわかりますね。
まとめ
WAFを使用して、XSS(Cross Site Scripting)攻撃用のルールを作成しました。
WAFを使用することでXSS等の攻撃からアプリケーションを防御し、アプリケーションのセキュリティを向上させることができます。