Home » Php » php – MySQL – is it safe to check the 6 first characters of a query to be sure it is a SELECT?

php – MySQL – is it safe to check the 6 first characters of a query to be sure it is a SELECT?

Posted by: admin July 12, 2020 Leave a comment

Questions:

A coworker of mine has written something really awful.
Our boss wanted to be able to write any SELECT queries from the Back Office of our website, then get the result in CSV format.

Those queries will be executed by our PRODUCTION MySQL cluster.
This back-office feature should reject any non-SELECT queries.

So he comes up with a really naive solution for that.
This is the PHP code:

function checkQuery()
{
        $sQuery = trim($_POST['query']);
        if (empty($sQuery))
                return false;
        $sCmd = substr($sQuery, 0, 6);
        if (strtolower($sCmd) != 'select')
                return errorDiv('Only SELECT queries are authorized');
        return $sQuery;
}

For people not knowing PHP, this code removes white space from the begining and the end of an SQL query string, then get the 6 first characters, transforms them to lowercase characters, and if it doesn’t match (erf… loosely match) ‘select’, the query is rejected.

It looks awful and disgusting to me.
I tried to convince him to create at least another MySQL user, with limited privileges, but he is too lazy to do that.

However I cannot prove him that some kind of hacks are possible.

He uses mysql_query() to run the query string, and this driver rejects multiple queries at once.
I can’t find any real exploit, but I think there are at least 50% of chance that something bad can happen.

Maybe some NUL char, or some obscur utf-8 chars can do the trick?

How to&Answers:

I do not see any way to bypass your colleague’s checkQuery routine. It would be trivial if you weren’t using the mysql_* functions in PHP, but as they only allow a single statement to be executed they at least aren’t vulnerable to such classics as the Little Bobby Tables style of attack. And in fact, this is one of the key reasons why the standard query functions in both mysql_* and mysqli_* don’t allow multiple statements (this is described in the mysqli Multiple Statements documentation.)

I’ve looked at other routes and I’m pretty satisfied that your colleague’s code does actually work. (There’s a way of generating false positives, e.g. triggering the detection even on a valid SELECT query, but that’s not much use to you.) And as long as you don’t have functions that change data as a side-effect, it shouldn’t be possible to do anything other than SELECT with a statement that begins SELECT....

There’s a possibility of writing new files to the server using SELECT ... INTO OUTFILE. That’s restricted to new files, so you can’t clobber /etc/passwd, or whatever, but there are dangers.

The most dangerous scenario I can see would be if your MySQL server is the same machine as your PHP server, your MySQL user can write to your PHP directory, and the user you’re using has FILE privilege. In that case, a carefully crafted SELECT ... INTO OUTFILE along these lines:

SELECT '<?php echo \'DANGER WILL ROBINSON!\';' INTO OUTFILE '/Users/matt/Sites/dangerous.php'

might allow someone to create a new PHP file which could be executed by then browsing to it, at which point someone can execute arbitrary PHP code. I tested this potential exploit locally and after giving a user SELECT and FILE privilege and allowing write access by MySQL to my wwwroot, I could successfully create a PHP file with the above code in it and execute it in my browser.

Apart from attacks along that particular line, which may be impossible if the user you’re currently using doesn’t have FILE privilege, I can’t see much danger. I’m aware this doesn’t help you much, but it does seem to be the answer to your question.

However, I’d still strongly advise that you use MySQL authentication and security to create a new user for this application that only has SELECT permission. This is the built-in, database-level, utterly standard, well-proven, well-tested way of solving the problem, and limits potential future problems.

For example, if someone hacks your PHP server in some other way, to the extent of being able to read your MySQL user’s password, they then have access to an authenticated user with high privileges they can use for other attacks (perhaps by throwing their own scripts onto your PHP server and running them.) If the MySQL user is SELECT-only, that limits the damage that can be done to your data/database even if your PHP server is completely compromised.

Answer:

The correct approach to this feature (if you can’t convince him it is a bad idea) is to run the queries as a limited MySQL user. Grant that user only the SELECT permission. You can also limit the table set if desired.