Security isn't a feature—it's an integral part of design, no different than internationalization or accessibility. Good security design aims to make it difficult at every possible step to breach your system, and within the world of databases, this is often referred to as “hardening” your database.
Some of those steps include keeping software up-to-date, restricting where a system can be accessed, strengthening passwords, and regularly auditing access, though there are plenty more. While these tips are broadly applicable, we’ll go over how to use these techniques to make your Cloud SQL for MySQL database instance as difficult to breach as possible.
There’s no sugarcoating it: There are attackers who can easily scan the entire internet in minutes and crack passwords in seconds. While it’s sometimes necessary to leave a database accessible through a public IP network, this leaves your database exponentially more vulnerable. One way to mitigate the risks of public IP is to restrict database access to a limited set of IP addresses.
That said, the better solution is to only allow access inside a virtual private cloud (VPC), and only allow operator connections inside a bastion host. In Google Cloud, this solution is called private IP. If your MySQL instance is only accessible inside a VPC, then an attacker will first have to breach the VPC before trying to breach your instance.
Every MySQL minor version has release notes, and they almost invariably contain a section regarding security updates. If your instance is several versions out of date, there are several versions of security fixes that your database doesn't have.
In addition to fixing security issues over time, MySQL also periodically introduces several entirely new security features. For example, MySQL 8.0 introduced many ways to harden MySQL schemas and users such as roles, failed sign-in tracking, and random password generations.
Say you're working at a coffee shop, and you want to run some queries, so you connect to MySQL over the shop’s free Wi-Fi network. If you didn't use TLS (Transport Layer Security), you’re effectively shouting your username and password out over TCP (the Transmission Control Protocol, one of the main protocols of the internet). Enforce TLS on your MySQL instance and you can work over your latte without worrying if other customers are snooping on the network.
SQL injection
Often it's not the database but the application that's most vulnerable to an attack. Many applications have fallen victim to SQL injection attacks, which is where an attacker inserts malicious input that your application thinks is a valid SQL statement. The best protection against these attacks is to use prepared statements when creating SQL statements instead of concatenating user input with SQL statements.
Secrets in your repo
Countless databases have been breached due to a developer inadvertently pushing their username and password into their source code repo. Instead, use a tool like Google Cloud's Secret Manager, which manages all kinds of secrets, including your database password.
Logging passwords
Finally, use your logging framework's filtering tools to filter your database's password out of your application logs. Examples include Log4j Filters or Rails ParameterFilter, but most logging frameworks should have an equivalent. If you don't, you'll need to ensure that your application logs are secure for your database to be secure.
Consider using password validation to ensure that all passwords on your database are sufficiently complex. For application passwords, make them long, complex, and rotate them frequently. Secret managers are especially useful so that you don’t need to memorize complex and often-changing passwords.
If a human (and not an application) will use a database user, follow NIST's standards and prioritize length above all else. Password expiration dates and excessively complex password requirements frequently cause people to delegate their memory to plaintext files and sticky notes, which undermines the whole purpose of these rules.
Better yet, consider leveraging IAM authentication for non-application users. IAM authentication delegates connection establishment to Google Cloud's IAM offering, meaning that risk is mitigated to only the IAM user, instead of the IAM user and the database user.
Finally, the MySQL defaults hash, not salt, your database password. This means that users with the same password will have identical authentication strings, trivializing cracking weak passwords with a rainbow-table attack. Notice how the two following MySQL users have the same authentication_string.
Consider setting the flag default_authentication_plugin to caching_sha2_password. This ensures that two users with the same password will have a different hash. Notice how the two users have different authentication_string values in the following example.
Limiting access doesn’t only mean securing access from the outside; it also means regularly auditing access from within.
Audit your application: If it only reads from the billing, product, and customer tables, does it really need access to everything else? Leaving root access available to your database means an attacker can cause tremendous damage to your database. Instead, think of every requirement your instance has, itemize the privileges they require, and then create a database user for each one. If an attacker breaches your database under one of these database users, you’ll have limited the scope of the damage.
Audit your users: How many of your database’s users really need admin privileges over your instances? How many of those users are still at your organization, or are attached to inactive projects, or are inactive themselves? Access tokens can leak, and insider threat is an evergreen concern for most systems. Ensuring you have as few users as possible can help your instance to protect against this class of issues.
Audit your ‘admin’: Consider removing or renaming users like ‘root’ or ‘admin’. These are easy targets for attackers, and if they don’t exist, your system is ever so slightly harder to breach. This is called security through obscurity, and while it shouldn't be the first line of defense, it can add a thin layer of security to your MySQL database.
Regular data backups and verifiable data recovery plans are critical to healthy database management. But even with regular backups, you can still lose all data written after the last backup. One of the benefits of the MySQL ecosystem is point-in-time recovery (PITR), which lets you recover data at any point in time. To make PITR possible, enable binary logs on your Cloud SQL for MySQL instances.
If you've done all the above, you are well-prepared and well-protected. That said, no security design would be complete without ongoing vigilance—database auditing. The Cloud SQL for MySQL Audit Plugin helps you track unusual behavior in the event of a breach.
With the above in place, you’ll have made any path to a database breach much harder for an attacker. An attacker would have to breach your application and hope that the application's database user has enough privileges for their attack, or they would have to breach your VPC, find an existing username, crack that password, and hope that user has enough privileges to run their attack. All the while, you can view MySQL database logs or audit logs to monitor any usual behavior on the database and react accordingly.
This is what security by design means. Attackers will continue to discover ways to exploit machines. That's why it's important to design your system with as many layers of protection as possible to keep your database protected.
Start building on Google Cloud with $300 in free credits and 20+ always free products.