Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow SELECT queries and prevent others?

In our application, users can create custom export functions in form of SQL statements. Something like this:

SELECT name, age, date_birth FROM users WHERE group_id = 2

I don't want them to clear the whole database by inserting a DELETE statement. My ideas would be:

  • Use an SQL account, which is only allowed to SELECT. (I don't want to do this, if there are alternatives.)
  • Use a magic regex, that checks whether the query is dangerous or not. (Would this be good? Is there already such a regex?)

We are using PHP PDO.

like image 782
Christoph Bühler Avatar asked May 07 '15 08:05

Christoph Bühler


People also ask

How do you restrict data in SQL query?

The SQL LIMIT clause constrains the number of rows returned by a SELECT statement. For Microsoft databases like SQL Server or MSAccess, you can use the SELECT TOP statement to limit your results, which is Microsoft's proprietary equivalent to the SELECT LIMIT statement.

Why select * is not recommended?

When you use select * you're make it impossible to profile, therefore you're not writing clear & straightforward code and you are going against the spirit of the quote. select * is an anti-pattern. So selecting columns is not a premature optimization.

How do you avoid subquery in select statement?

Use an INNER JOIN to join with your max ID's. Assuming the ID column is indexed, this is likely as fast as its going to get. MySQL will create many temporary tables, while in my example there will be only one.

Can a select statement cause blocking?

The SELECT statements can force locking via hints and they can get blocked for other reasons.

How can I reduce the I/O resources of a SELECT query?

Tune the SELECT query so it uses fewer I/O resources. Run the query at a quiet time. Run the query on a separate copy of the database (e.g. a readable secondary).

Does SELECT query block updates?

SELECT can block updates. A properly designed data model and query will only cause minimal blocking and not be an issue. The 'usual' WITH NOLOCK hint is almost always the wrong answer. The proper answer is to tune your query so it does not scan huge tables.

Is it possible to protect a Power Query in Excel?

Is it Possible to Protect a Power Query in excel? The answer is yes, you can protect your query in Excel. It’s actually a no brainer. It not only protects but also prevents your users from modifying your queries.

What is the best way to run a query?

Run the query at a quiet time. Run the query on a separate copy of the database (e.g. a readable secondary). Run the query in an I/O-limited resource pool as described here *.


3 Answers

As I see it, there are three options to choose from:

Option 1

Create a tool that will create the query for the user on the background. Simply by clicking buttons and entering table names. This way you can catch all weird behavior in the background bringing you out of danger for queries you don't want executed.

Option 2

Create a MySQL user that is only allowed to do SELECT queries. I believe you can even decide what tables that user is allowed to select from. Use that user to execute the queries the user enters. Create a seperate user that has the permissions you want it to to do your UPDATE, INSERT and DELETE queries.

Option 3

Before the query is executed, make sure there is nothing harmfull in it. Scan the query for bad syntax.

Example:

// Check if SELECT is in the query
if (preg_match('/SELECT/', strtoupper($query)) != 0) {
    // Array with forbidden query parts
    $disAllow = array(
        'INSERT',
        'UPDATE',
        'DELETE',
        'RENAME',
        'DROP',
        'CREATE',
        'TRUNCATE',
        'ALTER',
        'COMMIT',
        'ROLLBACK',
        'MERGE',
        'CALL',
        'EXPLAIN',
        'LOCK',
        'GRANT',
        'REVOKE',
        'SAVEPOINT',
        'TRANSACTION',
        'SET',
    );

    // Convert array to pipe-seperated string
    // strings are appended and prepended with \b
    $disAllow = implode('|',
        array_map(function ($value) {
            return '\b' . $value . '\b';
        }
    ), $disAllow);

    // Check if no other harmfull statements exist
    if (preg_match('/('.$disAllow.')/gai', $query) == 0) {
        // Execute query
    }
}

Note: You could add some PHP code to filter out comments before doing this check

Conclusion

What you are looking to do is quite possible however you'll never have a 100 percent guarantee that it's safe. Instead of letting the users make the queries it's better to use an API to provide data to your users.

like image 126
Peter Avatar answered Sep 16 '22 22:09

Peter


Don't do this, there will always be creative ways to make a dangerous query. Create an API that will manually construct your queries.

like image 40
FrozenDroid Avatar answered Sep 19 '22 22:09

FrozenDroid


Since you said you would prefer not to use read only SQL accounts if there are alternatives. If you're running PHP 5.5.21+ or 5.6.5+: I'd suggest checking if the first statement of the query is a SELECT statement and disabling multiple queries in your PDO connection.

First disable multi statements on your PDO object...

$pdo = new PDO('mysql:host=hostname;dbname=database', 'user', 'password', [PDO::MYSQL_ATTR_MULTI_STATEMENTS => false]);

Then check that the first statement in the query uses SELECT and that there are no subqueries. The first regex ignores leading whitespace which is optional and the second detects parenthesis which would be used to create subqueries -- this does have the side effect of preventing users from using SQL functions but based on your example I don't think that's an issue.

if (preg_match('/^(\s+)?SELECT/i', $query) && preg_match('/[()]+/', $query) === 0) {
    // run query
}

If you're running an older version, you can disable emulated prepares to prevent multiple statements being executed but this relies on PDO::prepare() being used.

FWIW: It would be a lot better to use prepared statements/generate safe queries for your users or to use a read-only SQL account. If you're using MySQL and have admin rights/remote access, I'd suggest using the community edition of SQLyog (https://github.com/webyog/sqlyog-community/wiki/Downloads) to create read-only user accounts. It's extremely user friendly so you won't have to learn the GRANT syntax.

like image 34
tiernanx Avatar answered Sep 19 '22 22:09

tiernanx