What is the MySQL session variable equivalent to Sybase ASE's APPLICATIONNAME? - mysql

APPLICATIONNAME in Sybase is used in information schema queries (current connections, slow queries, etc.), so the results contian not just a thread/process id, but a more human readable description of the application responsible for the given query.
Is there an equivalent to it in mysql? My google fu failed me

On an SQL email list (sq-l at list.prog.hu) I got the answer that no, this is not supported on MySQL, but a relatively common workaround is to prefix each SQL statement with a descriptive comment identifying the issuing application.
E.g.:
/* cms_blog_module */ select * from blog_posts where id = ....
And this way, wherever a query is present, its source is present as well

Related

SQL FullText Indexes and SQL Injection

Is there any known danger with exposing the use of a FullText index to internal and possibly public users?
Assuming the queries are properly parameterized, is there any way that a user could abuse the inputs to trigger a SQL injection or denial of service attack?
// SQL Server
select * from content_table WHERE CONTAINS((Title, Subtitle, Body), #fullTextSearch);
// MySQL
select * from content_table WHERE MATCH(Title, Subtitle, Body) AGAINST (#fullTextSearch);
// Oracle
select * from content_table WHERE CONTAINS(Body, #fullTextSearch);
The trigger for this question is the large variety of inputs a user could specify and the fact that the different SQL servers have different query syntax and at least some (MySQL) will return a syntax error if an invalid query is specified.
'FORMSOF(INFLECTIONAL, model NEAR airplane)'
'NEAR((term1, term2),5) AND term3'
'NEAR((term1, term2),5) OR NEAR((term3, term4),2, TRUE)'
'+join +(>left <right)'
'electric INPATH (/purchaseOrder/items/item/comment)'
When talking about SQL injection the risk is that someone can introduce SQL keywords into the query itself by adding SQL to a data parameter.
This is why separation of data and query is absolutely critical. This normally plays out by using placeholder values, as in:
SELECT * FROM content_table WHERE MATCH(Title, Subtitle, Body) AGAINST (?);
In the case of search there's often two levels you need to be aware of:
The SQL layer where you're using raw SQL keywords to express the query conditions, such as WHERE x=? AND y=?
The search layer where you're expressing conditions within a string, like WHERE CONTAINS(?) which has a bound data parameter '"computer software" NEAR hardware)'
Note that the second form has a syntax within a string, so if you're exposing that you're not at risk of SQL injection per-se, but you may end up receiving a lot of syntax errors caused by bad user input that you need to handle.
In the first case if you need to compose the query conditions you need to follow the usual rules:
Do not permit:
Inclusion of unknown fields into the query.
Inclusion of unknown operators into the query.
Maintain as strict a separation between query and data as is practical.
You may need to parse the request's data into components that can be recomposed into a SQL query. This can get messy, especially if you're allowing a lot of latitude in how things can be searched, so try and keep it as simple and testable as possible.
If you have unit tests, include one that's deliberately hostile and tries to introduce invalid or injection-type data into the query. Ensure the data is properly contained.
Note: If you're calling a stored procedure using placeholder values, but the stored procedure composes SQL statements using concatenation you're still at risk, so you need to be absolutely certain you're keeping the data separated from the query. If you have a query with zero user data introduced in it there is no risk of SQL injection.

Query to detect MySQL

I'm fixing a bug in a proprietary piece of software, where I have some kind of JDBC Connection (pooled or not, wrapped or not,...). I need to detect if it is a MySQL connection or not. All I can use is an SQL query.
What would be an SQL query that succeeds on MySQL each and every time (MySQL 5 and higher is enough) and fails (Syntax error) on every other database?
The preferred way, using JDBC Metadata...
If you have access to a JDBC Connection, you can retrieve the vendor of database server fairly easily without going through an SQL query.
Simply check the connection metadata:
string dbType = connection.getMetaData().getDatabaseProductName();
This will should give you a string that beings with "MySQL" if the database is in fact MySQL (the string can differ between the community and enterprise edition).
If your bug is caused by the lack of support for one particular type of statement which so happens that MySQL doesn't support, you really should in fact rely on the appropriate metadata method to verify support for that particular feature instead of hard coding a workaround specifically for MySQL. There are other MySQL-like databases out there (MariaDB for example).
If you really must pass through an SQL query, you can retrieve the same string using this query:
SELECT ##version_comment as 'DatabaseProductName';
However, the preferred way is by reading the DatabaseMetaData object JDBC provides you with.
Assuming your interesting preconditions (which other answers try to work around):
Do something like this:
SELECT SQL_NO_CACHE 1;
This gives you a single value in MySQL, and fails in other platforms because SQL_NO_CACHE is a MySQL instruction, not a column.
Alternatively, if your connection has the appropriate privileges:
SELECT * FROM mysql.db;
This is an information table in a database specific to MySQL, so will fail on other platforms.
The other ways are better, but if you really are constrained as you say in your question, this is the way to do it.
MySql may be the only db engine that uses backticks. That means something like this should work.
SELECT count(*)
FROM `INFORMATION_SCHEMA.CHARACTER_SETS`
where 1=3
I might not have the backticks in the right spot. Maybe they go like this:
FROM `INFORMATION_SCHEMA`.`CHARACTER_SETS`
Someone who works with MySql would know.

What is the best way to filter a multi-tenant MySQL database?

In MySQL I have a single database with one schema. In Microsoft Sql Server it is recommended to use a "Tenant View Filter" so in Microsoft Sql Server this gives me exactly what I need.
CREATE VIEW TenantEmployees AS
SELECT * FROM Employees WHERE TenantID = SUSER_SID()
What is the best way to accomplish the same in MySQL? An equivalent to the "Tenant View Filter" will work if it is performs well.
Thanks!!
The query you suggest (that I could find in MSDN) has text afterwards that explains exactly what are its assumptions. In particular, it mentions that it assumes that the "owner" of a row in the Employees table is specified in the TenantID field that is populated according to the SID of the user(s) you are partitioning for.
What that means is that you can replicate the same idea whatever way you decide to implement your data as long as you have clearly defined partitions of the data and know exactly how to associate it with the table you are creating a view for.
In particular, if you configure your system so that each partition accesses the DB with its own credentials, you could use the CURRENT_USER or USER constructs of MySQL as the IDs defining your partitions and the query to create the view would be basically the same as the one suggested in MSDN replacing SUSER_ID with CURRENT_USER.
But if you use the same user to access from all the partitions, then the suggested method is irrelevant on either database server.
Since you need to use your tenantId value to perform filtering, a table valued user defined function would be ideal, as a view normally does not accept parameters. Unfortunately, unlike many other database products MySQL doesn't support table-valued functions. However, there are MySQL hacks that claim to emulate parametrized views. These could be useful for you.
It's a little tricky in MySQL, but it can be done:
CREATE OR REPLACE VIEW {viewName}
AS
SELECT {fieldListWithoutTenantID}
FROM {tableName}
WHERE (id_tenant = SUBSTRING_INDEX(USER( ),'#',1))
I wrote up a full blog post on how I converted a single-tenant MySQL application to multi-tenant in one weekend with minimal changes. https://opensource.io/it/mysql-multi-tenant/

SQL Statement Syntax Differences

I was wondering what types of things usually vary between SQL implementations when looking at the query statements. One thing that I thought was the use of IS NULL in the WHERE clause. See bleow for example. I'm writing a query statement parser that handles the statement and queries in a custom language and need to account for most of the general differences between the more widely used SQL products.
Oracle Syntax:
SELECT * FROM TABLE WHERE COLUMN_A IS NULL
SELECT * FROM TABLE WHERE COLUMN_A IS NOT NULL
MySQL Syntax?
SQL Server Syntax?
I'm not sure you're going to find a definitive list of all differences. A few things I can think of off the top of my head:
MySQL uses LIMIT while SQL Server uses TOP.
SQL Server is much stricter on GROUP BY operations than MySQL, requiring that all non-aggregated columns from the SELECT appear in the GROUP BY clause.
SQL Server supports a proprietary UPDATE FROM and DELETE FROM syntax that goes beyond the ANSI standard.
Functions that exist in one system but not another. MySQL has FIND_IN_SET and GROUP_CONCAT that don't exist in SQL Server. Likewise, SQL Server has ROW_NUMBER() that doesn't exist in MySQL.
The IS NULL / IS NOT NULL syntax is ANSI standard SQL, and supported in all three of those RDBMS as you have listed it for Oracle.
IS NULL and IS NOT NULL is the same pretty much everywhere. The main differences for basic queries would relate to function calls, and those are vastly different so you'll have to be more specific there.
There are plenty of things that vary between different RDMBS implementation. Here's a simple example which doesn't use any specific function:
In Oracle, you can update table A from data in table B as follows:
UPDATE A
SET (COL1,COL2) = (SELECT B.COL3, B.COL4 FROM B WHERE B.COL5 = A.COL6)
WHERE A.COL7 = 3
AND EXISTS (SELECT 1 FROM B WHERE B.COL5 = A.COL6);
But in SQL Server the same task can be done as follows:
UPDATE A
SET COL1 = B.COL3, COL2 = B.COL4
FROM B
WHERE B.COL5 = A.COL6
AND A.COL7 = 3;
Additionally, the Oracle syntax is invalid in SQL Server and vice versa, so you can't settle for a common denominator. Writing a parser for this particular syntax is a challenge, so a general parser seems to be a highly non-trivial task.
You can apply both queries to all the rdbms. This is standard ansi.
About a decade ago I bookmarked a link, long since broken, to a document entitled, "Levels of Vendor Compliance with ANSI SQL". I've kept it so I can think, "Ah, how quaint." The Standard is now ISO (I = international) and not just ANSI (A = USA). Nobody tries to document this kind of thing for more than one SQL product anymore.
All vendors pay close attention to the SQL Standard and will declare level compliance on a feature-by-feature basis. Even when no such declaration is forthcoming you know they have read the Standard spec, even if it means a concious decision to extend or to do things completely differently. If you are interested in portability then get used to writing Standard SQL that is implemented by, or similar to syntax in, the SQL products you wish to target.
Taking mySQL and SQL Server as examples. I would guess that some mySQL features (e.g. ORDER BY LIMIT) are closer to Standards than SQL Server is (TOP) because mySQL have come to the party later and actually had a Standard spec to follow and no legacy version to be compatible with. I would guess that other features in mySQL (update on duplicate key) are further from Standards (SQL Server extends MERGE from Standards) because they wanted something easier to implement and simpler users. I would a guess some mySQL features are close to those in SQL Server to be able to poach users!

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');