この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWSチームのすずきです。
Log4jの脆弱性を緩和するため、AWSのマネージドルールに含まれる「Log4JRCE」のみ利用するAWS WAF、 CloudFormatinで設置する機会がありましたので紹介させて頂きます。
WAF設定
WebACL
- ELB(ALB)の保護で利用するため、「Scope: REGIONAL」としました。
- AWSのマネージドルール「AWSManagedRulesKnownBadInputsRuleSet」の最新バージョンを利用する指定を行いました。
- 誤検知による副作用を最小化するため、「Log4JRCE」以外のルールに該当したリクエストは受け入れる除外設定を行いました。
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: !Sub '${AWS::StackName}-webacl'
DefaultAction:
Allow: {}
Description: AWS WAFv2 WebACL with only Log4JRCE
Scope: REGIONAL
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub '${AWS::StackName}'
SampledRequestsEnabled: false
Rules:
- Name: AWS-AWSManagedRulesKnownBadInputsRuleSet
Priority: 1
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesKnownBadInputsRuleSet
ExcludedRules:
- Name: Host_localhost_HEADER
- Name: PROPFIND_METHOD
- Name: ExploitablePaths_URIPATH
OverrideAction:
None: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub '${AWS::StackName}-AWSManagedRulesKnownBadInputsRuleSet'
SampledRequestsEnabled: true
LoggingConfiguration
- AWS WAFログの出力先は、S3へ直接出力する設定としました。
- S3費用を抑制するためフィルタリング設定を実施、「BLOCK」ログのみ保存する設定としました。
LoggingConfiguration:
Type: AWS::WAFv2::LoggingConfiguration
DependsOn: S3Bucket
Properties:
ResourceArn: !GetAtt 'WebACL.Arn'
LogDestinationConfigs:
- !GetAtt 'S3Bucket.Arn'
LoggingFilter:
DefaultBehavior: DROP
Filters:
- Behavior: KEEP
Conditions:
- ActionCondition:
Action: BLOCK
Requirement: MEETS_ALL
S3設定
- AWS WAF出力先の要件を満たすため、「aws-waf-logs」で開始するバケット名としました。
- AWS WAFログの書き込み許可を付与するため、バケットポリシーを設定しました。
- ストレージコスト抑制のため、一定期間で削除を行うライフサイクル設定を行いました。
- ログに秘匿情報が含まれる可能性に考慮し、デフォルトの暗号化とパブリック公開を抑制する指定を行いました。
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'aws-waf-logs-${AWS::StackName}-${AWS::Region}-${AWS::AccountId}'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: ExpirationInDays
Status: Enabled
ExpirationInDays: 45
- Id: NoncurrentVersionExpiration
Status: Enabled
NoncurrentVersionExpirationInDays: 7
- Id: ExpiredObjectDeleteMarker
Status: Enabled
ExpirationInDays: 7
- Id: AbortIncompleteMultipartUpload
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 3
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref 'S3Bucket'
PolicyDocument:
Id: !Sub '${AWS::StackName}-BucketPolicy'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: delivery.logs.amazonaws.com
Action:
- s3:PutObject
Resource:
- !Sub 'arn:aws:s3:::${S3Bucket}/*'
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
- Sid: AWSLogDeliveryAclCheck
Effect: Allow
Principal:
Service: delivery.logs.amazonaws.com
Action:
- s3:GetBucketAcl
Resource:
- !Sub 'arn:aws:s3:::${S3Bucket}'
WAF有効化
AWSコンソールを利用、「Associated AWS resources」より、AWS WAFの保護を有効化しました。
保護対象のELB (ALB) を指定しました。
テスト
curlコマンドを利用して、AWS WAFによりブロックされる事を確認しました。
$ curl http:/waftest-00000.ap-northeast-1.elb.amazonaws.com -H 'User-Agent: ${jndi' -o /dev/null -w '%{http_code}\n' -s
403
ブロックログ確認
ログ出力先として指定したS3バケットに、以下のログがJSON形式で参照できました。
{
"timestamp": 1639205931267,
"formatVersion": 1,
"webaclId": "arn:aws:wafv2:ap-northeast-1:000000000000:regional/webacl/waf-webacl/0000-0000-0000-0000",
"terminatingRuleId": "AWS-AWSManagedRulesKnownBadInputsRuleSet",
"terminatingRuleType": "MANAGED_RULE_GROUP",
"action": "BLOCK",
"terminatingRuleMatchDetails": [],
"httpSourceName": "ALB",
"httpSourceId": "000000000000-app/waftest/000000000000",
"ruleGroupList": [
{
"ruleGroupId": "AWS#AWSManagedRulesKnownBadInputsRuleSet",
"terminatingRule": {
"ruleId": "Log4JRCE",
"action": "BLOCK",
"ruleMatchDetails": null
},
"nonTerminatingMatchingRules": [],
"excludedRules": null
}
],
"rateBasedRuleList": [],
"nonTerminatingMatchingRules": [],
"requestHeadersInserted": null,
"responseCodeSent": null,
"httpRequest": {
"clientIp": "0.0.0.0",
"country": "JP",
"headers": [
{
"name": "Host",
"value": "waftest-00000.ap-northeast-1.elb.amazonaws.com"
},
{
"name": "Accept",
"value": "*/*"
},
{
"name": "User-Agent",
"value": "${jndi"
}
],
"uri": "/",
"args": "",
"httpVersion": "HTTP/1.1",
"httpMethod": "GET",
"requestId": "1-0000-0000"
},
"labels": [
{
"name": "awswaf:managed:aws:known-bad-inputs:Log4JRCE"
}
]
}
まとめ
Log4jの脆弱性対策としてAWS WAFの利用を検討されている場合、今回のテンプレートをお試しください。
AWS WAFルールの誤判定が発生、副作用回避が必要となった場合には、S3に記録されたブロックログより詳細を確認を行い、IPアドレスなどを利用した除外設定をお試しください。
複数のマネージドルール利用、副作用の除外設定
サンプルテンプレート全文(YAML)
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS WAFv2 configuration for Log4JRCE blocking and S3 logging
Resources:
WebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: !Sub '${AWS::StackName}-webacl'
DefaultAction:
Allow: {}
Description: AWS WAFv2 WebACL with only Log4JRCE
Scope: REGIONAL
Tags:
- Key: StackName
Value: !Sub '${AWS::StackName}'
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub '${AWS::StackName}'
SampledRequestsEnabled: false
Rules:
- Name: AWS-AWSManagedRulesKnownBadInputsRuleSet
Priority: 1
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesKnownBadInputsRuleSet
ExcludedRules:
- Name: Host_localhost_HEADER
- Name: PROPFIND_METHOD
- Name: ExploitablePaths_URIPATH
OverrideAction:
None: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub '${AWS::StackName}-AWSManagedRulesKnownBadInputsRuleSet'
SampledRequestsEnabled: true
LoggingConfiguration:
Type: AWS::WAFv2::LoggingConfiguration
DependsOn: S3Bucket
Properties:
ResourceArn: !GetAtt 'WebACL.Arn'
LogDestinationConfigs:
- !GetAtt 'S3Bucket.Arn'
LoggingFilter:
DefaultBehavior: DROP
Filters:
- Behavior: KEEP
Conditions:
- ActionCondition:
Action: BLOCK
Requirement: MEETS_ALL
#- Behavior: KEEP
# Conditions:
# - ActionCondition:
# Action: COUNT
# Requirement: MEETS_ANY
# --------------------------------------------- #
# S3 bucket configuration for WAF log output
# --------------------------------------------- #
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'aws-waf-logs-${AWS::StackName}-${AWS::Region}-${AWS::AccountId}'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LifecycleConfiguration:
Rules:
- Id: ExpirationInDays
Status: Enabled
ExpirationInDays: 45
- Id: NoncurrentVersionExpiration
Status: Enabled
NoncurrentVersionExpirationInDays: 7
- Id: ExpiredObjectDeleteMarker
Status: Enabled
ExpirationInDays: 7
- Id: AbortIncompleteMultipartUpload
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 3
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: StackId
Value: !Sub '${AWS::StackId}'
VersioningConfiguration:
Status: Enabled
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref 'S3Bucket'
PolicyDocument:
Id: !Sub '${AWS::StackName}-BucketPolicy'
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: delivery.logs.amazonaws.com
Action:
- s3:PutObject
Resource:
- !Sub 'arn:aws:s3:::${S3Bucket}/*'
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
- Sid: AWSLogDeliveryAclCheck
Effect: Allow
Principal:
Service: delivery.logs.amazonaws.com
Action:
- s3:GetBucketAcl
Resource:
- !Sub 'arn:aws:s3:::${S3Bucket}'
Outputs:
WafAclArn:
Value: !GetAtt 'WebACL.Arn'
WafAclId:
Value: !GetAtt 'WebACL.Id'