亚马逊AWS官方博客

亚马逊云科技 WAF 部署小指南(一) WAF原理、默认部署及日志存储

什么是亚马逊云科技WAF?

亚马逊云科技WAF是一个网页应用的防火墙服务。它能帮助保护您的网页应用或网页API应对常见的网页攻击。这些攻击会影响应用的可用性,产生安全风险,或消耗过量的计算资源。

使用WAF是对网页应用增加深度防御的好办法。WAF 可以帮助应对类似SQL注入、CSS和其他常见的漏洞攻击。WAF允许您创建自己的定制规则在HTTP请求到达应用之前决定是阻断还是允许该请求。

使用curl来创建和发送HTTP请求。这些请求是用来测试WAF规则的。curl在Windows Subsystem for Linux (简称WSL)上可以使用。

如果你不确定,建议您使用亚马逊 Cloud9 环境来完成这个实验。Cloud9环境包含所有所需工具。Cloud9的默认设置就可以满足本次实验的要求。

内容简介:

本文分为五个步骤为您讲述如何创建带有WAF的网站环境,并把WAF的检查日志记录下来的最小架构。

步骤一: 创建WAF的Web ACL

步骤二: 创建一个测试用的Web服务器和ALB以测试WAF的防护效果。

步骤三: 把我们创建的Web ACL 与步骤二创建的ALB关联起来。以使WAF防护功能生效。

步骤四: 对WAF规则做一些常用的配置调整。

步骤五: 启用WAF日志,把日志记录进S3存储桶。并用Athena进行查询。

架构图:

步骤一 : 创建Web ACL

一个Web ACL (Web Access Control List) 是亚马逊云科技WAF的核心资源。他包含评估每个收到请求的原则(rules)。一个Web ACL 使用CloudFront发布点、API网关或者应用负载均衡器关联你的网页应用程序。

这个实验使用WAF v2,请确认你没有使用经典WAF (WAF Classic)。

托管规则

最快的方法来开始使用WAF是在WebACL里部署托管规则组(Managed Rule Group for WAF)

托管规则组是一组WAF规则,由亚马逊云科技或者亚马逊市场里的第三方厂商创建和维护。这些规则提供了对常见攻击的保护。或者针对特定应用类型的攻击。

每一个托管的规则组防御一类常见的攻击,如SQL或者命令行攻击。

亚马逊云科技提供了一系列可供选择的规则组。例如Amazon IP Reputation list,Known Bad Inputs 和 Core rule set

还有其他规则组可供使用。

下图在WebACL里加入了 亚马逊云科技的托管规则里的Core rule set (包含OWASP和常见CVE防御的托管规则)以及防范SQL injection的托管规则。

现在让我们来创建我们的第一个Web ACL- Sample-web-acl

在配置开始,我们需要了解亚马逊云科技的WAF WebACL 可以部署在两个位置,分别是Global和各个region。如下图所示:

Global的Web ACL会被部署到亚马逊云科技全球300多个PoP节点,使用一致的WAF防护策略与CloudFront结合来保护用户的源站。

Region的Web ACL在每个Region配置不同的Web ACL,用于保护同Region里的Web服务器。

* 如果您的Web ACL配置完以后找不到,很可能就是因为查看的不是当时创建Web ACL的Region。

现在我们点击Create web ACL来创建我们的第一个Web ACL:

在这里填写红框里的内容,注意Description如果不填写以后不能修改。在Resource type这里,选择CloudFront distribution,创建的是Global Web ACL. 如果选择Regional resource创建的就是Region Web ACL。

这里,如图所示,我们选择在US East Region创建一个Regional Web ACL。

点击Next

添加相关的规则,这里我们选择Web防护必用的四个托管规则:

分别是:

Amazon IP reputation list (来自亚马逊云科技安全情报的已知恶意IP列表)

Core rule set(包含跨站脚本攻击等OWASP攻击防护的策略)

Known bad inputs(包含已知的漏洞发现和利用防护-其中包含Log4j漏洞的相关防护)

SQL database(包含SQL注入攻击防护)


加好以后点击Next结果如下:

再点击Next可以设置各条规则的优先级:

这里我们建议把比较复杂的检查条件优先级安排在低优先位置,让恶意请求先经过简单条件的检查。比如Reputation List的检查只检查IP地址信息,就把它放在最先检查的位置。

点击Next设置CloudWatch metrics -这里保持默认即可:

然后检查之前配置的内容点击“Create web ACL”

几乎不用等待,您就可以得到一个名为“Sample-web-acl”,位于US East Region的Web ACL。

步骤二 创建一个测试用的Web服务器和ALB以测试服务效果。

直接用WAF保护生产环境里的Web服务器需要特别注意出现误杀的情况。因此这里我们先创建一个简单的测试用网页服务器。这里我们选用了一个echo-server的Docker镜像,创建一个httpecho的容器让它能够把我们通过http访问服务器的请求头都提取出来作为http 响应给我们的浏览器。

首先启用一个t2.micro的EC2,使用Amazon Linux2 AMI启动。

在此EC2上安装Docker,参考 https://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/docker-basics.html

