最新消息



雲端技能學習

【雲端技能學習】使用 AWS OpenSearch 分析 WAF Logs

使用 AWS OpenSearch 分析 WAF Logs

 

 

什麼是 AWS WAF

AWS WAF 是一個網頁應用的防火牆服務,能夠保護您的網頁應用程式或網頁 API 免於常見的網頁攻擊。這些攻擊可能會導致應用程式無法使用、造成安全風險或耗盡大量計算資源。

使用 WAF 是增加網頁應用程式深度防禦的好方法。WAF 可以幫助避免 SQL Injection、CSS 和其他常見的漏洞攻擊。允許您建立自己的定制規則,以在 HTTP 請求到達應用程式之前決定阻擋還是允許該請求。

架構圖:

Step 1. 建立 Web ACL

Web ACL(Web Access Control List)是 AWS WAF 的核心資源,它包含用於評估每個收到請求的規則。Web ACL 可以與 CloudFront 發佈點、API Gateway或ALB關聯,以保護您的網頁應用程式。

最快開始WAF的方法是使用 Web ACL 裡的部署託管規則組(Managed Rule Group for WAF)

  • 託管規則組
    是一組WAF規則,由 AWS 或者 AWS Marketplace 裡的第三方廠商建立和維護。這些規則提供了對常見攻擊的保護,或者針對特定應用類型的攻擊。
    例如:
    • SQL 或者命令行攻擊
    • Amazon IP Reputation list
    • Known Bad Inputs
    • Core rule set等,還有其他規則組可供使用

下圖在 Web ACL 裡加入了 AWS 託管規則裡的 Core rule set (包含 OWASP 和常見 CVE 防禦的託管規則)以及防範 SQL Injection 的託管規則。

在建立之前,WAF Web ACL 可以部署在兩個位置,分別是 Global 和各個 Region

  • Global:Web ACL 會被部署到 AWS 全球300多個 PoP 節點,使用一致的 WAF 防護策略與 CloudFront 結合來保護用戶的來源伺服器。
  • Region:Web ACL 在每個 Region 配置不同的 Web ACL,用於保護同 Region 裡的 Web 服務器。

如果您的 Web ACL 建立後找不到,很可能就是因為查看的 Region 不是當時建立 Web ACL 的 Region。

a. 建立 Web ACL

注意!

  • Description 如果不填寫以後不能修改。
  • Resource type:
    • CloudFront distribution是建立Global Web ACL。
    • Regional resource 是建立 Region Web ACL。

點擊Next

b. Add rules and rule group

新增相關的規則,這裡我們選擇 Web 防護必用的四個託管規則,分別是:

  • Amazon IP reputation list (來自 AWS 安全情報的已知惡意IP列表)
  • Core rule set (包含跨站腳本攻擊等OWASP攻擊防護的策略)
  • Known bad inputs (包含已知的漏洞發現和利用防護-其中包含Log4j漏洞的相關防護)
  • SQL database (包含SQL注入攻擊防護)

點擊Next後,結果如下:

點擊Next。

c. Set rule priority

在這步驟可以設定各條規則的優先級。

建議可以把比較複雜的檢查條件優先級安排在後面位置,讓惡意請求先經過簡單條件的檢查
例如:Reputation List 的檢查只檢查 IP 地址,就把它放在最前面的位置。

設定好點擊Next。

d. Configure metrics

這部分保持預設即可。


點擊Next。

e. Review and create web ACL

最後檢查之前設定的內容,接著點擊"Create Web ACL"

Step 2. 設定 ALB

此範例ALB是連接EC2,大家可依自身需求環境,選擇連接不同的服務。

ALB 建立完成如下:

ALB Target Group 如下:

這時,將 ALB DNS 名稱複製並貼至瀏覽器。
將得到以下結果,表示 ALB 與 EC2 成功轉送要求。

Step 3. 將 Web ACL 與 ALB 關聯

a. 點擊 Web ACL 裡的 Associated resource

b. 選擇 Application Load Balancer

c. 關聯完成

📌補充!在設定好 Certificate 的情況下,ALB 還可以完成 HTTPS 解密,然後由 WAF 進行網頁請求的內容檢查。

d. 模擬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-XXXXXXX.XXXXXXX.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>

