I'm trying to update the database library that we use at work to use parameterized queries so that coworkers who are not very knowledgeable about SQL injection won't have to remember to escape input and just pass in an array of parameters instead (I'm using pg_query_params).
However, I am running into a problem. One of the requirements of the database library is that it logs each query that is executed and I can't figure out a way to get the text of a parameterized query once the parameters have been filled in. Is there any way to do this (aside from rolling my own function for parameterized queries, I guess)?
In other words, when executing a parameterized query like
pg_query_params('SELECT id FROM table WHERE foo = $1', array('bar'));
I want to get something like
SELECT id FROM table WHERE foo = 'bar'
PostgreSQL extended protocol (which is used by pg_query_params) separates parameters from queries.
The queries are ultimately constructed only on the server side, there is no way to construct them on the client side using only PostgreSQL's capabilities.
You can substitute the values of the parameters provided and log the query before sending the params using preg_replace.
Update:
You can enable PostgreSQL logging which will log the queries into a file or syslog (including bound parameters) on the server side, then periodically load this file into a PostgreSQL table.
Unless you can replace every occurrence of pg_query_params() in your source code with your own wrapper function (if you can, then you might want to use the auto prepend functionality in PHP to include the declaration of the wrapper) then you can only really do this at the database level.
C.
Related
This is odd. I'm running a query with just a single INSERT, preceded by a SET statement. The query looks something like this:
SET #discount:=(SELECT discount * :applyDiscount FROM fra_cus WHERE customerID=:customerID AND franchiseID=:franchiseID);
INSERT INTO discounts_applied (unitID, franchiseID, customerID, amount)
VALUES(:unitID, :franchiseID, :customerID, #discount * :price);
It appears that if I prepare these as two separate PDO queries, lastInsertID() works fine... but if I prepare them and execute them in the same statement, lastInsertID() returns nothing.
It's not the end of the world, but it's annoying. Anyone know why this would be the case? For the record, there's a reason I need to define #discount as a variable (pertains to triggers on one of the tables). Also this is all taking place within a larger transaction.
First of all, I would strongly recommend to run every query in a distinct API call. This is how an Application Programming Interface is intended to work.
It won't only prevent situations like this but also will make your code a multitude times more readable and maintainable.
And it will make your code much safer too. You can run multiple statements in a single call only at the expense of the native prepared statements. However virtual this vulnerability is, why taking chances at all?
Why not to make a regular SELECT query instead of SET, get the resulting value into a PHP variable and then use it among other variables, just through a placeholder? I don't see any reason why there should be such a complex way to deal with simple data.
In case I failed to convince you, the reason is simple. You are running two queries, and the first one doesn't trigger any insert ids. And obviously, you need this query's metadata (errors, affected rows, whatever), not the other one's first. So you get it. And to get the second'query's metadata you have to ask a database for it. The process is explained in my article: Treating PHP delusions - The only proper PDO tutorial: Running multiple queries with PDO. Basically PDOStatement::nextRowset() is what you need.
I have a Symfony 4 project and I want to store mysql queries in as a string in a mysql database. However, before storing the strings I want to make sure they are valid mysql syntax. Is there a way of doing this?
Thanks!
I didn't test it but it should work.
Use the database API you already use in your project to prepare the SQL statements you want to validate then discard them; do not execute the prepared statements.
For example, using PDO, use PDO::prepare() to ask the server to prepare the statement. It returns a PDOStatement object on success (i.e. when the query is correct). Do not call execute() on the returned statement, just discard it (using unset()).
PDO::prepare() returns FALSE or throws an exception on error, depending on how the PDO's error handling is configured.
The easiest way would be to run a query in a new transaction and then roll it back. SQL can get complex to validate especially if you plan to allow MySQL-specific functions. What if a new function gets introduced in next MySQL release? Writing and maintaining a separate SQL validation library seems counterproductive.
Why not to try following:
Create a new user for running these queries in your database. This will allow you to manage security e.g. allowing only to use SELECT statement so no one will run DROP DATABASE.
Run the user provided statement using the new user created in point 1. Start a new transaction using START TRANSACTION, execute the user provided statement, and rollback with ROLLBACK. Ensure SET autocommit=0 is set as per 13.3.1 START TRANSACTION, COMMIT, and ROLLBACK Syntax.
If the user provided statement executes without errors it's valid. You don't have to read all the returned rows in your PHP code.
Make sure to check on performance because some statements will be expensive to execute. This functionality can DOS your application.
I'd probably create procedure or function in the database. That's what they are for. Storing SQL in a table just to query it and then execute only results in a redundant round trip between the database and the application.
I'm using PHP, and MySQL with PDO. Sometimes I need to prepare a statement with one variable (placeholder) used more than once in this query.
Example:
SELECT * FROM messages WHERE from_id = :user OR to_id = :user
However if I will try to prepare this statement I will have an error so I need to do this in a way like this:
SELECT * FROM messages WHERE from_id = :user1 OR to_id = :user2
To call this statement I will need to have an array like this:
array('user1'=>$user_id, 'user2'=>$user_id);
It looks so stupid for me! Why MySQL (PDO?) don't allowing me to use one place holder more than once and forcing me to use extra variables which requires more control?!
This can be handled easy if the query is relatively simple (like I posted above), but now I built a query with 5 (!!!) uses of single variable. Each time I add the placeholder I need to check the code in many places to make it OK.
Is there any setting or a tweak to bypass this?
Is there any setting or a tweak to bypass this?
Yes, there is. You can turn emulation mode ON and be able to use the same placeholder multiple times.
So the described behavior is observed only when the emulation is turned OFF. I don't really understand why it is so but here is an explanation from Wez Furlong (the PDO author):
The change was made for two reasons; first and foremost, if you re-use the same variable in a bind, it is possible to induce a crash when using some drivers. It’s not possible to guarantee to do the right thing, and having a way to trigger a crash can sometimes be used as an attack vector for a security exploit.
The second reason is that of portability. Some drivers would internally perform this check and error out. If you code against the drivers that don’t enforce this, then your code won’t work on those that don’t.
http://paul-m-jones.com/archives/243#comment-740
I am creating a magento module and and in the controller I am trying to generate a query.
ex: "INSERT INTO ". $resource->getTableName('mymod/mymodtable')." SETpid='".mysql_real_escape_string($pp['id'])."'";
On my local setup this works ok, and I get the expected id in pid. But as soon as I upload it to my server, that portion becomes blank. I know that the database connection exists, because it inserts a new row with blank pid on server. I have tried var_dump and it does prove that $pp['id'] has the value, yet mysql_real_escape_string returns blank. I also tried mysqli_real_escape_string, but nothing. Any ideas?
Which database interface are you using? mysql_real_escape_string should be used only with mysql_query, and you shouldn't be using that interface if you can avoid it. Without a valid connection it may not function correctly.
When using mysqli you should be using parameterized queries and bind_param to add user data to your query. Calling the escaping function manually is usually a mistake.
If you're using Magento, you might want to look at how to escape values using the Magento database layer.
Per this previous question, you probably don't have a "database connection".
What's often confusing is, that when the mysql_real_escape_string documentation talks about a database connection, they specifically mean a database connection opened with the mysql_connect function.
If you're using Magento's standard objects to talk to the database, you don't have a mysql_connect connection, you have a PDO connection (via a Zend_Db_Adapter class). PDO is designed to encourage parameterized queries.
So, your options here
Go with the flow and build your queries using parameterized query strings. (if you can't figure out how a new questions with some code samples should set you right).
The adapter object has a quote method which you can use to quote your strings
I am developing a Codeigniter (2.0.2) Application, which will utilise a Master database for all write operations (INSERT/UPDATE/DELETE) and a read replica for all read operations (SELECT).
Now I know I can access two different database objects within the code to route the individual requests to the specific database server, but i'm thinking there has a better way, automated way. I'll be using MySQL and Active Record, and also want to build in Memcache checking - although it won't be used immediately, I'd like the option there for the future, built in at this stage.
I'm thinking if its possible to add a hook/library of some kind to intercept the $this->db->query so that the following happens:
1) SQL Query received
2) Check if SELECT query
2a) If SELECT, see if Memcache is active, if so encode SQL and check Memcache for response.
2b) If no memcache response, or Memcache is not active, execute query as normal through READ MySQL server.
3) Query was NOT select, so execute query as normal through the WRITE MySQL server.
4) Return response.
I'm sure that looking at this, it should be quite simple to do, but no matter how I look at it i'm just not seeing a potential answer - but there's got to be one! Can anyone help/assist?
In addition, I also want the ability to be able to log all write SQL commands for troubleshooting, presumably the best way is to introduce 3a) Write SQL command to plain text file ... into the above scheme. I don't believe MySQL actually logs the non-SELECT queries in anyway ... does it?
That type of behavior is a little bit beyond the normal scope of CI. Unfortunately, your best bet is to manually extend the database drivers, specifically override the function simple_query or _execute (simple_query is a wrapper around _execute which simply ensures initialization). That is really the only place where you can guarantee that you can catch all of the queries and branch the logic accordingly. (You may also want to override close as that is the cleanup script)
(Personally, I would have a the SELECT DB load a secondary DB into itself and just call $write_db->simple_query conditionally, that seems like it would be the least trouble).