具体Linux命令如下:

1.安装docker

sudo yum update -y

sudo amazon-linux-extras install docker

2.赋予ec2-user用户docker启动的linux权限

sudo usermod -a -G docker ec2-user

3.退出当前ssh session以使权限生效

logout

4.使用ec2-user再次login检查docker信息

sudo service docker start

docker info | grep Ver

输出如下即为Docker安装成功:

Server Version: 20.10.7
Cgroup Version: 1
Kernel Version: 4.14.47-64.38.amzn2.x86_64

5.运行echo-server docker image, 监听TCP 88端口。

docker run -d --name httpecho  -p 88:8080 jmalloc/echo-server httpecho

6.curl检查本地服务

curl 127.0.0.1:88

输出如下即表示服务正常。该输出表示echo-server服务器收到curl送来的http请求内容非常简单:

Request served by 16e51706efbe

HTTP/1.1 GET /

Host: 127.0.0.1:88
User-Agent: curl/7.79.1
Accept: /

7.在US East Region建立ALB

监听TCP 80端口,后端转发至EC2的TCP 88端口。

ALB配置完成后如下图:

ALB Target Group状态如图:

使用浏览器访问 http://httpechoalb-123456789.us-east-1.elb.amazonaws.com

得到如下结果,即表示ALB与EC2的工作正常。可以看到通过浏览器访问网页,我们的User-Agent字段内容是很长的。

步骤三 把我们创建的Web ACL – Sample-web-acl 与步骤二创建的ALB关联起来。

  1. 点击Web ACL里的Associated resources:

  1. 选择关联Application Load Balance。关联步骤二创建的httpechoalb。

  1. 关联完成,如果需要,在配置好证书的情况下,httpechoalb还可以完成https解密,然后由WAF进行网页请求的内容检查。

  1. 使用一个curl 命令模拟SQL injection测试,此处我们把User-Agent字段模拟成和浏览器一样,以绕过其他WAF检查条目:

curl --header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 Edg/97.0.1072.69' -X POST "httpechoalb-844137253.us-east-1.elb.amazonaws.com" -F "user='AND 1=1;"

输出如下,即表示WAF发现了恶意内容。予以阻断并回应http 403。

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

步骤四 对Web ACL做一些常用的调整。

1.在WAF规则列表前部增加限速规则。

在WAF防护的时候,我们会建议在WAF规则列表前部增加限速规则。

具体配置如下:

这里我们根据每个客户端访问的源IP进行限速,每个公网源IP访问的次数限定在每5分钟1000次以下。超过这个频率的请求将被过滤。具体大家可以根据自己应用的请求频率再做调整。有了这个限速规则,当外部使用HTTP泛洪进行攻击的时候,我们可以先把限速规则的访问速率降下来,先限制住请求数量最多的个别源IP,来恢复大多数正常用户的访问。

2.注意一些容易产生误杀的字段

例如: Core rule set里的SizeRestrictions_BODY子规则,经常会导致一些上传图片的请求被过滤。

此时可以根据业务情况把该条子规则设置为count模式。

  1. 设置检查白名单

对于需要设置白名单的请求,可以使用Scope Down(缩小检查范围)设置来让请求绕过检查。

具体设置如下:

注意Scope Down设置是当请求匹配过滤条件的时候必须进行检查。因此我们想让photo_upload这个uri path绕过检查的时候,选择的过滤条件是不匹配该条件(NOT)。从而达到不包含photo_upload uri path的请求需要检查。反之则不检查。

步骤五 启用日志

经过以上四个步骤,我们的WAF已经可以开始防护网页应用,打开WAF Overview的面板,我们可以看到一些经过WAF检查的Web请求。不过这里的请求,是帮助操作人员了解WAF的大致工作情况的。

WAF Overview里的数据是经过采样的。选择采样的请求来检查请求信息,里面并没有阻断请求的规则匹配细节。这对于我们日常的排查误杀的工作仍然不够用。

WAF Overview面板:

采样请求可以根据不同的WAF rule进行过滤

采样请求内容并不包含WAF匹配规则

由于WAF Overview里的数据是经过采样的,并且历史的访问记录并不归档。因此如果我们需要使用WAF完成安全合规的任务,或者做误杀排查工作,就必须要启用WAF日志功能。

WAF日志搜集和处理有多种选择。我们这里先介绍成本最低廉的S3日志存储方式。这种方法适用于仅把WAF日志作为访问记录存储,日常不会经常查询和做安全事件调查的组织。

  1. 启用WAF日志:

在日志记录选择方面,我们选择直接把日志放入S3 bucket.

然后我们需要创建一个S3桶,注意桶名必须以aws-waf-logs-开始。否则我们在下拉框里就看不到这个新建的桶。

完成后点击Next, WAF日志就会随后送到这个指定的桶了。

要查看WAF日志,我们可以去S3桶查看。如下图所示,可以看到waf日志根据每个Web ACL的名字分别做了子目录。因此多个Web ACL可以放在同一个S3桶里,并不会冲突。

*日志直接记录入S3桶的方法成本最低。但是日志的时延稍高,新的日志被记录S3桶通常需要5分钟左右的时间。

