Security Hub EC2.19: high-risk ports open to the internet
‘EC2.19 Security groups should not allow unrestricted access to ports with high risk’ is one of the few AWS Security Hub controls rated Critical. In plain English, it means a network rule leaves a sensitive door, such as SSH, RDP or a database port, open to the whole internet. This guide explains exactly what the control checks, how to find the rule that failed and how to close it without breaking anything.
What EC2.19 checks
| Title | Security groups should not allow unrestricted access to ports with high risk |
|---|---|
| Severity | Critical |
| Resource | EC2 security group (AWS::EC2::SecurityGroup) |
| AWS Config rule | restricted-common-ports |
| When it runs | When a security group changes, and periodically |
| Fails when | Any inbound rule allows traffic from 0.0.0.0/0 (all IPv4) or ::/0 (all IPv6) to one of the listed ports |
The port list is fixed by AWS and cannot be customised. The details are in the Security Hub EC2 controls reference; AWS now calls this part of the service Security Hub CSPM.
Ports like these are scanned constantly by automated tools looking for weak passwords, unpatched services and databases with no authentication. An open port is not a breach on its own, but it removes a layer of protection that costs nothing to keep.
The 24 ports it covers
| Port | Usually used for |
|---|---|
| 20, 21 | FTP file transfer |
| 22 | SSH remote login |
| 23 | Telnet |
| 25 | SMTP email |
| 110 | POP3 email |
| 135 | Windows RPC |
| 143 | IMAP email |
| 445 | Windows file sharing (CIFS/SMB) |
| 1433, 1434 | Microsoft SQL Server |
| 3000 | Node.js, Go and Ruby development servers |
| 3306 | MySQL |
| 3389 | Windows Remote Desktop (RDP) |
| 4333 | ahsp |
| 5000 | Python development servers |
| 5432 | PostgreSQL |
| 5500 | fcp-addr-srvr1 |
| 5601 | OpenSearch Dashboards |
| 8080, 8088, 8888 | Proxies and alternative HTTP |
| 9200, 9300 | OpenSearch |
Notice how many are development or database ports. The most common cause we see is a rule added during development, such as opening port 3000 or 5432 to test something from a laptop, that was never removed.
Find the rule that failed
In the console: open Security Hub, choose Controls, search for EC2.19 and open the failed resources. Each finding names the security group. Open it in the EC2 console and look at the Inbound rules tab for a source of 0.0.0.0/0 or ::/0.
With the AWS CLI: list the security groups with a rule open to all IPv4 addresses on, for example, port 22:
aws ec2 describe-security-groups --filters Name=ip-permission.cidr,Values=0.0.0.0/0 Name=ip-permission.from-port,Values=22 --query "SecurityGroups[].[GroupId,GroupName]" --output table
Filters can match across different rules in the same group, so treat the output as a shortlist and confirm the rule in the console. Then check what uses the group before you change it:
aws ec2 describe-network-interfaces --filters Name=group-id,Values=sg-0123456789abcdef0 --query "NetworkInterfaces[].[NetworkInterfaceId,Description]" --output table
Finally, list the group’s inbound rules that are open to the internet, with their rule IDs. A protocol of -1 means the rule allows all traffic, which includes every high-risk port:
aws ec2 describe-security-group-rules --filters Name=group-id,Values=sg-0123456789abcdef0 --query "SecurityGroupRules[?!IsEgress && (CidrIpv4=='0.0.0.0/0' || CidrIpv6=='::/0')].[SecurityGroupRuleId,IpProtocol,FromPort,ToPort,CidrIpv4,CidrIpv6]" --output table
Fix it
- Decide who actually needs access. Usually it is your own team, your application servers or a load balancer, not the whole internet.
- Remove the open rule by its ID. Use the rule ID from the list above:
Revoking by protocol, port and source only removes a rule whose values match exactly, so a rule that opens a port range or all traffic would be left in place. Remove any matchingaws ec2 revoke-security-group-ingress --group-id sg-0123456789abcdef0 --security-group-rule-ids sgr-0123456789abcdef0::/0rule as well, then run thedescribe-security-group-rulescommand again to confirm the rules have gone. - Add a narrower rule if something still needs access. Allow a known IP range, or better, another security group, such as your application’s group for a database port.
- Replace SSH and RDP with Session Manager. AWS Systems Manager Session Manager gives shell access, and port forwarding for remote desktop, without any inbound port open at all. The console keeps a history of sessions, and you can turn on logging of shell session commands and output to Amazon S3 or CloudWatch Logs. That content logging is not available for port-forwarding or SSH sessions.
- Re-check. Because the control runs when the security group changes, the finding should update shortly after the fix.
bearhug lists this finding in plain English and can close an open port in one click, with rollback if something turns out to need it.
When a port really must be public
Occasionally a service on one of these ports does need to be reachable from anywhere, such as a mail server on port 25. Before accepting that:
- Put web services behind a load balancer on port 443 and keep the application port private. That covers most cases of 3000, 5000, 8080 and 8888.
- Never expose database or search ports. MySQL, PostgreSQL, SQL Server and OpenSearch should only accept connections from inside your network.
- Record the exception. If the rule must stay, suppress the finding in Security Hub with a note explaining why, so the next person does not have to rediscover the reason.
A related control, EC2.18 Security groups should only allow unrestricted incoming traffic for authorized ports, is rated High and fails on unrestricted access to any port other than the ones you authorise. See our guide to Security Hub findings explained in plain English for more controls like these.