65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 CloudFormation 创建区域 Web ACL (WAFv2)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2022 年 2 月 21 日

CPOL

4分钟阅读

viewsIcon

6686

使用 CloudFormation 创建区域 Web ACL

引言

AWS WAF 是一项 Web 应用程序防火墙服务,可让您监控转发到 Amazon CloudFront 分配、Amazon API Gateway REST API、Application Load Balancer 或 AWS AppSync GraphQL API 的 Web 请求。本文档描述了一个 CloudFormation 模板,该模板针对使用 Application Load Balancer 为公共网站提供服务,但要阻止攻击者的请求并防范 OWASP Top 10 安全风险的场景创建 WAF 资源。提供的模板可以轻松地改编到其他使用场景。

本文是关于如何创建带 WAF 的 Elastic Beanstalk 应用程序系列文章的一部分。

背景

解决方案使用 CloudFormation、S3、WAF v2、Web ACL、CloudWatch、ALB。

任务

要为 ALB 设置 AWS WAF,我们需要创建 Web ACL、日志记录配置以及 Web ACL 和 Application Load Balancer (ALB) 之间的关联等资源。Application Load Balancer 是作为另一个脚本的一部分创建的,因此其 ARN 作为输入参数提供。

解决方案

CloudFormation 模板具有以下结构

  1. 输入参数:资源名称的通用部分和应用程序负载均衡器 ARN;
  2. 资源:Web ACL、CloudWatch 日志组、日志记录配置和关联;
  3. 输出值:Web ACL ARN 和 CloudWatch 日志组 ARN。
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "CloudFormation template defines Web ACL resources",
  "Metadata": {
    "AWS::CloudFormation::Interface": {
      "ParameterGroups": [
        {
          "Label": {
            "default": "Resources"
          },
          "Parameters": [
            "albARN"
          ]
        },
        {
          "Label": {
            "default": "Names"
          },
          "Parameters": [
            "tagName",
            "tagNamePrefix"
          ]
        }
      ],
      "ParameterLabels": {
        "albARN": {
          "default": "ALB ARN"
        },
        "tagName": {
          "default": "Name Tag"
        },
        "tagNamePrefix": {
          "default": "Name Prefix"
        }
      }
    }
  },
  "Parameters": {
    "albARN": {
      "Description": "ARN for the Application Load Balancer",
      "Type": "String",
      "MinLength": "30",
      "MaxLength": "180",
      "Default": "arn:aws:elasticloadbalancing:us-west-1:123456789012:
                  loadbalancer/app/load-balancer-EXAMPLE/0123456789abcdef",
      "AllowedPattern": "^arn:(aws[a-zA-Z-]*)?:elasticloadbalancing:[a-z]{2}
      ((-gov)|(-iso(b?)))?-[a-z]+-\\d{1}:\\d{12}:loadbalancer/app/([a-zA-Z0-9-/]{5,100})$",
      "ConstraintDescription": "must be a valid ARN of Application Load Balancer."
    },
    "tagName": {
      "Type": "String",
      "Description": "Name tag value",
      "MinLength": "5",
      "MaxLength": "25",
      "Default": "Default"
    },
    "tagNamePrefix": {
      "Description": "The prefix for use in Name tag values",
      "Type": "String",
      "MinLength": "5",
      "MaxLength": "25",
      "Default": "default"
    }
  },
  "Resources": {
    "webAcl": {
      "Type": "AWS::WAFv2::WebACL",
      "Properties": {
        "Description": "Web ACL for Application Load Balancer of Elastic Beanstalk",
        "Name": {
          "Fn::Sub": "${tagNamePrefix}-web-owasp"
        },
        "DefaultAction": {
          "Allow": {}
        },
        "Rules": [
          {
            "Name": "AWS-CRS",
            "Priority": 0,
            "Statement": {
              "ManagedRuleGroupStatement": {
                "VendorName": "AWS",
                "Name": "AWSManagedRulesCommonRuleSet",
                "ExcludedRules": []
              }
            },
            "OverrideAction": {
              "None": {}
            },
            "VisibilityConfig": {
              "SampledRequestsEnabled": true,
              "CloudWatchMetricsEnabled": true,
              "MetricName": {
                "Fn::Sub": "${tagNamePrefix}-aws-crs-metric"
              }
            }
          },
          {
            "Name": "Bad-Inputs",
            "Priority": 1,
            "Statement": {
              "ManagedRuleGroupStatement": {
                "VendorName": "AWS",
                "Name": "AWSManagedRulesKnownBadInputsRuleSet",
                "ExcludedRules": []
              }
            },
            "OverrideAction": {
              "None": {}
            },
            "VisibilityConfig": {
              "SampledRequestsEnabled": true,
              "CloudWatchMetricsEnabled": true,
              "MetricName": {
                "Fn::Sub": "${tagNamePrefix}-bad-inputs-metric"
              }
            }
          },
          {
            "Name": "Anonymous-IpList",
            "Priority": 2,
            "Statement": {
              "ManagedRuleGroupStatement": {
                "VendorName": "AWS",
                "Name": "AWSManagedRulesAnonymousIpList",
                "ExcludedRules": []
              }
            },
            "OverrideAction": {
              "None": {}
            },
            "VisibilityConfig": {
              "SampledRequestsEnabled": true,
              "CloudWatchMetricsEnabled": true,
              "MetricName": {
                "Fn::Sub": "${tagNamePrefix}-anonymous-iplist-metric"
              }
            }
          },
          {
            "Name": "Windows-RuleSet",
            "Priority": 3,
            "Statement": {
              "ManagedRuleGroupStatement": {
                "VendorName": "AWS",
                "Name": "AWSManagedRulesWindowsRuleSet"
              }
            },
            "OverrideAction": {
              "None": {}
            },
            "VisibilityConfig": {
              "SampledRequestsEnabled": true,
              "CloudWatchMetricsEnabled": true,
              "MetricName": {
                "Fn::Sub": "${tagNamePrefix}-windows-ruleset-metric"
              }
            }
          },
          {
            "Name": "SQLInject-RuleSet",
            "Priority": 4,
            "Statement": {
              "ManagedRuleGroupStatement": {
                "VendorName": "AWS",
                "Name": "AWSManagedRulesSQLiRuleSet"
              }
            },
            "OverrideAction": {
              "None": {}
            },
            "VisibilityConfig": {
              "SampledRequestsEnabled": true,
              "CloudWatchMetricsEnabled": true,
              "MetricName": {
                "Fn::Sub": "${tagNamePrefix}-SQLinjection-ruleset-metric"
              }
            }
          }
        ],
        "Scope": "REGIONAL",
        "Tags": [
          {
            "Key": "Name",
            "Value": {
              "Fn::Sub": "${tagName} OWASP Web ACL"
            }
          }
        ],
        "VisibilityConfig": {
          "SampledRequestsEnabled": true,
          "CloudWatchMetricsEnabled": true,
          "MetricName": {
            "Fn::Sub": "${tagNamePrefix}-web-owasp-metric"
          }
        }
      }
    },
    "cloudwatchLogsGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": {
          "Fn::Sub": "aws-waf-logs-${tagNamePrefix}-web-owasp"
        },
        "RetentionInDays": 180
      }
    },
    "webAcllogging": {
      "Type": "AWS::WAFv2::LoggingConfiguration",
      "Properties": {
        "ResourceArn": {
          "Fn::GetAtt": [
            "webAcl",
            "Arn"
          ]
        },
        "LogDestinationConfigs": [
          {
            "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:
                        log-group:aws-waf-logs-${tagNamePrefix}-web-owasp"
          }
        ],
        "LoggingFilter": {
          "DefaultBehavior": "KEEP",
          "Filters": [
            {
              "Behavior": "KEEP",
              "Conditions": [
                {
                  "ActionCondition": {
                    "Action": "BLOCK"
                  }
                }
              ],
              "Requirement": "MEETS_ANY"
            }
          ]
        },
        "RedactedFields": [
          {
            "SingleHeader": {
              "Name": "password"
            }
          }
        ]
      }
    },
    "albWebACLAssociation": {
      "Type": "AWS::WAFv2::WebACLAssociation",
      "Properties": {
        "ResourceArn": {
          "Ref": "albARN"
        },
        "WebACLArn": {
          "Fn::GetAtt": [
            "webAcl",
            "Arn"
          ]
        }
      }
    }
  },
  "Outputs": {
    "OWASPWebAclARN": {
      "Description": "ARN of WebACL",
      "Value": {
        "Fn::GetAtt": [
          "webAcl",
          "Arn"
        ]
      }
    },
    "CloudwatchLogsGroupARN": {
      "Description": "ARN of CloudWatch Logs Group",
      "Value": {
        "Fn::GetAtt": [
          "cloudwatchLogsGroup",
          "Arn"
        ]
      }
    }
  }
}