想要查询S3桶里的日志。我们可以使用athena来直接对S3桶的数据进行查询。

首先,使用下面的athena命令创建一个WAF日志表:

CREATE EXTERNAL TABLE `webacls3log_partition`(
  `timestamp` bigint,
  `formatversion` int,
  `webaclid` string,
  `terminatingruleid` string,
  `terminatingruletype` string,
  `action` string,
  `terminatingrulematchdetails` array<
                                  struct<
                                    conditiontype:string,
                                    location:string,
                                    matcheddata:array<string>
                                        >
                                     >,
  `httpsourcename` string,
  `httpsourceid` string,
  `rulegrouplist` array<
                     struct<
                        rulegroupid:string,
                        terminatingrule:struct<
                           ruleid:string,
                           action:string,
                           rulematchdetails:string
                                               >,
                        nonterminatingmatchingrules:array<
                                                       struct<
                                                          ruleid:string,
                                                          action:string,
                                                          rulematchdetails:array<
                                                               struct<
                                                                  conditiontype:string,
                                                                  location:string,
                                                                  matcheddata:array<string>
                                                                     >
                                                                  >
                                                               >
                                                            >,
                        excludedrules:array<
                                         struct<
                                            ruleid:string,
                                            exclusiontype:string
                                               >
                                            >
                           >
                       >,
  `ratebasedrulelist` array<
                        struct<
                          ratebasedruleid:string,
                          limitkey:string,
                          maxrateallowed:int                              >
                           >,
  `nonterminatingmatchingrules` array<
                                  struct<
                                    ruleid:string,
                                    action:string
                                        >
                                     >,
  `requestheadersinserted` string,
  `responsecodesent` string,
  `httprequest` struct<
                      clientip:string,
                      country:string,
                      headers:array<
                                struct<
                                  name:string,
                                  value:string
                                      >
                                   >,
                      uri:string,
                      args:string,
                      httpversion:string,
                      httpmethod:string,
                      requestid:string
                      >,
  `labels` array<
             struct<
               name:string
                   >
                  >
)
PARTITIONED BY(
 day STRING
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'LOCATION 's3://aws-waf-logs-awmbkt/AWSLogs/867186585348/WAFLogs/us-east-1/webacls3log/'TBLPROPERTIES
(
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.range" = "2021/01/01,NOW",
 "projection.day.format" = "yyyy/MM/dd",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "storage.location.template" = "s3://aws-waf-logs-awmbkt/AWSLogs/123456789012/WAFLogs/us-east-1/webacls3log/${day}"
)

然后就可以使用SQL语句来进行日志查询了:

这里我们举两个例子:

搜索前5条waf日志,列出日志里所有字段

select from_unixtime(timestamp/1000) as datetime,* from webacls3log_partition
limit 5

输出结果:

搜索特定时间范围的日志并按照clientip做汇总:

WITH test_dataset AS  (SELECT     format_datetime(from_unixtime((timestamp/1000) - ((minute(from_unixtime(timestamp / 1000))%5) * 60)),'yyyy-MM-dd HH:mm') AS five_minutes_ts,
     "httprequest"."clientip"
     FROM default.webacls3log_partition
     WHERE day >= '2021/03/01' AND day < '2022/03/31')
SELECT five_minutes_ts,"clientip",count(*) ip_countFROM test_dataset
GROUP BY five_minutes_ts,"clientip"

关于WAF直接使用S3存储日志并使用Athena做查询的更多介绍可以在这里找到:

https://docs.aws.amazon.com/zh_cn/athena/latest/ug/waf-logs.html#query-examples-waf-logs

小结

通过以上五个步骤,我们完成了亚马逊云科技WAF的启用和配置的工作。并且把WAF日志存储在S3桶,使用Athena进行历史日志的查询。可以满足基本的归档查询需要。本文介绍的WAF防护的Web ACL和Rule 可以作为一个生产环境的WAF防护策略。达到OWASP和SQL注入等攻击防护的要求。此外,本方案通过使用S3存储桶记录WAF日志,保证用户可以存储合乎行业和法规要求时间长度的日志。并可以通过Athena进行类SQL语言的查询来回溯日志。对于WAF运营有更高要求的用户,可以参考WAF部署小指南的后续文章,选择更适合您的方案。

如果您对本文有任何问题,请在此处留言。

亚马逊云科技 WAF 部署小指南系列文章

 

本篇作者

崔俊杰

亚马逊云科技解决方案架构师,负责亚马逊云科技边缘云安全相关的服务产品。为亚马逊云用户提供DDoS防御/网站前端安全防御/域名安全相关的产品咨询。对Cloudfront, Shield, WAF, Route53,Global Accelerator等边缘云安全相关产品有深入了解。在计算机安全,数据中心和网络领域有多年的工作经验。

叶明

亚马逊云科技边缘产品架构师,负责CloudFront和Global Accelerator服务在中国和全球的市场拓展,专注于互联网用户访问云上服务的感受的优化以及数据洞察。在互联网基础设施领域有丰富的实践经验。