Thanks to the generous contributions of Timur Shaidullin, jOOQ 3.11 will now support GRANT
and REVOKE
statements through #6812. While implementing integration tests for these new features, I had researched the different ways how these statements work on a variety of databases, and the good news is, they’re all mostly quite standardised (in fact, they’re even part of the SQL standard).
The less good news is that a lot of people do not seem to care about security – at all!
Granted (great pun!) MySQL seems to be lacking a feature here. When I create a new user:
-- Obviously, you will choose a better password CREATE USER 'NO_RIGHTS'@'%' IDENTIFIED BY 'NO_RIGHTS';
… then this user can connect to the server, but not to any databases yet. From JDBC, we most often use the connection string:
jdbc:mysql://host/database
After all, we don’t just want to connect to a server, but also to a database. This is not allowed, and that’s a reasonable default, of course:
Caused by: java.sql.SQLSyntaxErrorException: Access denied for user 'NO_RIGHTS'@'%' to database 'test' at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:112) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:116) at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:853) at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:440) at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241) at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:221) at org.jooq.test.jOOQAbstractTest.getConnection1(jOOQAbstractTest.java:1132) at org.jooq.test.jOOQAbstractTest.getConnection0(jOOQAbstractTest.java:1064) ... 31 more
No doubt about that. But how can I grant the right to connect to this database? There is no such grant in the documentation:
https://dev.mysql.com/doc/refman/8.0/en/grant.html
That’s unfortunate, because in order to start working with the database, the first thing I’d like to do is something like the hypothetical:
GRANT CONNECT ON test TO 'NO_RIGHTS'@'%';
From then on, I could start working and add more and more grants, step by step, depending on what I would like to allow the user NO_RIGHTS to do on the database. I have created a feature request for this: https://bugs.mysql.com/bug.php?id=89030
A workaround
In fact, any grant to any object inside of the database implicitly grants the “CONNECT” privilege to this user. I could, for instance, do this:
GRANT SELECT ON test.bank_account_transactions TO 'NO_RIGHTS'@'%';
But I don’t want to do that! That’s already much too big of a GRANT for the fact that I don’t actually want this user to be able to do anything at this point.
Here’s a more viable (but ugly) workaround:
-- Create a dummy view that is never going to be used: CREATE VIEW v_unused AS SELECT 1; -- Now grant showing (not selecting) this view to the user: GRANT SHOW VIEW ON test.v_unused TO 'NO_RIGHTS'@'%';
Among all the possible grants, that’s about the most harmless I could find that will now allow this user to connect to the test database (and to show this view):
SHOW TABLES;
Yielding
Tables_in_test -------------- v_unused
Note, with my security background and being the pessimist I am, I don’t even grant the SELECT privilege on this view, but just the SHOW VIEW privilege.
I could possibly live with that. Or if I cannot create any view myself, perhaps I could grant “SHOW VIEW” of all views. I don’t like that thought, but it seems to be about the least intrusive privilege to get that implied “CONNECT” privilege.
What does the Internet recommend?
I was obviously googling for this topic. The best way to google for this is by googling the JDBC error message:
Because that’s what people do, right? Google error messages. I would have expected tons of advice how to solve that particular problem. The problem of getting the “CONNECT” privilege, and the “CONNECT” privilege only
Here are some of the first results, which all shocked me completely. In order to illustrate and emphasise that shock, I will use emojis:
At first, it recommends this:
mysql> CREATE USER 'finley'@'localhost' IDENTIFIED BY 'password'; mysql> GRANT ALL PRIVILEGES ON *.* TO 'finley'@'localhost' -> WITH GRANT OPTION;
“GRANT ALL”
OK, perhaps not the best first example, I mean I really don’t trust this guy finley. But OK, it’s the manual and it later proceeds to showing more restrictive GRANT options.
Plenty of talk about:
CREATE DATABASE `zabbix_db`; GRANT ALL PRIVILEGES ON `zabbix_db`.* TO `zabbix_user`@'localhost' IDENTIFIED BY 'XXXXXXXXX'; FLUSH PRIVILEGES;
“GRANT ALL”
Great, so now we know that this particular user can do everything on this forum. Excellent. Let’s find a SQLi vulnerability somewhere.
3. A random ServerFault question
Two answers suggesting:
CREATE USER 'username'@'192.168.22.2' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON databasename.* TO 'username'@'192.168.22.2';
And also
GRANT ALL on database.* to 'user'@'localhost' identified by 'paswword';
“GRANT ALL”
Great advice, folks!
… where the user asking the question had already followed one of the previous forums’ advice:
GRANT USAGE ON *.* TO 'someusr'@'localhost' GRANT ALL PRIVILEGES ON `qorbit_store`.* TO 'someusr'@'localhost'
Great, so qorbit_store also has a user with total privileges.
“GRANT ALL”
So… this is the travis CI server itself, isn’t it?
mysql -u root -e "CREATE DATABASE mydb;" mysql -u root -e "GRANT ALL PRIVILEGES ON mydb.* TO 'travis'@'%';";
What are you folks thinking??
“GRANT ALL”
I am trying to get some software installed but am running into problems. […] I used the command “grant all privileges on newdb.* to sam@localhost;” […]
True to the idea, let’s just hammer out commands until this dang thing works. The unix equivalent would be:
chmod -R 777 *
Solves all problems right?
“GRANT ALL”
[…] you may need to give your MySQL user proper permissions. Log in to MySQL as the MySQL root user and issue these commands:
GRANT ALL PRIVILEGES ON database_name TO user@host IDENTIFIED BY ‘password’;
FLUSH PRIVILEGES;
“Proper permissions” – GRANT ALL is never proper!
“GRANT ALL”
To be fair, this particular forum then proceeds advising
If you want (or need) to be more restrictive with database permissions: You will need to at least grant the ALTER, CREATE, DELETE, INSERT, SELECT, and UPDATE permissions to your database user. ALTER and CREATE permissions are only needed for installing and upgrading Geeklog, as well as for installing plugins and other add-ons.
But as we’ve seen before, people don’t read all that advice, they just use the first thing that works.
8. Another random forum, on drupal
This is a true gem. Not only GRANT ALL, but also an unredacted password. I redacted it, but you can easily look this up if you want some data exchange business with swisha swisha:
2. define('MYSQL_HOST','localhost'); 3. define('MYSQL_USER','swhisa_swhisa'); 4. define('MYSQL_PASS','12345678'); // Redaction mine 5. define('MYSQL_DB','swhisa_swhisadb'); 6. if(!mysql_connect (MYSQL_HOST, MYSQL_USER, MYSQL_PASS)){die (mysql_error()); } mysql_select_db(MYSQL_DB); 7. 8. $grt = "GRANT ALL ON *.* TO 'swhisa_swhisa'@'%'"; 9. mysql_query($grt) or die(mysql_error());
“GRANT ALL” and public password
In case you wondered, this emoji is me losing faith in humanity. Not a single post recommended first to grant the minimal rights necessary for the user. All of them (including the official documentation) just defaulted to granting everything.
Wrap up
For crying out loud. I googled for an error message about being unable to connect, and almost every single answer Google gave me was someone recommending to just GRANT ALL. That’s like googling for an alternative to tacos and being recommended to join the NASA / SpaceX missions to Mars because of the excellent astronaut food.
Of course, as a developer we like to GRANT ALL. That’s convenient. If we don’t ship to production but just want to play around with some features, we might not need security.
But the fact is that if the readers of such posts are never exposed to even the mere idea of thinking about a security model and user design where not every user can access (let alone modify, drop, shutdown) every resource, then these readers are simply going to ship GRANT ALL to production.
SQL injection is a serious threat on its own, but if the database user that exposes the vulnerability has complete privileges on your database, then you’re doubly doomed.
Always start with users that have no rights – then add rights as you need them, and add them sparingly!
Now go out there and start REVOKING (Perhaps wait until after everyone leaves for their Christmas Holiday, though )