A database port is reachable from the internet
What this check looks for
Port ‹port› on ‹host› accepts connections from anywhere on the internet, and that port is where ‹service› listens. A database should only ever be reachable from your own application servers.
Why it matters
An exposed database is one password away from your entire dataset, and attackers do not need to find you first — they connect to every address on the internet continuously and try. This is among the most common root causes in published breach reports.
When the check passes, your report says: “No database port answered from the public internet”.
What it costs your score
When this check fails it removes 40 points from your Network score, before the status, confidence and repeat multipliers are applied. Network carries a weight of 5 in the overall score.
It shares the network.exposure family ceiling of 60 points: however many findings that family produces, together they cannot remove more than that from Network. One underlying problem showing up in several places is still one problem.
- Severity
- critical
- Default confidence
- high
- Status when triggered
- fail
- Deduction
- 40 points
- Family cap
- network.exposure · 60
- Category
- Network
- Module
- Net ports
- Fix owned by
- user
- In the ruleset since
- 2026.09
How to fix it
Close port ‹port› to the internet today, and bind ‹service› to a private address.
A database reachable from the public internet is continuously attacked by automated tooling and needs only one weak or reused credential to give up everything it holds.
Confirm what is listening:
sudo ss -tlnp | grep :‹port›. If the address column shows0.0.0.0or*, it is listening on every interface.Change the service's bind address to
127.0.0.1(or a private interface your application servers can reach) and restart it.Deny the port at the host firewall, then allow only the specific application-server addresses that need it.
Apply the same rule at the cloud provider's security group or network ACL — a host firewall does not protect against a permissive cloud rule, and vice versa.
Assume the credentials are known. Rotate the database passwords and review access logs for connections from addresses you do not recognise.
How to confirm it worked
nc -z -w 3 ‹host› ‹port› — should fail with no route or connection refused from outside your network
sudo ss -tlnp | grep :‹port› — should show 127.0.0.1 or a private address, never 0.0.0.0
A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.
Remediation by platform
# 1. Find out what is listening, and on which address.
sudo ss -tlnp | grep -E ':(21|1433|3306|3389|5432|5900|6379|27017)\b'
# 2. Bind the service to the loopback interface or a private address, not 0.0.0.0.
# MySQL/MariaDB: bind-address = 127.0.0.1 (my.cnf)
# PostgreSQL: listen_addresses = 'localhost' (postgresql.conf)
# Redis: bind 127.0.0.1 (redis.conf) + requirepass
# MongoDB: net.bindIp: 127.0.0.1 (mongod.conf)
# 3. Deny the port at the host firewall, then allow only the hosts that need it.
sudo ufw deny 3306/tcp
sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp
# 4. Restart the service and confirm it is no longer bound to a public address.
sudo ss -tlnp | grep 3306Cloudflare's proxy only carries HTTP and HTTPS traffic, so no Cloudflare setting closes a database or remote-administration port. If the origin's address is publicly known, the port is reachable regardless of what the DNS record says.Do both: bind the service privately *and* deny at the firewall. Either alone is one mistake away from re-exposure — a package upgrade that restores a default config, or a firewall flush on reboot.
Listed to rule it out. The fix is at the host firewall or the cloud provider's security group. Cloudflare Tunnel or Access can replace a direct exposure once the port itself is closed.
Technical detail
A TCP connection to ‹host›:‹port› completed. ‹implication›
We did not attempt authentication, send a query, or read anything beyond a greeting the server volunteered, so this finding says only that something accepted a connection on a database port. That is enough to act on: whether or not credentials hold today, every authentication bug in the server becomes an internet-facing bug the moment the port is public, and several of the databases on this list have shipped pre-authentication remote-code-execution vulnerabilities.
Confidence is high rather than confirmed because a transparent proxy or a honeypot could answer in place of a real database. The correct response is the same either way: nothing should be answering on a database port from the public internet.
**Two changes, not one.** Bind the service to loopback or a private address *and* deny the port at the firewall. A service bound only privately is re-exposed by one package upgrade that restores a default; a firewall rule alone is undone by one flush. Doing both means a single mistake is not enough.
If the database genuinely must be reached across the internet — a managed service, a remote analytics client — put it behind a VPN, an SSH tunnel or the cloud provider's private networking rather than an allow-list of addresses. An address allow-list is better than nothing and is not an access control: it authenticates a network path, not a person.
Standards and references
Test this on your domain
Run the check that produces this finding, on its own, against any domain.