waf-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWAF Setup
WAF 部署
Protect web applications with Web Application Firewalls.
使用Web应用防火墙保护Web应用。
When to Use This Skill
何时使用此Skill
Use this skill when:
- Deploying a public-facing web application that needs attack protection
- Meeting compliance requirements (PCI-DSS, SOC2) for web application security
- Blocking OWASP Top 10 attack categories (SQLi, XSS, CSRF, etc.)
- Protecting APIs from abuse, injection, and rate-based attacks
- Adding a virtual patching layer while application code is being fixed
在以下情况使用此skill:
- 部署需要攻击防护的公网Web应用
- 满足Web应用安全的合规要求(PCI-DSS、SOC2)
- 阻止OWASP Top 10攻击类别(SQLi、XSS、CSRF等)
- 保护API免受滥用、注入和基于速率的攻击
- 在应用程序代码修复期间添加虚拟补丁层
Prerequisites
先决条件
- Web application behind a load balancer or reverse proxy
- AWS account for AWS WAF, or Cloudflare account for Cloudflare WAF
- Nginx with ModSecurity module compiled for self-hosted WAF
- Access to application logs to tune rules and identify false positives
- Understanding of HTTP request/response structure
- 位于负载均衡器或反向代理后面的Web应用
- 用于AWS WAF的AWS账户,或用于Cloudflare WAF的Cloudflare账户
- 为自托管WAF编译了ModSecurity模块的Nginx
- 访问应用日志以调优规则并识别误报
- 理解HTTP请求/响应结构
AWS WAF
AWS WAF
Create Web ACL with Managed Rules
使用托管规则创建Web ACL
bash
undefinedbash
undefinedCreate Web ACL with AWS managed rules
Create Web ACL with AWS managed rules
aws wafv2 create-web-acl
--name production-waf
--scope REGIONAL
--default-action Allow={}
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=production-waf
--rules file://waf-rules.json
--name production-waf
--scope REGIONAL
--default-action Allow={}
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=production-waf
--rules file://waf-rules.json
undefinedaws wafv2 create-web-acl
--name production-waf
--scope REGIONAL
--default-action Allow={}
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=production-waf
--rules file://waf-rules.json
--name production-waf
--scope REGIONAL
--default-action Allow={}
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=production-waf
--rules file://waf-rules.json
undefinedAWS WAF Rules Configuration
AWS WAF规则配置
json
[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ExcludedRules": []
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSCommonRules"
}
},
{
"Name": "AWSManagedRulesSQLiRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesSQLiRuleSet"
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSSQLiRules"
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 3,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSBadInputRules"
}
},
{
"Name": "RateLimitRule",
"Priority": 4,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimit"
}
},
{
"Name": "GeoBlockRule",
"Priority": 5,
"Statement": {
"GeoMatchStatement": {
"CountryCodes": ["KP", "IR", "SY"]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "GeoBlock"
}
},
{
"Name": "BlockBadUserAgents",
"Priority": 6,
"Statement": {
"ByteMatchStatement": {
"SearchString": "sqlmap",
"FieldToMatch": { "SingleHeader": { "Name": "user-agent" } },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS"
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BadUserAgent"
}
}
]json
[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ExcludedRules": []
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSCommonRules"
}
},
{
"Name": "AWSManagedRulesSQLiRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesSQLiRuleSet"
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSSQLiRules"
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 3,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSBadInputRules"
}
},
{
"Name": "RateLimitRule",
"Priority": 4,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimit"
}
},
{
"Name": "GeoBlockRule",
"Priority": 5,
"Statement": {
"GeoMatchStatement": {
"CountryCodes": ["KP", "IR", "SY"]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "GeoBlock"
}
},
{
"Name": "BlockBadUserAgents",
"Priority": 6,
"Statement": {
"ByteMatchStatement": {
"SearchString": "sqlmap",
"FieldToMatch": { "SingleHeader": { "Name": "user-agent" } },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS"
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BadUserAgent"
}
}
]Associate WAF with ALB
将WAF与ALB关联
bash
undefinedbash
undefinedAssociate with Application Load Balancer
Associate with Application Load Balancer
aws wafv2 associate-web-acl
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-alb/abc123
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-alb/abc123
aws wafv2 associate-web-acl
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-alb/abc123
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-alb/abc123
Associate with API Gateway
Associate with API Gateway
aws wafv2 associate-web-acl
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123/stages/prod
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123/stages/prod
undefinedaws wafv2 associate-web-acl
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123/stages/prod
--web-acl-arn arn:aws:wafv2:us-east-1:123456789:regional/webacl/production-waf/abc123
--resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123/stages/prod
undefinedAWS WAF Terraform
AWS WAF Terraform
hcl
resource "aws_wafv2_web_acl" "main" {
name = "production-waf"
scope = "REGIONAL"
description = "Production WAF with OWASP protections"
default_action {
allow {}
}
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
rule_action_override {
name = "SizeRestrictions_BODY"
action_to_use { count {} }
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSCommonRules"
sampled_requests_enabled = true
}
}
rule {
name = "RateLimit"
priority = 10
action { block {} }
statement {
rate_based_statement {
limit = 2000
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimit"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "production-waf"
sampled_requests_enabled = true
}
}
resource "aws_wafv2_web_acl_association" "alb" {
resource_arn = aws_lb.main.arn
web_acl_arn = aws_wafv2_web_acl.main.arn
}hcl
resource "aws_wafv2_web_acl" "main" {
name = "production-waf"
scope = "REGIONAL"
description = "Production WAF with OWASP protections"
default_action {
allow {}
}
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action { none {} }
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
rule_action_override {
name = "SizeRestrictions_BODY"
action_to_use { count {} }
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSCommonRules"
sampled_requests_enabled = true
}
}
rule {
name = "RateLimit"
priority = 10
action { block {} }
statement {
rate_based_statement {
limit = 2000
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimit"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "production-waf"
sampled_requests_enabled = true
}
}
resource "aws_wafv2_web_acl_association" "alb" {
resource_arn = aws_lb.main.arn
web_acl_arn = aws_wafv2_web_acl.main.arn
}Cloudflare WAF
Cloudflare WAF
API Configuration
API配置
bash
undefinedbash
undefinedList available WAF rulesets
List available WAF rulesets
curl -s "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}" | jq '.result[] | {id, name, phase}'
-H "Authorization: Bearer ${CF_TOKEN}" | jq '.result[] | {id, name, phase}'
curl -s "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}" | jq '.result[] | {id, name, phase}'
-H "Authorization: Bearer ${CF_TOKEN}" | jq '.result[] | {id, name, phase}'
Create a custom WAF rule
Create a custom WAF rule
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Custom WAF Rules", "kind": "zone", "phase": "http_request_firewall_custom", "rules": [ { "action": "block", "expression": "(http.request.uri.query contains "union select" or http.request.uri.query contains "1=1")", "description": "Block SQL injection patterns in query string" }, { "action": "block", "expression": "(http.request.uri.path contains "..%2f" or http.request.uri.path contains "..%5c")", "description": "Block path traversal attempts" }, { "action": "challenge", "expression": "(cf.threat_score gt 30)", "description": "Challenge high threat score visitors" }, { "action": "block", "expression": "(http.request.headers["user-agent"] contains "sqlmap" or http.request.headers["user-agent"] contains "nikto")", "description": "Block known attack tools" } ] }'
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Custom WAF Rules", "kind": "zone", "phase": "http_request_firewall_custom", "rules": [ { "action": "block", "expression": "(http.request.uri.query contains "union select" or http.request.uri.query contains "1=1")", "description": "Block SQL injection patterns in query string" }, { "action": "block", "expression": "(http.request.uri.path contains "..%2f" or http.request.uri.path contains "..%5c")", "description": "Block path traversal attempts" }, { "action": "challenge", "expression": "(cf.threat_score gt 30)", "description": "Challenge high threat score visitors" }, { "action": "block", "expression": "(http.request.headers["user-agent"] contains "sqlmap" or http.request.headers["user-agent"] contains "nikto")", "description": "Block known attack tools" } ] }'
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Custom WAF Rules", "kind": "zone", "phase": "http_request_firewall_custom", "rules": [ { "action": "block", "expression": "(http.request.uri.query contains "union select" or http.request.uri.query contains "1=1")", "description": "Block SQL injection patterns in query string" }, { "action": "block", "expression": "(http.request.uri.path contains "..%2f" or http.request.uri.path contains "..%5c")", "description": "Block path traversal attempts" }, { "action": "challenge", "expression": "(cf.threat_score gt 30)", "description": "Challenge high threat score visitors" }, { "action": "block", "expression": "(http.request.headers["user-agent"] contains "sqlmap" or http.request.headers["user-agent"] contains "nikto")", "description": "Block known attack tools" } ] }'
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Custom WAF Rules", "kind": "zone", "phase": "http_request_firewall_custom", "rules": [ { "action": "block", "expression": "(http.request.uri.query contains "union select" or http.request.uri.query contains "1=1")", "description": "Block SQL injection patterns in query string" }, { "action": "block", "expression": "(http.request.uri.path contains "..%2f" or http.request.uri.path contains "..%5c")", "description": "Block path traversal attempts" }, { "action": "challenge", "expression": "(cf.threat_score gt 30)", "description": "Challenge high threat score visitors" }, { "action": "block", "expression": "(http.request.headers["user-agent"] contains "sqlmap" or http.request.headers["user-agent"] contains "nikto")", "description": "Block known attack tools" } ] }'
Configure rate limiting
Configure rate limiting
curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Rate Limiting", "kind": "zone", "phase": "http_ratelimit", "rules": [ { "action": "block", "ratelimit": { "characteristics": ["ip.src"], "period": 60, "requests_per_period": 100, "mitigation_timeout": 600 }, "expression": "(http.request.uri.path matches "^/api/")", "description": "Rate limit API endpoints" } ] }'
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Rate Limiting", "kind": "zone", "phase": "http_ratelimit", "rules": [ { "action": "block", "ratelimit": { "characteristics": ["ip.src"], "period": 60, "requests_per_period": 100, "mitigation_timeout": 600 }, "expression": "(http.request.uri.path matches "^/api/")", "description": "Rate limit API endpoints" } ] }'
undefinedcurl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets"
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Rate Limiting", "kind": "zone", "phase": "http_ratelimit", "rules": [ { "action": "block", "ratelimit": { "characteristics": ["ip.src"], "period": 60, "requests_per_period": 100, "mitigation_timeout": 600 }, "expression": "(http.request.uri.path matches "^/api/")", "description": "Rate limit API endpoints" } ] }'
-H "Authorization: Bearer ${CF_TOKEN}"
-H "Content-Type: application/json"
-d '{ "name": "Rate Limiting", "kind": "zone", "phase": "http_ratelimit", "rules": [ { "action": "block", "ratelimit": { "characteristics": ["ip.src"], "period": 60, "requests_per_period": 100, "mitigation_timeout": 600 }, "expression": "(http.request.uri.path matches "^/api/")", "description": "Rate limit API endpoints" } ] }'
undefinedCloudflare Terraform
Cloudflare Terraform
hcl
resource "cloudflare_ruleset" "waf_custom" {
zone_id = var.zone_id
name = "Custom WAF Rules"
kind = "zone"
phase = "http_request_firewall_custom"
rules {
action = "block"
expression = "(http.request.uri.query contains \"union select\")"
description = "Block SQL injection in query string"
}
rules {
action = "managed_challenge"
expression = "(cf.threat_score gt 30)"
description = "Challenge suspicious visitors"
}
}hcl
resource "cloudflare_ruleset" "waf_custom" {
zone_id = var.zone_id
name = "Custom WAF Rules"
kind = "zone"
phase = "http_request_firewall_custom"
rules {
action = "block"
expression = "(http.request.uri.query contains \"union select\")"
description = "Block SQL injection in query string"
}
rules {
action = "managed_challenge"
expression = "(cf.threat_score gt 30)"
description = "Challenge suspicious visitors"
}
}ModSecurity with Nginx
ModSecurity与Nginx
Installation
安装
bash
undefinedbash
undefinedInstall ModSecurity for Nginx (Ubuntu)
Install ModSecurity for Nginx (Ubuntu)
apt install -y libmodsecurity3 libmodsecurity-dev nginx libnginx-mod-http-modsecurity
apt install -y libmodsecurity3 libmodsecurity-dev nginx libnginx-mod-http-modsecurity
Or compile from source
Or compile from source
git clone https://github.com/SpiderLabs/ModSecurity /opt/modsecurity
cd /opt/modsecurity
git submodule init && git submodule update
./build.sh && ./configure && make && make install
undefinedgit clone https://github.com/SpiderLabs/ModSecurity /opt/modsecurity
cd /opt/modsecurity
git submodule init && git submodule update
./build.sh && ./configure && make && make install
undefinedNginx Configuration
Nginx配置
nginx
undefinednginx
undefined/etc/nginx/nginx.conf
/etc/nginx/nginx.conf
load_module modules/ngx_http_modsecurity_module.so;
http {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
server {
listen 443 ssl http2;
server_name example.com;
# ModSecurity can also be enabled per-location
location /api/ {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/api-rules.conf;
proxy_pass http://backend;
}
}}
undefinedload_module modules/ngx_http_modsecurity_module.so;
http {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
server {
listen 443 ssl http2;
server_name example.com;
# ModSecurity can also be enabled per-location
location /api/ {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/api-rules.conf;
proxy_pass http://backend;
}
}}
undefinedModSecurity Main Configuration
ModSecurity主配置
bash
undefinedbash
undefined/etc/nginx/modsec/main.conf
/etc/nginx/modsec/main.conf
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/modsecurity.conf
Set to DetectionOnly first, switch to On after tuning
Set to DetectionOnly first, switch to On after tuning
SecRuleEngine On
SecRuleEngine On
Request body handling
Request body handling
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecRequestBodyAccess On
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
Response body handling
Response body handling
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml application/json
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml application/json
Logging
Logging
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec/modsec_audit.log
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsec/modsec_audit.log
Include OWASP Core Rule Set
Include OWASP Core Rule Set
Include /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf
undefinedInclude /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf
undefinedOWASP Core Rule Set Setup
OWASP核心规则集设置
bash
undefinedbash
undefinedDownload and install OWASP CRS
Download and install OWASP CRS
cd /etc/nginx/modsec
git clone https://github.com/coreruleset/coreruleset crs
cp crs/crs-setup.conf.example crs/crs-setup.conf
cd /etc/nginx/modsec
git clone https://github.com/coreruleset/coreruleset crs
cp crs/crs-setup.conf.example crs/crs-setup.conf
Customize CRS settings
Customize CRS settings
cat >> crs/crs-setup.conf << 'EOF'
cat >> crs/crs-setup.conf << 'EOF'
Set paranoia level (1-4, higher = more strict)
Set paranoia level (1-4, higher = more strict)
SecAction "id:900000, phase:1, pass, t:none, nolog, setvar:tx.paranoia_level=2"
SecAction "id:900000, phase:1, pass, t:none, nolog, setvar:tx.paranoia_level=2"
Set anomaly score thresholds
Set anomaly score thresholds
SecAction "id:900110, phase:1, pass, t:none, nolog,
setvar:tx.inbound_anomaly_score_threshold=5,
setvar:tx.outbound_anomaly_score_threshold=4"
setvar:tx.inbound_anomaly_score_threshold=5,
setvar:tx.outbound_anomaly_score_threshold=4"
SecAction "id:900110, phase:1, pass, t:none, nolog,
setvar:tx.inbound_anomaly_score_threshold=5,
setvar:tx.outbound_anomaly_score_threshold=4"
setvar:tx.inbound_anomaly_score_threshold=5,
setvar:tx.outbound_anomaly_score_threshold=4"
Exclude known false positives
Exclude known false positives
SecRule REQUEST_URI "@beginsWith /api/upload"
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=920420" EOF
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=920420" EOF
SecRule REQUEST_URI "@beginsWith /api/upload"
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=920420" EOF
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=920420" EOF
Create rule exclusions file
Create rule exclusions file
cat > /etc/nginx/modsec/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf << 'EOF'
cat > /etc/nginx/modsec/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf << 'EOF'
Exclude rules that cause false positives on specific paths
Exclude rules that cause false positives on specific paths
SecRule REQUEST_URI "@beginsWith /api/webhook"
"id:1000001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:payload"
"id:1000001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:payload"
SecRule REQUEST_URI "@beginsWith /api/webhook"
"id:1000001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:payload"
"id:1000001,phase:1,pass,nolog,ctl:ruleRemoveTargetById=942100;ARGS:payload"
Exclude rules for specific parameters
Exclude rules for specific parameters
SecRule ARGS_NAMES "^content$"
"id:1000002,phase:1,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content" EOF
"id:1000002,phase:1,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content" EOF
undefinedSecRule ARGS_NAMES "^content$"
"id:1000002,phase:1,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content" EOF
"id:1000002,phase:1,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content" EOF
undefinedCustom ModSecurity Rules
自定义ModSecurity规则
bash
undefinedbash
undefined/etc/nginx/modsec/custom-rules.conf
/etc/nginx/modsec/custom-rules.conf
Block requests with known attack tool user agents
Block requests with known attack tool user agents
SecRule REQUEST_HEADERS:User-Agent "@pm sqlmap nikto nmap masscan dirbuster"
"id:10001,phase:1,deny,status:403,log,msg:'Blocked attack tool'"
"id:10001,phase:1,deny,status:403,log,msg:'Blocked attack tool'"
SecRule REQUEST_HEADERS:User-Agent "@pm sqlmap nikto nmap masscan dirbuster"
"id:10001,phase:1,deny,status:403,log,msg:'Blocked attack tool'"
"id:10001,phase:1,deny,status:403,log,msg:'Blocked attack tool'"
Block requests to sensitive paths
Block requests to sensitive paths
SecRule REQUEST_URI "@rx /(.git|.env|.svn|wp-admin|phpmyadmin|adminer)"
"id:10002,phase:1,deny,status:404,log,msg:'Blocked sensitive path access'"
"id:10002,phase:1,deny,status:404,log,msg:'Blocked sensitive path access'"
SecRule REQUEST_URI "@rx /(.git|.env|.svn|wp-admin|phpmyadmin|adminer)"
"id:10002,phase:1,deny,status:404,log,msg:'Blocked sensitive path access'"
"id:10002,phase:1,deny,status:404,log,msg:'Blocked sensitive path access'"
Rate limit by IP (10 requests/second)
Rate limit by IP (10 requests/second)
SecRule IP:REQUEST_RATE "@gt 10"
"id:10003,phase:1,deny,status:429,log,msg:'Rate limit exceeded',
setvar:IP.request_rate=+1,expirevar:IP.request_rate=1"
"id:10003,phase:1,deny,status:429,log,msg:'Rate limit exceeded',
setvar:IP.request_rate=+1,expirevar:IP.request_rate=1"
SecRule IP:REQUEST_RATE "@gt 10"
"id:10003,phase:1,deny,status:429,log,msg:'Rate limit exceeded',
setvar:IP.request_rate=+1,expirevar:IP.request_rate=1"
"id:10003,phase:1,deny,status:429,log,msg:'Rate limit exceeded',
setvar:IP.request_rate=+1,expirevar:IP.request_rate=1"
Block oversized cookies (potential overflow attack)
Block oversized cookies (potential overflow attack)
SecRule REQUEST_HEADERS:Cookie "@gt 4096"
"id:10004,phase:1,deny,status:400,log,msg:'Oversized cookie header'"
"id:10004,phase:1,deny,status:400,log,msg:'Oversized cookie header'"
SecRule REQUEST_HEADERS:Cookie "@gt 4096"
"id:10004,phase:1,deny,status:400,log,msg:'Oversized cookie header'"
"id:10004,phase:1,deny,status:400,log,msg:'Oversized cookie header'"
Virtual patch: block specific CVE exploit pattern
Virtual patch: block specific CVE exploit pattern
SecRule ARGS:filename "@contains ../../"
"id:10005,phase:2,deny,status:403,log,msg:'Path traversal blocked (virtual patch CVE-XXXX-XXXX)'"
"id:10005,phase:2,deny,status:403,log,msg:'Path traversal blocked (virtual patch CVE-XXXX-XXXX)'"
SecRule ARGS:filename "@contains ../../"
"id:10005,phase:2,deny,status:403,log,msg:'Path traversal blocked (virtual patch CVE-XXXX-XXXX)'"
"id:10005,phase:2,deny,status:403,log,msg:'Path traversal blocked (virtual patch CVE-XXXX-XXXX)'"
Require Content-Type on POST requests
Require Content-Type on POST requests
SecRule REQUEST_METHOD "@streq POST"
"id:10006,phase:1,chain,deny,status:400,log,msg:'POST without Content-Type'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" ""
"id:10006,phase:1,chain,deny,status:400,log,msg:'POST without Content-Type'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" ""
undefinedSecRule REQUEST_METHOD "@streq POST"
"id:10006,phase:1,chain,deny,status:400,log,msg:'POST without Content-Type'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" ""
"id:10006,phase:1,chain,deny,status:400,log,msg:'POST without Content-Type'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" ""
undefinedWAF Tuning Workflow
WAF调优工作流
bash
#!/bin/bashbash
#!/bin/bashwaf-tune.sh - Analyze WAF logs for false positives
waf-tune.sh - Analyze WAF logs for false positives
AUDIT_LOG="/var/log/modsec/modsec_audit.log"
TIMEFRAME="24h"
echo "=== WAF Tuning Report ==="
echo "Analyzing last ${TIMEFRAME} of audit logs"
echo ""
AUDIT_LOG="/var/log/modsec/modsec_audit.log"
TIMEFRAME="24h"
echo "=== WAF Tuning Report ==="
echo "Analyzing last ${TIMEFRAME} of audit logs"
echo ""
Top blocked rules
Top blocked rules
echo "--- Top 10 triggered rules ---"
grep -oP 'id "\K[0-9]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top blocked URIs ---"
grep -oP 'REQUEST_URI: \K[^\s]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top blocked IPs ---"
grep -oP 'client \K[0-9.]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- False positive candidates (high-frequency blocks on common paths) ---"
grep -oP 'id "\K[0-9]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn |
while read count rule_id; do if [ "$count" -gt 100 ]; then echo " Rule $rule_id triggered $count times - review for false positive" fi done
while read count rule_id; do if [ "$count" -gt 100 ]; then echo " Rule $rule_id triggered $count times - review for false positive" fi done
undefinedecho "--- Top 10 triggered rules ---"
grep -oP 'id "\K[0-9]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top blocked URIs ---"
grep -oP 'REQUEST_URI: \K[^\s]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top blocked IPs ---"
grep -oP 'client \K[0-9.]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- False positive candidates (high-frequency blocks on common paths) ---"
grep -oP 'id "\K[0-9]+' "$AUDIT_LOG" | sort | uniq -c | sort -rn |
while read count rule_id; do if [ "$count" -gt 100 ]; then echo " Rule $rule_id triggered $count times - review for false positive" fi done
while read count rule_id; do if [ "$count" -gt 100 ]; then echo " Rule $rule_id triggered $count times - review for false positive" fi done
undefinedTroubleshooting
故障排除
| Problem | Cause | Solution |
|---|---|---|
| Legitimate requests blocked | False positives from CRS rules | Set |
| WAF not blocking attacks | Rules in detection-only mode | Switch |
| High latency with WAF enabled | Response body inspection overhead | Disable |
| AWS WAF rules not matching | Rule priority order wrong | Lower priority number = evaluated first; reorder rules |
| ModSecurity crashes nginx | Memory exhaustion on large requests | Increase |
| Cloudflare WAF blocks API calls | Expression too broad | Narrow expression with path or method conditions |
| CRS update breaks application | New rules trigger on existing traffic | Pin CRS version; test updates in staging first |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 合法请求被阻止 | CRS规则误报 | 先设置 |
| WAF未阻止攻击 | 规则处于仅检测模式 | 调优期后切换 |
| 启用WAF后延迟高 | 响应体检查开销 | 如非必要,禁用 |
| AWS WAF规则不匹配 | 规则优先级顺序错误 | 较低优先级数字 = 先评估;重排规则 |
| ModSecurity导致Nginx崩溃 | 大请求导致内存耗尽 | 增加 |
| Cloudflare WAF阻止API调用 | 表达式过于宽泛 | 使用路径或方法条件缩小表达式 |
| CRS更新破坏应用 | 新规则触发现有流量 | 固定CRS版本;先在staging环境测试更新 |
Best Practices
最佳实践
- Start in detection/log mode, switch to blocking after tuning
- Tune rules for at least 1-2 weeks before enforcement
- Monitor blocked requests daily during tuning phase
- Update managed rule sets and CRS regularly
- Create custom rules for application-specific attack patterns
- Use virtual patching to protect against known CVEs while code is being fixed
- Set appropriate rate limits per endpoint
- Maintain exclusion rules documentation with justifications
- Test WAF rules with known attack payloads before deploying
- Keep audit logs for at least 90 days for forensic analysis
- 从检测/日志模式开始,调优后再切换为阻断
- 强制实施前至少调优规则1-2周
- 调优期间每天监控被阻止的请求
- 定期更新托管规则集和CRS
- 为应用特定的攻击模式创建自定义规则
- 使用虚拟补丁在代码修复期间防范已知CVE
- 为每个端点设置适当的速率限制
- 保留排除规则文档并注明原因
- 部署前使用已知攻击payload测试WAF规则
- 保留审计日志至少90天,用于取证分析
Related Skills
相关Skill
- dast-scanning - Web security testing
- ssl-tls-management - HTTPS configuration
- firewall-config - Network-level firewalling
- dast-scanning - Web安全测试
- ssl-tls-management - HTTPS配置
- firewall-config - 网络层防火墙