DEV Community

바람의평온
바람의평온

Posted on

My MySQL 3306 Port Was Open to the Internet: bind 0.0.0.0 Risks & SSH Tunneling

My MySQL 3306 Port Was Open to the Internet: bind 0.0.0.0 Risks & SSH Tunneling

One day, while checking the port forwarding status of my home camera, I suddenly wondered about the status of the MySQL database port on my web server. Initially, I thought there wouldn't be any issues because my Next.js app was running smoothly, connecting to the DB on the same server. I didn't realize then that this complacent thought of 'it works, so it's fine' was a huge trap. When I tried to connect to port 3306 of my server via telnet from an external network, to my surprise, the MySQL server version banner was exposed! The dizzying feeling when I realized my DB port was accessible from anywhere in the world... Just like a first-time parent fumbling through everything, I sometimes find myself flustered by unexpected things in development. I wrote this post to share my experience and solution with other developers.

What this post covers

The risks of MySQL bind-address 0.0.0.0 leading to external exposureThe importance of DB port and security group settings in a cloud environmentHow to check DB port exposure using telnet and ss commandsHow to securely configure MySQL for local-only accessHow to use SSH tunnels for secure external access to the DB*Audience:* Solo developers who use DB only with an app on the same server but inadvertently exposed it externally*Difficulty:* Beginner

Unexpectedly Encountering the 'Open Door' of MySQL Port 3306

As I primarily focus on web service development, I tended to think, 'if it works, it's good enough' for infrastructure and security. Then one day, while checking ports for another device, I thought I'd check my web server's MySQL port. Without much expectation, I tried to connect to port 3306 of my server's IP from another PC using telnet. Usually, the connection should be refused or there should be no response, but I was surprised to see the MySQL server version information displayed directly on the screen. This meant that anyone in the world could attempt to connect to port 3306 of my server, and at the very least, an attacker could identify what my DB server was. My mind went blank for a moment, thinking, 'Is this really true?' Below is a command that shows a similar situation to what I saw when attempting an external connection. telnet serverIP 3306 # If MySQL version information appears when you run this command, it is exposed externally.

bind-address 0.0.0.0 and Cloud Security Groups: A Dangerous Combination

Why did this happen? I investigated and found that the cause was a combination of two main factors. First, the bind-address value in my MySQL configuration file (mysqld.cnf) was set to 0.0.0.0. This setting means that the MySQL server will accept connections from all network interfaces. In other words, it will accept connection attempts not only from the local network but also from the external internet. I assumed that since my local Next.js app connected to the DB via 127.0.0.1 (localhost), it would only be accessible locally. However, with MySQL open to 0.0.0.0, while the app connected fine via 127.0.0.1, the DB had its doors wide open to the world. Second, port 3306 was not blocked in the security group (firewall) of the cloud instance I was using. Cloud services usually recommend blocking all ports by default and only opening necessary ones, but I seem to have overlooked this during initial setup. These two factors combined led to my DB being completely exposed to the internet. In particular, exposing the version banner can be detrimental as it allows attackers to know which MySQL version is being used, making it easier for them to target known vulnerabilities for that version.

Enhancing Security with MySQL Local Binding and SSH Tunneling

Now that the problem was identified, it was time to fix it. The first thing I did was configure MySQL to not accept external connections at all. I opened the MySQL configuration file, /etc/mysql/mysql.conf.d/mysqld.cnf, and modified the bind-address setting. I changed it to 127.0.0.1, as shown below. # /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] bind-address = 127.0.0.1 This setting restricts the MySQL server to accept connections only from the local host (127.0.0.1). This way, my Next.js app running on the same server can continue to connect to the DB via localhost, and external access becomes impossible. After changing the settings, you must restart the MySQL service for the changes to take effect. And when I need to perform DB operations with a GUI tool like DBeaver from outside, I routed it through an SSH tunnel instead of directly opening port 3306. An SSH tunnel securely passes DB traffic through an SSH port (default 22), which is the only port open. This method minimizes external exposure while allowing necessary operations. While I can't definitively say this method is the 'correct answer,' it seemed to be the safest and most effective solution for my environment.

Verifying Changes and Starting Secure DB Operations

After changing the settings, it's crucial to verify that they have been applied correctly. First, I tried connecting to port 3306 again from outside using telnet. This time, I saw a 'Connection refused' message, confirming that access was denied. Ah, I finally felt a sense of relief. Then, to check which address MySQL was listening on internally on the server, I used the following command: sudo ss -tlnp | grep 3306 If the output of this command shows 127.0.0.1:3306, it means MySQL is only accepting connections from the local host. I also confirmed that my Next.js app was still connecting to the DB and functioning normally. External DB tools like DBeaver could connect to the DB without issues through SSH tunnel settings. Through this experience, I deeply realized how dangerous the thought of 'it works, so it's fine' can be. It's important to remember that even if a local app connects fine via 127.0.0.1, the DB itself might be open to 0.0.0.0. Unless external access is absolutely necessary, I believe that the combination of localhost binding + SSH tunnel is one of the safest methods for DB operation. Just like a first-time parent learns one thing at a time while raising a child, development also seems to involve learning and growing through such mistakes.

Conclusion

I've shared my alarming experience of having my web server's MySQL 3306 port wide open to the world and the process of resolving it. By meticulously examining something I could have easily overlooked with 'it works, so what?', I discovered an unexpected security vulnerability. This experience served as a reminder of how crucial it is for developers to pay attention to such details. I encourage anyone reading this to check their own DB port configurations. I believe that a little attention can prevent major security incidents. I hope my fumbling journey can be of some help to others.

Top comments (0)