Skip to main content

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 rules
  • ec2:DescribeSecurityGroupRules - View individual rule details
  • ec2:RevokeSecurityGroupIngress - Remove inbound rules
  • ec2:RevokeSecurityGroupEgress - Remove outbound rules
  • ec2:AuthorizeSecurityGroupIngress - Add new inbound rules
  • ec2:AuthorizeSecurityGroupEgress - Add new outbound rules

AWS Console Method

Step 1: Find security groups with overly permissive rules

  1. Sign in to the AWS Management Console
  2. Navigate to VPC > Security groups (in the left sidebar)
  3. Make sure you are in the correct region (e.g., us-east-1)
  4. Review each security group's Inbound rules and Outbound rules tabs
  5. 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 /1 to /23 public ranges

Step 2: Remove overly permissive rules

  1. Select the security group with the problematic rule
  2. Click the Inbound rules or Outbound rules tab (depending on where the issue is)
  3. Click Edit inbound rules or Edit outbound rules
  4. Find the rule with the wide CIDR range and click Delete (the X button)
  5. Click Save rules

Step 3: Add replacement rules with specific IP addresses

  1. With the security group still selected, click Edit inbound rules or Edit outbound rules
  2. Click Add rule
  3. 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 /24 or smaller (e.g., 203.0.113.0/24 or ideally 203.0.113.45/32 for a single IP)
  4. Click Save rules

Step 4: Consider using security group references (best practice)

Instead of IP-based rules, you can reference other security groups:

  1. Click Edit inbound rules
  2. Click Add rule
  3. For Source, select Custom and enter another security group ID (e.g., sg-0123456789abcdef0)
  4. 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:

  1. Go to VPC > Security groups in the AWS Console
  2. Select the modified security group
  3. Review the Inbound rules and Outbound rules tabs
  4. Confirm that no rules have public CIDR blocks wider than /24
  5. 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

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, and 192.168.0.0/16 are 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.