AnalysisType: policy
Filename: aws_application_load_balancer_web_acl.py
PolicyID: "AWS.ApplicationLoadBalancer.WebACL"
DisplayName: "AWS Application Load Balancer Web ACL"
Enabled: false
ResourceTypes:
- AWS.ELBV2.ApplicationLoadBalancer
Tags:
- AWS
- Configuration Required
- Security Control
- Initial Access:Exploit Public-Facing Application
Reports:
MITRE ATT&CK:
- TA0001:T1190
Severity: High
Description: >
This policy validates that all application load balancers have an associated Web ACl to enforce protections against various web attacks.
Runbook: >
https://docs.runpanther.io/alert-runbooks/built-in-policies/aws-application-load-balancer-has-web-acl
Reference: https://aws.amazon.com/blogs/aws/aws-web-application-firewall-waf-for-application-load-balancers/
Tests:
- Name: Load Balancer Does Not Have Required WAF Web ACL
ExpectedResult: false
Resource:
{
"AvailabilityZones":
[
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2d",
},
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2a",
},
],
"CanonicalHostedZoneId": "111222333AAA",
"CreatedTime": "2019-01-01T00:00:00.00Z",
"DNSName": "example-lb-111222333.us-west-2.elb.amazonaws.com",
"IpAddressType": "ipv4",
"LoadBalancerArn": "TEST_LOAD_BALANCER_ARN",
"LoadBalancerName": "TEST_LOAD_BALANCER",
"Scheme": "internal",
"SecurityGroups": ["sg-111222333444"],
"State": { "Code": "active", "Reason": null },
"Type": "application",
"VpcId": "vpc-111222333",
"WebAcl": null,
}
- Name: Load Balancer Does Not Require WAF Web ACL
ExpectedResult: true
Resource:
{
"AvailabilityZones":
[
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2d",
},
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2a",
},
],
"CanonicalHostedZoneId": "111222333AAA",
"CreatedTime": "2019-01-01T00:00:00.00Z",
"DNSName": "example-lb-111222333.us-west-2.elb.amazonaws.com",
"IpAddressType": "ipv4",
"LoadBalancerArn": "TEST_LOAD_BALANCER_ARN_NO_WEB_ACL",
"LoadBalancerName": "TEST_LOAD_BALANCER",
"Scheme": "internal",
"SecurityGroups": ["sg-111222333444"],
"State": { "Code": "active", "Reason": null },
"Type": "application",
"VpcId": "vpc-111222333",
"WebAcl": null,
}
- Name: Load Balancer Has Correct WAF Web ACL
ExpectedResult: true
Resource:
{
"AvailabilityZones":
[
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2d",
},
{
"LoadBalancerAddresses": null,
"SubnetId": "subnet-111222333",
"ZoneName": "us-west-2a",
},
],
"CanonicalHostedZoneId": "111222333AAA",
"CreatedTime": "2019-01-01T00:00:00.00Z",
"DNSName": "example-lb-111222333.us-west-2.elb.amazonaws.com",
"IpAddressType": "ipv4",
"LoadBalancerArn": "TEST_LOAD_BALANCER_ARN",
"LoadBalancerName": "TEST_LOAD_BALANCER",
"Scheme": "internal",
"SecurityGroups": ["sg-111222333444"],
"State": { "Code": "active", "Reason": null },
"Type": "application",
"VpcId": "vpc-111222333",
"WebAcl":
{
"DefaultAction": { "Type": "ALLOW" },
"MetricName": "examplewebacl",
"Name": "example-web-acl",
"Rules":
[
{
"Action": { "Type": "BLOCK" },
"ExcludedRules": null,
"OverrideAction": null,
"Priority": 2,
"RuleId": "111222-1111-1111-1111-111222333444",
"Type": "REGULAR",
},
{
"Action": { "Type": "COUNT" },
"ExcludedRules": null,
"OverrideAction": null,
"Priority": 1,
"RuleId": "111222-1111-2222-3333-111222333444",
"Type": "REGULAR",
},
],
"WebACLArn": "arn:aws:waf-regional:us-west-2:123456789012:webacl/111222-1111-2222-3333-111222333444",
"WebACLId": "TEST_WAF_WEB_ACL_ID",
},
}
# ------ paired body: aws_application_load_balancer_web_acl.py ------
from panther_base_helpers import deep_get
# MAPPINGS is a dictionary where the Key is an application load balancer ARN, and the
# Value is a WAF web ACL ID. For each Load Balancer ARN present in MAPPINGS,
# this rule verifies that the load balancer has the associated Web ACL
MAPPINGS = {
"TEST_LOAD_BALANCER_ARN": "TEST_WAF_WEB_ACL_ID",
}
def policy(resource):
# Check if a Web ACL is required for this load balancer
if resource["LoadBalancerArn"] not in MAPPINGS:
return True
# Check if a Web ACL exists for this load balancer
if resource["WebAcl"] is None:
return False
# Check that the correct Web ACL is assigned for this load balancer
return deep_get(resource, "WebAcl", "WebACLId") == MAPPINGS[resource["LoadBalancerArn"]]