你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
为 Azure Front Door 优化 Azure Web 应用程序防火墙
Microsoft 托管的默认规则集基于 OWASP 核心规则集,并且包括 Microsoft 威胁情报收集规则。
用户普遍认为,应用程序防火墙 (WAF) 规则需要进行调整,才能满足使用 WAF 的应用程序或组织的特定需求。 组织通常通过以下操作之一来实现优化:
- 定义规则排除项。
- 创建自定义规则。
- 禁用可能导致问题或误报的规则。
本文介绍在应通过 WAF 的请求被阻止时可以执行的操作。
注意
Microsoft 托管的规则集不适用于 Azure Front Door 标准 SKU。 有关不同层级 SKU 的详细信息,请参阅《层级之间的功能比较》。
请参阅《Azure Front Door WAF 概述》和《Azure Front Door 的 WAF 策略》文档。 同时,请启用“WAF 监视和日志记录”。 这些文章说明了 WAF 函数、WAF 规则集的工作原理,以及访问 WAF 日志的方法。
了解 WAF 日志
WAF 日志用于显示 WAF 匹配或阻止的每个请求。 它是所有被评估为匹配或阻止请求的集合。 如果你发现 WAF 阻止了原本不应该阻止的请求(误报),可以采取几种方法来解决问题。
首先缩小查找范围,找到特定的请求。 可以配置自定义响应消息,将 trackingReference
字段包含在内,以便轻松发现事件,并针对该特定值执行日志查询。 查看日志,以找到请求的特定 URI、时间戳或客户端 IP。 找到相关的日志条目后,可以开始处理误报。
例如,假设某个合法流量(你希望该流量通过 WAF)包含字符串 1=1
。 该请求如下所示:
POST http://afdwafdemosite.azurefd.net/api/Feedbacks HTTP/1.1
Host: afdwafdemosite.azurefd.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 55
UserId=20&captchaId=7&captchaId=15&comment="1=1"&rating=3
如果尝试请求,WAF 将阻止任何参数或字段中包含 1=1
字符串的流量。 此字符串通常与 SQL 注入攻击相关。 你可以浏览日志,查看被阻止/匹配的请求和规则的时间戳。
以下示例演示基于规则匹配生成的日志条目。 以下 Log Analytics 查询可用于查找在过去 24 小时内被阻止的请求。
AzureDiagnostics
| where Category == 'FrontDoorWebApplicationFirewallLog'
| where TimeGenerated > ago(1d)
| where action_s == 'Block'
AzureDiagnostics
| where Category == 'FrontdoorWebApplicationFirewallLog'
| where TimeGenerated > ago(1d)
| where action_s == 'Block'
在 requestUri
字段中可以看到,该请求已专门转变为 /api/Feedbacks/
。 接下来,在 ruleName
字段中找到规则 ID 942110
。 获得规则 ID 后,访问 OWASP ModSecurity 核心规则集官方存储库,并搜索该规则 ID,查看其代码,了解此规则确切的匹配对象。
然后,通过检查 action
字段,可以看到此规则设置为在匹配时阻止请求。 可以确认由于 policyMode
被设置为 prevention
,所以请求被 WAF 阻止。
现在查看 details
字段中的信息。 你可以在其中查看 matchVariableName
和 matchVariableValue
信息。 触发此规则的原因是,有人将 1=1
输入到了 Web 应用的 comment
字段。
{
"time": "2020-09-24T16:43:04.5422943Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.CDN/PROFILES/AFDWAFDEMOSITE",
"category": "FrontDoorWebApplicationFirewallLog",
"operationName": "Microsoft.Cdn/Profiles/WebApplicationFirewallLog/Write",
"properties": {
"clientIP": "1.1.1.1",
"clientPort": "53566",
"socketIP": "1.1.1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"ruleName": "DefaultRuleSet-1.0-SQLI-942110",
"policy": "AFDWAFDemoPolicy",
"action": "Block",
"host": "afdwafdemosite.azurefd.net",
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"policyMode": "prevention",
"details": {
"matches": [
{
"matchVariableName": "PostParamValue:comment",
"matchVariableValue": "\"1=1\""
}
],
"msg": "SQL Injection Attack: Common Injection Testing Detected",
"data": "Matched Data: \"1=1\" found within PostParamValue:comment: \"1=1\""
}
}
}
{
"time": "2020-09-24T16:43:04.5422943Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.NETWORK/FRONTDOORS/AFDWAFDEMOSITE",
"category": "FrontdoorWebApplicationFirewallLog",
"operationName": "Microsoft.Network/FrontDoor/WebApplicationFirewallLog/Write",
"properties": {
"clientIP": "1.1.1.1",
"clientPort": "53566",
"socketIP": "1.1.1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"ruleName": "DefaultRuleSet-1.0-SQLI-942110",
"policy": "AFDWAFDemoPolicy",
"action": "Block",
"host": "afdwafdemosite.azurefd.net",
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"policyMode": "prevention",
"details": {
"matches": [
{
"matchVariableName": "PostParamValue:comment",
"matchVariableValue": "\"1=1\""
}
],
"msg": "SQL Injection Attack: Common Injection Testing Detected",
"data": "Matched Data: \"1=1\" found within PostParamValue:comment: \"1=1\""
}
}
}
你还可以通过查看访问日志来进一步了解特定的 WAF 事件。 接下来查看日志,该日志是对上述事件的响应。
通过 trackingReference
值均相同可以看出这些日志都是关联的。 在各种提供一般信息的字段(例如 userAgent
和 clientIP
)中,请注意 httpStatusCode
和 httpStatusDetails
字段。 这里可以看出客户端收到了 HTTP 403 响应,由此可以确认请求遭到了拒绝并被阻止。
{
"time": "2020-09-24T16:43:04.5430764Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.CDN/PROFILES/AFDWAFDEMOSITE",
"category": "FrontDoorAccessLog",
"operationName": "Microsoft.Cdn/Profiles/AccessLog/Write",
"properties": {
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"httpMethod": "POST",
"httpVersion": "1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"requestBytes": "2160",
"responseBytes": "324",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36",
"clientIp": "1.1.1.1",
"socketIp": "1.1.1.1",
"clientPort": "53566",
"timeToFirstByte": "0.01",
"timeTaken": "0.011",
"securityProtocol": "",
"routingRuleName": "DemoBERoutingRule",
"rulesEngineMatchNames": [],
"backendHostname": "13.88.65.130:3000",
"isReceivedFromClient": true,
"httpStatusCode": "403",
"httpStatusDetails": "403",
"pop": "WST",
"cacheStatus": "CONFIG_NOCACHE"
}
}
{
"time": "2020-09-24T16:43:04.5430764Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.NETWORK/FRONTDOORS/AFDWAFDEMOSITE",
"category": "FrontdoorAccessLog",
"operationName": "Microsoft.Network/FrontDoor/AccessLog/Write",
"properties": {
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"httpMethod": "POST",
"httpVersion": "1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"requestBytes": "2160",
"responseBytes": "324",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36",
"clientIp": "1.1.1.1",
"socketIp": "1.1.1.1",
"clientPort": "53566",
"timeToFirstByte": "0.01",
"timeTaken": "0.011",
"securityProtocol": "",
"routingRuleName": "DemoBERoutingRule",
"rulesEngineMatchNames": [],
"backendHostname": "13.88.65.130:3000",
"isReceivedFromClient": true,
"httpStatusCode": "403",
"httpStatusDetails": "403",
"pop": "WST",
"cacheStatus": "CONFIG_NOCACHE"
}
}
处理误报
若要明智地决定如何处理误报,必须熟悉应用程序所用的技术。 例如,假设技术堆栈中没有 SQL 服务器,但你收到了与这些规则相关的误报。 禁用这些规则不一定会削弱安全性。
了解上述信息并知道规则 942110 是示例中匹配 1=1
字符串的规则后,可以通过执行以下操作,来阻止此合理请求遭到阻止:
- 使用排除列表。 有关排除列表的详细信息,请参阅《Azure Web 应用程序防火墙与 Azure Front Door 的排除列表》。
- 更改 WAF 操作。 请参阅《WAF 操作》,详细了解当请求匹配规则条件时 WAF 可能会执行哪些操作。
- 使用自定义规则。 有关自定义规则的详细信息,请参阅《Azure Front Door 中 Azure Web 应用程序防火墙的自定义规则》。
- 禁用规则。
提示
选择允许合理请求通过 WAF 的方法时,请尽量缩小此方法的作用范围。 例如,使用排除列表优于完全禁用规则。
使用排除列表
使用排除列表的优势之一是,对于给定请求,只有你选择要排除的匹配变量不再受到检查。 也就是说,可以在特定的请求标头、请求 cookie、查询字符串参数或请求正文 post 参数之间进行选择,以在满足特定条件时将这些内容排除,而非将整个请求排除在检查范围之外。 其他未指定的请求变量仍会照常检查。
排除项是全局设置。 配置的排除项将应用于所有通过 WAF 传递的流量,而不只是仅适用于特定 Web 应用或 URI。 例如,1=1
在某个 Web 应用的正文中是有效请求,但在遵守同一个 WAF 策略的其他 Web 应用中无效,这种情况下排除项可能会引发问题。
如果可以将不同的排除列表用于不同的应用程序,请考虑将不同的 WAF 策略用于各个应用程序,并将这些策略应用于每个应用程序的前端。
为托管规则配置排除列表时,可以选择排除:
- 规则集中的所有规则。
- 规则组中的所有规则。
- 单个用户。
可以使用 PowerShell、Azure CLI、REST API、Bicep、Azure 资源管理器模板或 Azure 门户来配置排除列表。
- 规则级别的排除项:在规则级别应用排除项意味着不会仅针对单个规则分析指定的排除项。 规则集中的所有其他规则都会分析该排除项。 这是最精细的排除项级别。 排除事件故障时,可以使用该方法根据 WAF 日志中的信息微调托管规则集。
- 规则组级别的排除项:如果在规则组级别应用排除项,这意味着特定规则类型集不会分析指定的排除项。 例如,将“SQLI”选为被排除的规则组表示所有 SQLI 相关规则都不会检查规定的请求排除项。 但其他组(如 PHP、RFI 或 XSS)的规则仍会检查。 如果确信应用程序不容易遭受特定类型的攻击时,可使用这种排除类型。 例如,没有任何 SQL 数据库的应用程序可以将所有 SQLI 规则排除,同时也不会降低其安全级别。
- 规则集级别的排除项:如果在规则集级别应用排除项,这意味着该规则集中的任何安全规则都不会分析指定的排除项。 该排除范围很全面,请小心使用。
在此示例中,通过对单个规则应用排除,在最精细的级别执行排除。 假设要排除包含 comment
的“请求正文 POST 参数名称”匹配变量。 可以在防火墙日志中查看匹配变量的详细信息:"matchVariableName": "PostParamValue:comment"
。 属性为 comment
。 还可以通过其他几种方法找到此属性名称。 详细信息请参阅《查找请求属性名称》。
有时特定的参数会以一种间接方式传入 WAF。 例如,使用 Microsoft Entra ID 进行身份验证时会传递令牌。 令牌 __RequestVerificationToken
通常作为请求 cookie 传入。
但有时当 cookie 被禁用时,此令牌还会以请求 POST 参数的形式传入。 因此,若要解决 Microsoft Entra 令牌误报,需要确保 __RequestVerificationToken
添加到 RequestCookieNames
和 RequestBodyPostArgsNames
的排除列表中。
排除字段名(选择器)意味着 WAF 将不再评估此值。 但是,字段名本身将继续受到评估,在极少数情况下,它可能会匹配 WAF 规则并触发操作。
更改 WAF 操作
另一种处理 WAF 规则行为的方式是,选择请求匹配规则条件时要执行的操作。 可采取的操作包括允许、阻止、记录和重定向。
在此示例中,规则 942110 的默认操作“阻止”被更改为“记录”。 这样 WAF 就会记录请求,并继续根据其他优先级较低的规则评估这个请求。
执行同样的请求后,若查询日志,则可以看到该请求匹配规则 942110。 字段 action_s
现在指示“记录”而不是“阻止”。 然后,将日志查询扩展到包含 trackingReference_s
信息,查看与此请求相关的其他事项。
现在可以看到在处理完规则 942110 后,很快就出现了一个不同的 SQLI 规则匹配项。 同一个请求已与规则 ID 942310 匹配,这一次它触发了默认操作“阻止”。
在调整 WAF 或进行故障排除时使用“日志记录”操作的另一个优势是,可以确定特定规则组中的多个规则是否匹配和阻止给定请求。 然后,可以在适当的级别(规则级别或规则组级别)创建排除项。
使用自定义规则
确定导致 WAF 规则匹配的原因后,可以使用自定义规则调整 WAF 响应事件的方式。 自定义规则在托管规则之前进行处理。 自定义规则可以包含多个条件,其操作可以是允许、拒绝、记录或重定向。
警告
当请求与自定义规则匹配时,WAF 引擎将停止处理请求。 不会针对此请求处理托管规则,优先级较低的其他自定义规则也不会处理。
下列示例展示了有两个条件的自定义规则。 第一个条件是在请求正文中查找 comment
值。 第二个条件是在请求 URI 中查找 /api/Feedbacks/
值。
自定义规则是最精细的规则,可以微调 WAF 规则并处理误报。 本示例不会仅根据 comment
请求正文值执行操作,因为此值可能存在于遵守同一个 WAF 策略的多个站点或应用中。
如果包含另一个也匹配特定请求 URI /api/Feedbacks/
的条件,需确保此自定义规则真正适用于受审查的确切用例。这样哪怕同一攻击针对不同的条件,WAF 引擎仍能检查出来并预防。
浏览日志时可发现 ruleName_s
字段包含自定义规则 redirectcomment
的名称。 在 action_s
字段中,可以看到针对此事件执行了“重定向”操作。 在 details_matches_s
字段中可以看到两个条件匹配项的详细信息。
禁用规则
另一个避免误报的方式是禁用与 WAF 认为是恶意输入匹配的规则。 因为通过分析 WAF 日志将规则范围缩小到 942110,因此可以在 Azure 门户中禁用它。 详细信息请参阅《使用 Azure 门户自定义 Azure Web 应用程序防火墙规则》。
如果确信满足特定条件的所有请求实际上都是合理请求,或者确信规则不适用于你的环境,则可以禁用规则(例如,后端非 SQL,因此禁用 SQL 注入规则)。
但是,禁用规则是一种全局设置,它会应用于所有与 WAF 策略关联的前端主机。 如果选择禁用规则,则可能由于与 WAF 策略关联的任何其他前端主机不再受到保护或检测而暴露漏洞。
如果要使用 Azure PowerShell 禁用托管规则,请参阅 PSAzureManagedRuleOverride
对象文档。 如果要使用 Azure CLI,请参阅 az network front-door waf-policy managed-rules override
文档。
提示
请记录对 WAF 策略所做的任何更改。 包括说明误报检测的示例请求。 说明添加自定义规则、禁用规则或规则集或者添加例外的原因。 如果未来要重新设计应用程序,可能会需要检查更改是否仍然有效。 接受审查或者需要证明重新配置 WAF 策略默认设置的理由时,此文档也可提供帮助。
查找请求字段
借助 Fiddler 等浏览器代理,可以检查单个请求,并确定请求调用了网页的哪些特定字段。 需要使用 WAF 中的排除列表将某些字段排除在检查范围之外时,此技术非常有用。
查找请求属性名称
在此示例中,输入了 1=1
字符串的字段名为 comment
。 此数据已传入 POST 请求的正文。
可以排除此字段。 若要详细了解排除列表,请参阅《Web 应用程序防火墙排除列表》。 在本例中,可以通过配置以下排除项来排除评估:
还可以检查防火墙日志来获取信息,以确定需要将哪些内容添加到排除列表。 若要启用日志记录,请参阅《在 Azure Front Door 中监视指标和日志》。
根据要检查的请求的发生时间,查看 PT1H.json
文件中的防火墙日志。 PT1H.json
文件可从存储 FrontDoorWebApplicationFirewallLog
和 FrontDoorAccessLog
诊断日志的存储帐户容器中获取。
根据要检查的请求的发生时间,查看 PT1H.json
文件中的防火墙日志。 PT1H.json
文件可从存储 FrontdoorWebApplicationFirewallLog
和 FrontdoorAccessLog
诊断日志的存储帐户容器中获取。
在此示例中可以看到,规则进行阻止的时间和请求发生的时间相同(具有相同的事务引用)。
{
"time": "2020-09-24T16:43:04.5422943Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.CDN/PROFILES/AFDWAFDEMOSITE",
"category": "FrontDoorWebApplicationFirewallLog",
"operationName": "Microsoft.Cdn/Profiles/WebApplicationFirewallLog/Write",
"properties": {
"clientIP": "1.1.1.1",
"clientPort": "53566",
"socketIP": "1.1.1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"ruleName": "DefaultRuleSet-1.0-SQLI-942110",
"policy": "AFDWAFDemoPolicy",
"action": "Block",
"host": "afdwafdemosite.azurefd.net",
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"policyMode": "prevention",
"details": {
"matches": [
{
"matchVariableName": "PostParamValue:comment",
"matchVariableValue": "\"1=1\""
}
],
"msg": "SQL Injection Attack: Common Injection Testing Detected",
"data": "Matched Data: \"1=1\" found within PostParamValue:comment: \"1=1\""
}
}
}
{
"time": "2020-09-24T16:43:04.5422943Z",
"resourceId": "/SUBSCRIPTIONS/<Subscription ID>/RESOURCEGROUPS/<Resource Group Name>/PROVIDERS/MICROSOFT.NETWORK/FRONTDOORS/AFDWAFDEMOSITE",
"category": "FrontdoorWebApplicationFirewallLog",
"operationName": "Microsoft.Network/FrontDoor/WebApplicationFirewallLog/Write",
"properties": {
"clientIP": "1.1.1.1",
"clientPort": "53566",
"socketIP": "1.1.1.1",
"requestUri": "http://afdwafdemosite.azurefd.net:80/api/Feedbacks/",
"ruleName": "DefaultRuleSet-1.0-SQLI-942110",
"policy": "AFDWAFDemoPolicy",
"action": "Block",
"host": "afdwafdemosite.azurefd.net",
"trackingReference": "0mMxsXwAAAABEalekYeI4S55qpi5R7R0/V1NURURHRTA4MTIAZGI4NGQzZDgtNWQ5Ny00ZWRkLTg2ZGYtZDJjNThlMzI2N2I4",
"policyMode": "prevention",
"details": {
"matches": [
{
"matchVariableName": "PostParamValue:comment",
"matchVariableValue": "\"1=1\""
}
],
"msg": "SQL Injection Attack: Common Injection Testing Detected",
"data": "Matched Data: \"1=1\" found within PostParamValue:comment: \"1=1\""
}
}
}
了解了 Azure 托管的规则集的工作原理后,可以发现是包含 action: Block
属性的规则根据请求正文中匹配的数据进行了阻止。 (详细信息请参阅《Azure Front Door 中的 Azure Web 应用程序防火墙》。)可以在详细信息中看到它与模式匹配 (1=1
) 且字段名为 comment
。 请按照前面的相同步骤排除包含 comment
的请求正文 post 参数名称。
查找请求头名称
在 Fiddler 中还能够很方便地查找请求头名称。 以下屏幕截图显示了此 GET 请求的标头,其中包括 Content-Type
和 User-Agent
。 你也可以在 WAF 中使用请求标头创建排除项和自定义规则。
另一个查看请求和响应标头的方式是使用浏览器(如 Microsoft Edge 或 Chrome)的开发人员工具。 可以选择 F12 或右键单击“检查”>“开发人员工具”。 选择“网络”选项卡。加载网页并选择要检查的请求。
查找请求 Cookie 名称
如果请求包含 Cookie,可以选择“Cookie”选项卡在 Fiddler 中查看 Cookie。 Cookie 信息也可用于在 WAF 中创建排除项或自定义规则。
异常评分规则
如果在优化 WAF 的过程中看到规则 ID 949110,则表明请求已被异常评分程序阻止。
通过搜索具有相同跟踪参考编号的日志条目,查看同一请求的其他 WAF 日志条目。 查看每个被触发的规则。 根据本文指南优化各个规则。
警告
将新的托管规则集分配给 WAF 策略时,现有托管规则集的所有先前自定义项(例如规则状态、规则操作和规则级别排除)都将重置为新托管规则集的默认值。 但是,在分配新规则集期间,任何自定义规则和策略设置均不受影响。
后续步骤
- 了解 Azure Web 应用程序防火墙。
- 了解如何创建 Azure Front Door 的实例。