ELB classic load balancers should be configured with defensive or strictest desync mitigation mode
HTTP desync attacks send ambiguous requests that a load balancer and backend parse differently, letting an attacker smuggle requests, poison caches, or hijack other users' sessions. The "monitor" mode only emits CloudWatch metrics without blocking anything, so backends are fully exposed.
Setting the mode to "defensive" (the AWS default) or "strictest" causes the Classic Load Balancer to actively modify or reject requests that violate RFC 7230. "Strictest" drops any ambiguous request outright, which can break poorly behaved clients but provides the strongest protection.
Retrofit consideration
Switching from "monitor" to "strictest" may reject legitimate but non-conformant HTTP requests from older clients. Apply to a non-production environment first via a separate terraform apply targeting that workspace. If you see unexpected 400s, "defensive" is the safer intermediate step.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "elb" {
source = "pcidss.compliance.tf/terraform-aws-modules/elb/aws"
version = ">=4.0.0,<5.0.0"
health_check = {
healthy_threshold = 2
interval = 30
target = "HTTP:80/"
timeout = 5
unhealthy_threshold = 2
}
internal = true
listener = [
{
instance_port = 80
instance_protocol = "HTTP"
lb_port = 80
lb_protocol = "HTTP"
}
]
name = "abc123"
security_groups = ["sg-abc12345"]
subnets = ["subnet-abc123", "subnet-def456"]
}
module "elb" {
source = "acscessentialeight.compliance.tf/terraform-aws-modules/elb/aws"
version = ">=4.0.0,<5.0.0"
health_check = {
healthy_threshold = 2
interval = 30
target = "HTTP:80/"
timeout = 5
unhealthy_threshold = 2
}
internal = true
listener = [
{
instance_port = 80
instance_protocol = "HTTP"
lb_port = 80
lb_protocol = "HTTP"
}
]
name = "abc123"
security_groups = ["sg-abc12345"]
subnets = ["subnet-abc123", "subnet-def456"]
}
module "elb" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/elb/aws"
version = ">=4.0.0,<5.0.0"
health_check = {
healthy_threshold = 2
interval = 30
target = "HTTP:80/"
timeout = 5
unhealthy_threshold = 2
}
internal = true
listener = [
{
instance_port = 80
instance_protocol = "HTTP"
lb_port = 80
lb_protocol = "HTTP"
}
]
name = "abc123"
security_groups = ["sg-abc12345"]
subnets = ["subnet-abc123", "subnet-def456"]
}
If you use terraform-aws-modules/elb/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "elb" {
source = "terraform-aws-modules/elb/aws"
version = ">=4.0.0,<5.0.0"
health_check = {
healthy_threshold = 2
interval = 30
target = "HTTP:80/"
timeout = 5
unhealthy_threshold = 2
}
internal = true
listener = [
{
instance_port = 80
instance_protocol = "HTTP"
lb_port = 80
lb_protocol = "HTTP"
}
]
name = "abc123"
security_groups = ["sg-abc12345"]
subnets = ["subnet-abc123", "subnet-def456"]
}
Use AWS provider resources directly. See docs for the resources involved: aws_elb.
resource "aws_elb" "this" {
internal = true
listener {
instance_port = 80
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
subnets = ["subnet-abc123", "subnet-def456"]
desync_mitigation_mode = "defensive"
}
What this control checks
The policy evaluates desync_mitigation_mode on every aws_elb resource. The argument takes three values: "monitor", "defensive", and "strictest". Setting it to "defensive" or "strictest" passes; "monitor" fails. If the argument is omitted, Terraform defaults to "defensive", which also passes.
Common pitfalls
Default value masks explicit intent
Omitting
desync_mitigation_modepasses the check today because Terraform defaults to"defensive". But if someone changes the attribute directly via the console or CLI, the drift is silent until the nextterraform planor Config evaluation catches it. Set the value explicitly in every resource block.Strictest mode can break non-conformant clients
Setting
desync_mitigation_mode = "strictest"rejects any request that doesn't strictly conform to RFC 7230. Applications receiving traffic from legacy systems, IoT devices, or HTTP libraries that send malformed headers such as duplicate Content-Length will start returning 400 errors. Check theDesyncMitigationMode_NonCompliant_Request_CountCloudWatch metric before making the switch.Control applies only to Classic Load Balancers
This check only covers
aws_elb(Classic Load Balancer). Application Load Balancers (aws_lbwithload_balancer_type = "application") have their owndesync_mitigation_modeargument and are covered by a separate control. Passing this check says nothing about your ALBs.
Audit evidence
Auditors typically want AWS Config rule evaluation results showing all Classic Load Balancers pass the desync mitigation check. The CLI command aws elb describe-load-balancer-attributes --load-balancer-name <name> returns DesyncMitigationMode under AdditionalAttributes as point-in-time evidence. Console screenshots from the EC2 Load Balancers page, showing the attribute under the Description tab, are also acceptable.
For continuous compliance, AWS Config conformance pack reports or CSPM findings filtered to this control show ongoing enforcement rather than a single snapshot.
Framework-specific interpretation
PCI DSS v4.0: Requirement 6.2.1 calls for software attack prevention as part of secure development practices. HTTP request smuggling falls squarely in that category. Setting mitigation to defensive or strictest reduces exposure at the load balancer layer for cardholder data environments running behind Classic ELBs.
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
elb_classic_lb_desync_mitigation_modeAWS Config Managed Rule:
CLB_DESYNC_MODE_CHECKCheckov Check:
CKV_AWS_328Powerpipe Control:
aws_compliance.control.elb_classic_lb_desync_mitigation_modeProwler Check:
elb_desync_mitigation_modeAWS Security Hub Control:
ELB.14
Last reviewed: 2026-03-09