EC2 Security Group Wide Open Public IPv4
Overview
This check identifies EC2 security groups that contain rules allowing traffic from overly broad public IPv4 address ranges. Specifically, it flags rules that permit non-RFC1918 (public) IPv4 CIDR blocks ranging from /1 to /23. These wide ranges expose your resources to millions of IP addresses when access should typically be restricted to specific, known addresses.
Risk
Overly permissive security group rules create multiple security threats:
- Unauthorized access: Attackers from a vast range of IP addresses can attempt to connect to your resources
- Data exfiltration: Open egress rules allow compromised instances to send data to virtually any destination on the internet
- Command and control: Malicious actors can establish communication channels with compromised resources
- Scanning and exploitation: Your resources become easy targets for automated vulnerability scanners and attacks
- Compliance violations: Many security frameworks (PCI-DSS, HIPAA, SOC 2) require restrictive network access controls
This check has High severity because wide-open CIDR ranges significantly increase your attack surface.
Remediation Steps
Prerequisites
- AWS account access with permissions to view and modify EC2 security groups
- Knowledge of which IP addresses actually need access to your resources
Required IAM permissions
You will need the following permissions:
ec2:DescribeSecurityGroups- View security group rulesec2:DescribeSecurityGroupRules- View individual rule detailsec2:RevokeSecurityGroupIngress- Remove inbound rulesec2:RevokeSecurityGroupEgress- Remove outbound rulesec2:AuthorizeSecurityGroupIngress- Add new inbound rulesec2:AuthorizeSecurityGroupEgress- Add new outbound rules
AWS Console Method
Step 1: Find security groups with overly permissive rules
- Sign in to the AWS Management Console
- Navigate to VPC > Security groups (in the left sidebar)
- Make sure you are in the correct region (e.g., us-east-1)
- Review each security group's Inbound rules and Outbound rules tabs
- Look for rules with CIDR blocks like
0.0.0.0/0,0.0.0.0/8,10.0.0.0/8(if public), or any/1to/23public ranges
Step 2: Remove overly permissive rules
- Select the security group with the problematic rule
- Click the Inbound rules or Outbound rules tab (depending on where the issue is)
- Click Edit inbound rules or Edit outbound rules
- Find the rule with the wide CIDR range and click Delete (the X button)
- Click Save rules
Step 3: Add replacement rules with specific IP addresses
- With the security group still selected, click Edit inbound rules or Edit outbound rules
- Click Add rule
- Set the following:
- Type: Select the appropriate protocol (e.g., SSH, HTTPS, Custom TCP)
- Port range: Enter the required port (e.g., 22 for SSH, 443 for HTTPS)
- Source/Destination: Enter a specific CIDR block of
/24or smaller (e.g.,203.0.113.0/24or ideally203.0.113.45/32for a single IP)
- Click Save rules
Step 4: Consider using security group references (best practice)
Instead of IP-based rules, you can reference other security groups:
- Click Edit inbound rules
- Click Add rule
- For Source, select Custom and enter another security group ID (e.g.,
sg-0123456789abcdef0) - This allows all instances attached to that security group to communicate, regardless of IP address changes
AWS CLI (optional)
List security groups and their rules:
aws ec2 describe-security-groups \
--query 'SecurityGroups[*].[GroupId,GroupName,IpPermissions]' \
--output json \
--region us-east-1
Find rules with wide-open CIDR ranges:
aws ec2 describe-security-groups \
--query 'SecurityGroups[*].{GroupId:GroupId,GroupName:GroupName,Rules:IpPermissions[?contains(IpRanges[].CidrIp, `/0`) || contains(IpRanges[].CidrIp, `/8`)]}' \
--output json \
--region us-east-1
Remove an overly permissive inbound rule:
aws ec2 revoke-security-group-ingress \
--group-id <your-security-group-id> \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 \
--region us-east-1
Replace <your-security-group-id> with your actual security group ID (e.g., sg-0123456789abcdef0).
Add a more restrictive inbound rule:
aws ec2 authorize-security-group-ingress \
--group-id <your-security-group-id> \
--protocol tcp \
--port 22 \
--cidr 203.0.113.45/32 \
--region us-east-1
Replace 203.0.113.45/32 with the specific IP address that needs access.
Remove an overly permissive outbound rule:
aws ec2 revoke-security-group-egress \
--group-id <your-security-group-id> \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0 \
--region us-east-1
Add a restrictive outbound rule:
aws ec2 authorize-security-group-egress \
--group-id <your-security-group-id> \
--protocol tcp \
--port 443 \
--cidr 203.0.113.0/24 \
--region us-east-1
Using IP permissions for more complex rules:
aws ec2 authorize-security-group-ingress \
--group-id <your-security-group-id> \
--ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=203.0.113.0/24,Description=Office network}]' \
--region us-east-1
CloudFormation (optional)
Create a security group with properly scoped rules:
AWSTemplateFormatVersion: '2010-09-09'
Description: Security group with restrictive CIDR rules
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID where the security group will be created
AllowedCidr:
Type: String
Description: Allowed CIDR block for inbound access (use /24 or smaller)
Default: 203.0.113.0/24
AllowedPattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/(2[4-9]|3[0-2])$
ConstraintDescription: Must be a valid IPv4 CIDR block with prefix /24 or smaller (more specific)
Resources:
RestrictiveSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group with restrictive CIDR rules
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: restrictive-sg
- Key: Environment
Value: Production
# Inbound rule - HTTPS from specific CIDR only
IngressHttps:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref RestrictiveSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref AllowedCidr
Description: HTTPS access from allowed network
# Inbound rule - SSH from specific CIDR only
IngressSsh:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref RestrictiveSecurityGroup
IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref AllowedCidr
Description: SSH access from allowed network
# Outbound rule - HTTPS to specific destinations only
EgressHttps:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref RestrictiveSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref AllowedCidr
Description: HTTPS egress to allowed destinations
Outputs:
SecurityGroupId:
Description: ID of the restrictive security group
Value: !Ref RestrictiveSecurityGroup
Export:
Name: !Sub '${AWS::StackName}-SecurityGroupId'
Using security group references instead of CIDR blocks (recommended):
AWSTemplateFormatVersion: '2010-09-09'
Description: Security groups using references instead of CIDR blocks
Parameters:
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC ID where the security groups will be created
Resources:
# Security group for web servers
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for web servers
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: web-server-sg
# Security group for application servers
AppServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for application servers
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: app-server-sg
# Allow web servers to talk to app servers on port 8080
AppServerIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref AppServerSecurityGroup
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref WebServerSecurityGroup
Description: Allow traffic from web servers
Outputs:
WebServerSecurityGroupId:
Description: Web server security group ID
Value: !Ref WebServerSecurityGroup
AppServerSecurityGroupId:
Description: App server security group ID
Value: !Ref AppServerSecurityGroup
Terraform (optional)
Create a security group with properly scoped rules:
provider "aws" {
region = "us-east-1"
}
variable "vpc_id" {
description = "VPC ID where the security group will be created"
type = string
}
variable "allowed_cidr" {
description = "Allowed CIDR block for inbound access (use /24 or smaller)"
type = string
default = "203.0.113.0/24"
validation {
condition = can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/(2[4-9]|3[0-2])$", var.allowed_cidr))
error_message = "CIDR block must have prefix /24 or smaller (more specific)."
}
}
resource "aws_security_group" "restrictive" {
name = "restrictive-sg"
description = "Security group with restrictive CIDR rules"
vpc_id = var.vpc_id
tags = {
Name = "restrictive-sg"
Environment = "Production"
}
}
# Inbound rule - HTTPS from specific CIDR only
resource "aws_vpc_security_group_ingress_rule" "https" {
security_group_id = aws_security_group.restrictive.id
description = "HTTPS access from allowed network"
ip_protocol = "tcp"
from_port = 443
to_port = 443
cidr_ipv4 = var.allowed_cidr
}
# Inbound rule - SSH from specific CIDR only
resource "aws_vpc_security_group_ingress_rule" "ssh" {
security_group_id = aws_security_group.restrictive.id
description = "SSH access from allowed network"
ip_protocol = "tcp"
from_port = 22
to_port = 22
cidr_ipv4 = var.allowed_cidr
}
# Outbound rule - HTTPS to specific destinations only
resource "aws_vpc_security_group_egress_rule" "https" {
security_group_id = aws_security_group.restrictive.id
description = "HTTPS egress to allowed destinations"
ip_protocol = "tcp"
from_port = 443
to_port = 443
cidr_ipv4 = var.allowed_cidr
}
output "security_group_id" {
description = "ID of the restrictive security group"
value = aws_security_group.restrictive.id
}
Using security group references instead of CIDR blocks (recommended):
provider "aws" {
region = "us-east-1"
}
variable "vpc_id" {
description = "VPC ID where the security groups will be created"
type = string
}
# Security group for web servers
resource "aws_security_group" "web_server" {
name = "web-server-sg"
description = "Security group for web servers"
vpc_id = var.vpc_id
tags = {
Name = "web-server-sg"
}
}
# Security group for application servers
resource "aws_security_group" "app_server" {
name = "app-server-sg"
description = "Security group for application servers"
vpc_id = var.vpc_id
tags = {
Name = "app-server-sg"
}
}
# Allow web servers to talk to app servers on port 8080
resource "aws_vpc_security_group_ingress_rule" "app_from_web" {
security_group_id = aws_security_group.app_server.id
description = "Allow traffic from web servers"
ip_protocol = "tcp"
from_port = 8080
to_port = 8080
referenced_security_group_id = aws_security_group.web_server.id
}
output "web_server_security_group_id" {
description = "Web server security group ID"
value = aws_security_group.web_server.id
}
output "app_server_security_group_id" {
description = "App server security group ID"
value = aws_security_group.app_server.id
}
Verification
After completing the remediation:
- Go to VPC > Security groups in the AWS Console
- Select the modified security group
- Review the Inbound rules and Outbound rules tabs
- Confirm that no rules have public CIDR blocks wider than
/24 - Re-run the Prowler check to confirm the issue is resolved
CLI verification commands
List all rules for a specific security group:
aws ec2 describe-security-group-rules \
--filters "Name=group-id,Values=<your-security-group-id>" \
--query 'SecurityGroupRules[*].[SecurityGroupRuleId,IsEgress,IpProtocol,FromPort,ToPort,CidrIpv4]' \
--output table \
--region us-east-1
Check for any remaining wide-open rules:
aws ec2 describe-security-groups \
--group-ids <your-security-group-id> \
--query 'SecurityGroups[*].IpPermissions[*].IpRanges[?contains(CidrIp, `/0`) || contains(CidrIp, `/8`)]' \
--output json \
--region us-east-1
An empty array [] indicates no overly permissive rules remain.
Verify the security group has appropriate rules:
aws ec2 describe-security-groups \
--group-ids <your-security-group-id> \
--query 'SecurityGroups[0].{GroupId:GroupId,InboundRules:IpPermissions,OutboundRules:IpPermissionsEgress}' \
--output json \
--region us-east-1
Additional Resources
- Amazon VPC Security Groups
- Security Group Rules Reference
- VPC Security Best Practices
- AWS CLI: describe-security-groups
- AWS CLI: revoke-security-group-ingress
Notes
- Least privilege principle: Always use the most specific CIDR block possible. Prefer
/32(single IP) when a single system needs access, or/24(256 addresses) for small networks. - RFC1918 private ranges: The private IP ranges
10.0.0.0/8,172.16.0.0/12, and192.168.0.0/16are not flagged by this check because they are non-routable on the public internet. - Security group references: When possible, use security group references instead of CIDR blocks. This allows dynamic access based on group membership rather than static IP addresses.
- Defense in depth: Security groups should be one layer of your security strategy. Also use Network ACLs, VPNs, AWS PrivateLink, and other mechanisms to protect your resources.
- Egress rules matter too: Do not overlook outbound rules. Restricting egress prevents compromised instances from communicating with command-and-control servers or exfiltrating data.
- Default security group: The default security group in each VPC allows all outbound traffic and all inbound traffic from within the same security group. Consider creating custom security groups with explicit rules instead.
- Impact assessment: Before removing rules, verify which resources use the security group and ensure the changes will not disrupt legitimate traffic. Use VPC Flow Logs to analyze traffic patterns.