[Optional] Web ACL 常用規則設定

  1. 在WAF規則列表前增加限速規則
    設定如下:

    這裡我們根據每個客戶端訪問的IP進行限速,每個Public IP訪問的次數限定在每5分鐘1000次以下。超過這個頻率的請求將被過濾。
    大家可以根據自己應用的請求頻率再做調整。有了這個限速規則,當外部使用HTTP泛洪進行攻擊的時候,我們可以先把限速規則的訪問速率降下來,先限制住請求數量最多的個別IP,來恢復大多數正常用戶的訪問。

  2. 注意一些容易產生誤殺的字段
    例如,Core rule set 裡的 SizeRestrictions_BODY 子規則,經常會導致一些上傳圖片的請求被過濾掉。
    此時可根據情況把該條子規則設定為count模式

  3. 設定檢查白名單
    對於需要設定白名單的請求,可以使用Scope Down(縮小檢查範圍)設定來讓請求繞過檢查。
    設定如下:

    注意Scope Down設定是當請求匹配過濾條件的時候必須進行檢查。因此我們想讓photo_upload這個uri path繞過檢查的時候,選擇的過濾條件是不匹配該條件(NOT)。從而達到不包含photo_upload uri path的請求需要檢查。反之則不檢查。

Step 4. 使用 OpenSearch

a. 建立 OpenSearch

:warning: 以上設定值僅供參考,請根據需求設定數值。

  • 等待15分鐘後,OpenSearch 建立完成。
    如下圖:

b. 取得 OpenSearch DashBoard URL

  • 登入畫面

c. 登入後

請使用剛剛設定的帳號密碼,選擇Global。

:warning: 注意如果選錯了,可以到右上角切換tenant改過來。 Private tenant各用戶的資料不能共享。不適合SIEM的場景。

  • Switch Tenants

d. 匯入OpenSearch Dashboard

現在已經準備好了一個 OpenSearch 集群,這是一個可以通過 HTTP API 接口注入Log,由 OpenSearch 進行索引的 Log 搜索平台。您可以使用 Discover Menu 輕鬆進行 Log 搜索。為了使得 Log 可以使用圖形化界面進行搜索。我們需要匯入 Dashboard ,為 Log 做圖形化分析。

  • 匯入Dashboard的ndjson文件

  • 📍Menu > Stack Management > Saved Objects > Import

匯入後如下圖,這時可以看到 WAF Dashboard 的框架,後續調整 OpenSearch 的輸入 Index Template ,使資料正常顯示。

  • 調整 Index Template 設定
    Index Template 的設定是讓 OpenSearch 在對 WAF 輸入的 Log 進行索引寫入的時候,對每個 json 單元做不同的資料類型設定,以便後面查詢的時候可以根據特定字段來進行查詢和匯總。這個部分如果預先不設定好,有些 Dashboard 的圖形化顯示無法正常工作。

    如果Mapping設定未完成而Log已經寫入。則已經寫入的Log不能有效搜尋。需要等下一個index週期(一小時或者一天)以後,新的index生成才能生效。

  • 點擊 OpenSearch Menu 中的 Dev Tool
    在左邊邊框輸入我們準備好的Mapping 設置,得到"Acknowledged"表示設置成功了,如附圖。

:warning: 這邊你如果遇到400的錯誤 - expected total copies needs to be a multiple of total awareness attributes [3]

表示你OpenSearch的Data Nodes 大於 2, 最簡單的解決方式是將 number of replicas 從 1改成 2, 然後在重送一次就會正常了.

:warning: Json設定內容較長,放置文章最後,請點我

至此,我們的 OpenSearch 分析 Log 的準備工作基本完成。可以開始做 WAF Log 的設定,把 Log 匯入 OpenSearch了。

Step 5. 使用 Kinesis Firehose

透過 Kinesis Firehose 把 WAF Log 轉送至 OpenSearch 中。

a. 建立 Kinesis

等待2分鐘左右Delivery Stream建立完成。

b. 啟用 WAF Logging

經過以上步驟,我們的 WAF 已經可以開始防護網頁應用,打開 WAF Overview 我們可以看到一些經過 WAF 檢查的 Web 請求。不過這裡的請求,是幫助操作人員了解 WAF 的大致工作情況的。

WAF Overview 裡的資料是經過採樣的。選擇採樣的請求來檢查請求信息,裡面並沒有阻斷請求的規則匹配細節。這對於我們日常的排查誤殺的工作仍然不夠用。

  • WAF Overview:

  • 採樣請求可根據不同 WAF rule 進行過濾

  • 採樣請求內容不包含 WAF 匹配規則