Web ACL

Web ACL 可以使用自定义或托管规则集,并在 AWS marketplace 上购买。由于本文档不涉及如何设置自定义规则集,因此 webAcl 资源使用 AWS 托管规则规则组,这些规则组可防范各种安全风险,包括来自 OWASP Top 10 列表的风险。此外,ALB 后面的 Elastic Beanstalk 应用程序是 .NET Framework Web 应用程序,在 Windows Server 实例上运行,因此 Web ACL 使用以下规则集

  • 核心规则集 AWSManagedRulesCommonRuleSet 包含通常适用于 Web 应用程序的规则。这可以防范广泛漏洞的利用,包括 OWASP 出版物中描述的漏洞。
  • 已知不良输入 AWSManagedRulesKnownBadInputsRuleSet 包含阻止已知无效且与漏洞利用或发现相关的请求模式的规则。这有助于降低恶意行为者发现易受攻击应用程序的风险。
  • 匿名 IP 列表 AWSManagedRulesAnonymousIpList 包含阻止允许隐藏查看者身份的服务请求的规则。这包括来自 VPN、代理、Tor 节点和托管提供商的请求。如果您想过滤掉可能试图隐藏身份的查看者,这将非常有用。
  • 管理员保护托管规则组 AWSManagedRulesAdminProtectionRuleSet 包含阻止外部访问暴露的管理页面的规则。如果您正在运行第三方软件或想降低恶意行为者获得应用程序管理访问权限的风险,这可能会很有用。
  • Windows 操作系统 AWSManagedRulesWindowsRuleSet 包含阻止与利用特定于 Windows 的漏洞(例如 PowerShell 命令)相关的请求模式的规则。这有助于防止允许攻击者运行未经授权的命令或执行恶意代码的利用。
  • SQL 数据库 AWSManagedRulesSQLiRuleSet 包含允许您阻止与 SQL 数据库利用(如 SQL 注入攻击)相关的请求模式的规则。这有助于防止远程注入未经授权的查询。

