MySQL - Aliasing the database name? - mysql

I have a systematic problem that I don't have the time or budget to fix and I was hoping that there was a mysql hack I could deploy which would save my little world from impending doom.
The creators of the system I now administrate didn't realise that a database name could contain characters other than letters, numbers and underscores. As a result queries look like this.
SELECT * FROM {$db}.settings
If the database name contains something non-standard... like a dash. The world ends and all queries break.
The db prefix is there because this system used-to-but-doesn't-now run a multi-site feature.
You would think that I could fix this with..
$db = "`{$db}`";
If only.. you see it turns out a load of javascript functionality sends around this database name to keep track of which site to update. By adding these quotes, you break all of that as instead of getting databasename, it is getting
`databasename`
So... why don't I just set the database name to something that is compatible.. this system ships to external servers where I have no control over the database name...
The pain...
Is there a way to alias the database name in a preliminary query which will then affect all subsequent queries. Basically set a constant?
Something like that would then work on all subsequent queries. Something like:
set `$db` AS database
I can just set the $db variable to database and it should run.. if this is possible?

If you operate always on one database ($db doesn't change after you connect to the database), then just after connecting issue
USE `$db`;
and remove {$db}. prefix from all queries. USE query sets the active (default) database for future queries in current connection.
You didn't tell what programming language you are using, so I will assume PHP. Usually you can set default database while establishing connection (mysqli_connect(), $dbname parameter). Also, there may be a function like mysqli_select_db() that you can call after you connect.

I would fix the queries using something like:
sed -e "s/{\$db}/\`{\$db}\`/g" -i * */* */*/* */*/*/*
with a big warning to test this first, because this may mess up a lot. Especially check if your sed's -i option needs a suffix or not.

Related

How can I edit a view in MySQL Workbench without it auto prefixing a database name to the tables/views used

When I create a view, I create it in the context of the default database. So none of my references to table have a prefix which explicitly specify a database. However, when I edit a view in Workbench it automatically adds the database prefix!
I don't want the database prefix because when I restore a database under a different name it causes the restore to fail.
Is this possible to stop the prefixing in a view edit or there another way to get round the restore issue?
see https://bugs.mysql.com/bug.php?id=85176
The mysql 8.0.3 or above has been fixed
That's not possible. Views are stored in specific databases, not in some space "above" all databases. Consider following...
use playground_a; /*or whatever database*/
create view view_whatever as
select * from table_whatever;
use playground_b;
select * from view_whatever; /*here you will get an error that view_whatever does not exist*/
select * from playground_a.view_whatever; /*this works*/
That's why there will always be database prefixes in the view definition.
The only possibility I see, would be to use a stored procedure with a database name as parameter. In the procedure you'd use a prepared statement to execute a concated string of your query and the database name parameter. Of course this comes with downsides, like i.e. you can't add a where clause easily.
Creating the view without explicitely specifying a schema is a convenience feature. Behind the scenes the view is still saved in a specific schema (the default one in this case). When editing the source code is retrieved from the server which returns the real code (including the schema qualification). Hence already when you send the view code the association happens and cannot be removed again later.
Here is the command I use to create the backup:
mysqldump -u xxxxxx -pxxxxxx --routines database_a | gzip -9 > $FULLGZIPPATH
If you aren't easily able to update to MySQL 8.X then a workaround I've implemented was a post-processing step performed on the dump file itself prior to importing. I just remove the explicit prefixed db name, since the import process / view creation doesn't need it.
PowerShell -Command ^
"filter replace-dbname { $_ -replace '`<DB_NAME>`.`', '`' }"^
"Get-Content dump.sql -ReadCount 10 | replace-dbname | Add-Content replaced_dump.sql"
I've used PowerShell since I'm on Windows, but any scripting language will do. The only notes are that:
You'll need to do the replacement a-few-lines-at-a-time if you can't afford to read the entire dump into memory. Our dumps are about 11GB, which'd be a strain on our testing server's resources.
In my script I'm not doing an in-place string replacement, so it'll create a new dump file replaced_dump.sql alongside the original dump.sql. For me this was useful for diagnostics, because it meant if there was an issue, I didn't have to dump it again. Again, depending on your dump/disk size this might be an issue.
If your database happens to have `<DB_NAME>`.` as content in something like a text-field, this basic approach will also remove the string there as well.

MySQL: ceasing to use a Scheme (Database)?

How do I stop using a database?
To start mysql, you can use:
mysql -u root -pXXXX<ENTER>
At this time, no database is selected. We'll call this
state 1
To select (or use) a database:
use "MyDB";
.....My operations or queries
Now, I want to return to state 1 (without any database selected). How I can do that? I can select another database, but I don't want to do that.
What you are asking for is not possible. The only way to return to that state is to disconnect and then reconnect.
If you are just looking to switch away from your current db, you can switch to a system database, such as the internal "mysql" database:
use mysql
Or you could create an empty database and use that:
create database empty;
use empty
Try prompt.
From the MySQL manual:
Reconfigure the mysql prompt to the given string. The special
character sequences that can be used in the prompt are described later
in this section.
If you specify the prompt command with no argument, mysql resets the prompt to the default of mysql>.
Ike Walker's answer is on the right track me thinks.
Create a swap space, or you could just halt the server (stop the process) and restart it I suppose. That defeats the purpose of the server—but it would for sure wind up where you want it with no database in 'use'.
I'm certain you know this, but I mention here just in case. You never know. Someone might not know it is possible to do this.

sending terminal/shell command from mysql to terminal and retrieve answer while looping cursor

I'm using php with MySQL on macOS.
I would like to select a large amount of emails from a database and perform a dns lookup for each email in my selection using a dig command from the terminal/shell, something like: "dig gmail.com" .
Of course, I can loop this select through php but it will be very slow compared to looping cursor on MySQL.
How to send terminal commands from mysql to the terminal and retrieve answer on macOS?
You can't execute shell commands from within an SQL query (thank god), or else it would be a horrible security vulnerability... You would have to do it from php.
P.S. It is however possible to execute shell commands from the MySQL command line utility
\! ls
...but if I understand your question, it won't help solve your current problem.
(I'm assuming that you really mean ADDR_SPEC when you're talking about email addresses)
but it will be very slow in compare with looping cursor on mysql
No not really. The only difference is that depending on how you implement this the PHP approach requires that you retrieve the entire result set before you start iterating through it. However breaking this up into smaller result sets is trivial.
Also, the limitation on the performance of your algorithm is the speed of DNS lookups - and that's all about latency - if your objective is to make this go faster then you should be running multple requests in parallel.
The next thing you should consider is that you've probably got multiple mailboxes for each MX, e.g. user1#gmail.com, user2#gmail.com.... While if you've got DNS caching setup properly there will be less overhead than going to the source each time, if you're working with a very large data set or will be doing this more than once, it makes a lot more sense to just work with unique MX host values, e.g.
SELECT DISTINCT SUBSTR(addr_spec FROM LOCATE('#', addr_spec)) AS mx2chk
FROM yourtable
WHERE addr_spec LIKE '%#%'
AND (email_checked IS NULL
OR email_checked<NOW() - INTERVAL 300 DAY )
;
Indeed, if your flagging the data then you can use your own database to verify the MX.
using a dig command from terminal/shell
Please don't tell me that you're running a shell from a PHP controlling process to do a DNS lookup?

MySQL Injection - Use SELECT query to UPDATE/DELETE

I've got one easy question: say there is a site with a query like:
SELECT id, name, message FROM messages WHERE id = $_GET['q'].
Is there any way to get something updated/deleted in the database (MySQL)? Until now I've never seen an injection that was able to delete/update using a SELECT query, so, is it even possible?
Before directly answering the question, it's worth noting that even if all an attacker can do is read data that he shouldn't be able to, that's usually still really bad. Consider that by using JOINs and SELECTing from system tables (like mysql.innodb_table_stats), an attacker who starts with a SELECT injection and no other knowledge of your database can map your schema and then exfiltrate the entirety of the data that you have in MySQL. For the vast majority of databases and applications, that already represents a catastrophic security hole.
But to answer the question directly: there are a few ways that I know of by which injection into a MySQL SELECT can be used to modify data. Fortunately, they all require reasonably unusual circumstances to be possible. All example injections below are given relative to the example injectable query from the question:
SELECT id, name, message FROM messages WHERE id = $_GET['q']
1. "Stacked" or "batched" queries.
The classic injection technique of just putting an entire other statement after the one being injected into. As suggested in another answer here, you could set $_GET['q'] to 1; DELETE FROM users; -- so that the query forms two statements which get executed consecutively, the second of which deletes everything in the users table.
In mitigation
Most MySQL connectors - notably including PHP's (deprecated) mysql_* and (non-deprecated) mysqli_* functions - don't support stacked or batched queries at all, so this kind of attack just plain doesn't work. However, some do - notably including PHP's PDO connector (although the support can be disabled to increase security).
2. Exploiting user-defined functions
Functions can be called from a SELECT, and can alter data. If a data-altering function has been created in the database, you could make the SELECT call it, for instance by passing 0 OR SOME_FUNCTION_NAME() as the value of $_GET['q'].
In mitigation
Most databases don't contain any user-defined functions - let alone data-altering ones - and so offer no opportunity at all to perform this sort of exploit.
3. Writing to files
As described in Muhaimin Dzulfakar's (somewhat presumptuously named) paper Advanced MySQL Exploitation, you can use INTO OUTFILE or INTO DUMPFILE clauses on a MySQL select to dump the result into a file. Since, by using a UNION, any arbitrary result can be SELECTed, this allows writing new files with arbitrary content at any location that the user running mysqld can access. Conceivably this can be exploited not merely to modify data in the MySQL database, but to get shell access to the server on which it is running - for instance, by writing a PHP script to the webroot and then making a request to it, if the MySQL server is co-hosted with a PHP server.
In mitigation
Lots of factors reduce the practical exploitability of this otherwise impressive-sounding attack:
MySQL will never let you use INTO OUTFILE or INTO DUMPFILE to overwrite an existing file, nor write to a folder that doesn't exist. This prevents attacks like creating a .ssh folder with a private key in the mysql user's home directory and then SSHing in, or overwriting the mysqld binary itself with a malicious version and waiting for a server restart.
Any halfway decent installation package will set up a special user (typically named mysql) to run mysqld, and give that user only very limited permissions. As such, it shouldn't be able to write to most locations on the file system - and certainly shouldn't ordinarily be able to do things like write to a web application's webroot.
Modern installations of MySQL come with --secure-file-priv set by default, preventing MySQL from writing to anywhere other than a designated data import/export directory and thereby rendering this attack almost completely impotent... unless the owner of the server has deliberately disabled it. Fortunately, nobody would ever just completely disable a security feature like that since that would obviously be - oh wait never mind.
4. Calling the sys_exec() function from lib_mysqludf_sys to run arbitrary shell commands
There's a MySQL extension called lib_mysqludf_sys that - judging from its stars on GitHub and a quick Stack Overflow search - has at least a few hundred users. It adds a function called sys_exec that runs shell commands. As noted in #2, functions can be called from within a SELECT; the implications are hopefully obvious. To quote from the source, this function "can be a security hazard".
In mitigation
Most systems don't have this extension installed.
If you say you use mysql_query that doesn't support multiple queries, you cannot directly add DELETE/UPDATE/INSERT, but it's possible to modify data under some circumstances. For example, let's say you have the following function
DELIMITER //
CREATE DEFINER=`root`#`localhost` FUNCTION `testP`()
RETURNS int(11)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT ''
BEGIN
DELETE FROM test2;
return 1;
END //
Now you can call this function in SELECT :
SELECT id, name, message FROM messages WHERE id = NULL OR testP()
(id = NULL - always NULL(FALSE), so testP() always gets executed.
It depends on the DBMS connector you are using. Most of the time your scenario should not be possible, but under certain circumstances it could work. For further details you should take a look at chapter 4 and 5 from the Blackhat-Paper Advanced MySQL Exploitation.
Yes it's possible.
$_GET['q'] would hold 1; DELETE FROM users; --
SELECT id, name, message FROM messages WHERE id = 1; DELETE FROM users; -- whatever here');

Default database for MySQL

Is there a way to allocate a default database to a specific user in MySQL so they don't need to specify the database name while making a query?
I think you need to revisit some concepts - as Lmwangi points out if you are connecting with mysql client then my.cnf can set it.
However, your use of the word query suggests that you are talking about connecting from some programming environment - in this case you will always need a connection object. To create connection object and in this case having default database to connect to will lead to no improvement (in terms of speed or simplicity). Efficiently managing your connection(s) might be interesting for you - but for this you should let us know exactly what is your environment.
If you use a database schema you don't need to specify the database name every time, but you need to select the database name.
The best thing to do would be to use a MySQL trigger on the connection. However, MySQL only accepts triggers for updates, deletes and inserts. A quick Google search yielded an interesting stored procedure alternative. Please
see MySQL Logon trigger.
When you assign the permissions to every user group, you can also specify, at the same file, several things for that group, for example the database that users group need to use.
You can do this with a specification file, depending on the language you are working with, as a simple variable. Later, you only have to look for that variable to know which database you need to work with. But, I repeat, it depends on the language. The specification file can be an XML, phpspecs file, or anything like this.