由於 WAF Overview 裡的資料是經過採樣的,並且歷史的訪問記錄並不歸檔。因此如果我們需要使用 WAF完成安全合規的任務,或者做誤殺排查工作,就必須要啟用WAF日誌功能。
首先:

  1. 啟用 Logging and metrics

  2. 選擇存放 Logging 的地方
    這邊選擇了Kinesis Data Firehose stream。

  3. Firehose IAM Role 關聯到 OpenSearch 用戶
    接著需要把 Firehose Delivery Stream 的 IAM Role 關聯到 OpenSearch 的用戶裡。讓 Firehose 寫入OpenSearch Log 的動作不會被拒絕。

  • 點擊 IAM Role 連結

  • 進入 IAM 介面並複製 Role ARN
    這是 Firehose 做寫入動作的服務角色,需要將這ARN新增到在 OpenSearch 裡,給予寫入 OpenSearch 的權限。

  • 到OpenSearch Dashboard
    📍Menu > OpenSearch Plugins > Security > Roles > 點選 all_access > 點選 Mapped users
    點擊 Manage mapping 按鈕,如下圖。

  • 新增Backend roles,填入之前Firehose IAM Role ARN


    :warning: 以上步驟如果沒設定,在 OpenSearch 是看不到 Log 的,並且在 Delivery Stream 的 Destination error log 地方會看到 OpenSearch 回傳錯誤的訊息。

具體錯誤訊息如下:

Error received from the Amazon OpenSearch Service cluster. {"error":{"root_cause":[{"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000, backend_roles=[arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000], requestedTenant=null]"}],"type":"security_exception","reason":"no permissions for [indices:data/write/bulk] and User [name=arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000, backend_roles=[arn:aws:iam::123456789000:role/service-role/KinesisFirehoseServiceRole-aws-waf-logs—us-east-1-1234567890000], requestedTenant=null]"},"status":403}

測試ALB

記得到AWS console 找一下ALB 的位址, 然後可以透過curl 的方式來測試WAF, 並且透過opensearch 的 Dashboard 來查看


 
 
for i in {1..100} do 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-4xxxxxx.ap-southeast-1.elb.amazonaws.com" -F "user='AND 1=1;" done;

附上Dashboard上的簡圖, 你也可以在Filter上調整查看你想看的數據.






Summary

透過以上的步驟,我們成功利用 AWS OpenSearch 建立了一個 WAF Log 分析系統,對於有安全運營需求的企業而言是非常必要的。透過該系統,我們瞭解了如何將 WAF Log 導入 Kinesis Firehose ,並且建立了一個 OpenSearch 的託管 Log 分析平台,最終將我們準備好的 WAF Log 分析 Dashboard 導入系統。此外,也成功完成了 OpenSearch index template 的設定,從而確保 WAF Log 的有效搜尋。

OpenSearch WAF Log index mapping設定

PUT _index_template/awswaf-waf-logs
    {
    "index_patterns": [
        "awswaf-waf-*"
    ],
    "template": {
        "settings": {
            "index": {
                "number_of_shards": "5",
                "number_of_replicas": "1"
            }
        },
        "mappings": {
            "properties": {
                "terminatingRuleId": {
                    "type": "keyword"
                },
                "terminatingRuleType": {
                    "type": "keyword"
                },
                "ruleGroupList": {
                    "properties": {
                        "terminatingRule": {
                            "properties": {
                                "action": {
                                    "type": "keyword"
                                },
                                "ruleId": {
                                    "type": "keyword"
                                }
                            }
                        },
                        "ruleGroupId": {
                            "type": "keyword"
                        }
                    }
                },
                "httpSourceId": {
                    "type": "keyword"
                },
                "labels": {
                    "properties": {
                        "name": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        }
                    }
                },
                "webaclId": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "ignore_above": 256,
                            "type": "keyword"
                        }
                    }
                },
                "@timestamp": {
                    "path": "timestamp",
                    "type": "alias"
                },
                "webaclName": {
                    "type": "keyword"
                },
                "action": {
                    "type": "keyword"
                },
                "httpRequest": {
                    "properties": {
                        "args": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        },
                        "country": {
                            "type": "keyword"
                        },
                        "headers": {
                            "properties": {
                                "name": {
                                    "type": "keyword"
                                },
                                "value": {
                                    "type": "text",
                                    "fields": {
                                        "keyword": {
                                            "ignore_above": 256,
                                            "type": "keyword"
                                        }
                                    }
                                }
                            }
                        },
                        "httpVersion": {
                            "type": "keyword"
                        },
                        "requestId": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        },
                        "clientIp": {
                            "type": "ip"
                        },
                        "httpMethod": {
                            "type": "keyword"
                        },
                        "uri": {
                            "type": "text",
                            "fields": {
                                "keyword": {
                                    "ignore_above": 256,
                                    "type": "keyword"
                                }
                            }
                        }
                    }
                },
                "httpSourceName": {
                    "type": "keyword"
                },
                "formatVersion": {
                    "type": "keyword"
                },
                "timestamp": {
                    "format": "epoch_millis",
                    "type": "date"
                }
            }
        },
        "aliases": {
            "awswaf-waf": {}
        }
    }
}

Reference:

Contact
Contact