总共提供了 1350 个 WCU,低于允许的最大值 1500 个 WCU。规则集按优先级排序,未排除任何子集。

CloudWatch 日志组

CloudWatch 日志组定义在第 197-205 行。请注意,其名称应以 aws-waf-logs- 开头,否则 Web ACL 不会接受日志组作为有效的日志目标。我们使用中等的保留时间,等于 6 个月,但您可以根据您的任务使用任何适合的值。

日志记录配置

日志记录配置定义为 AWS::WAFv2::LoggingConfiguration 资源,它有四个属性:ResourceArnLogDestinationConfigsLoggingFilterRedactedFieldsResourceArn 是 Web ACL 的 ARN,它引用 webACL 的 ARN 属性。类似地,LogDestinationConfigs 是 CloudWatch 日志组的 ARN,预期它应引用 cloudwatchLogsGroup 的 ARN 属性,如下所示:

        "LogDestinationConfigs": [
          {
            "Fn::GetAtt": [
              "cloudwatchLogsGroup",
              "Arn"
            ]
          }
        ],

在这种情况下,堆栈创建失败并返回错误消息。

Resource handler returned message: "Error reason: The ARN isn't valid. 
A valid ARN begins with arn: and includes other information separated by colons or slashes., 
field: LOG_DESTINATION, parameter: arn:aws:logs:us-west-1:123456789012:log-group:
aws-waf-logs-default-web-owasp:* (Service: Wafv2, Status Code: 400, 
Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, Extended Request ID: null)" 
(RequestToken: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, HandlerErrorCode: InvalidRequest)

不幸的是,该消息并不具有信息量,实际上是指 cloudwatchLogsGroup 的 ARN 等于

arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:aws-waf-logs-${tagNamePrefix}-web-owasp/*

其末尾是 /*,这在有效的 ARN 中是不可接受的。为了解决这个问题,cloudwatchLogsGroup 的 ARN 在第 217 行硬编码,其中语句引用了 伪参数,如 AWS RegionAccountId

{
    "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:
                log-group:aws-waf-logs-${tagNamePrefix}-web-owasp"
}

LoggingFilter 定义所有被阻止的请求都写入 CloudWatch 日志组,而 RedactedFields 定义除了 password 标头外,所有请求数据都被记录。

AWS 控制台

CloudFormation 堆栈可以通过多种方式创建,在本篇文章中,我们使用 AWS 控制台。该堆栈不需要任何额外的属性和功能,因此过程非常简单。

创建堆栈

设置堆栈参数

堆栈选项

堆栈失败

创建完成

Web ACL
CloudFormation 堆栈

1. 所有使用的 IP 地址、服务器名称、工作站、域均为虚构,仅用于演示目的。
2. 信息按“原样”提供。

© . All rights reserved.