A jquery builder (from http://querybuilder.js.org/ ) is used to let the user pick a date and further select data for a DataTables (datatables.net/ ) via a PHP function.
The DataTables and especially Ajax function looks like this:
var table = $(id).DataTable({
serverSide: true,
searching: true,
processing: true,,
ajax: {
url: "controllers/myAjax.php",
type: "POST",
data: result
}
});
The object passed as data is defined by queryBuilder and appended to my query string in the PHP script. To nail things down I pass the data as plain SQL (http://querybuilder.js.org/plugins.html#import-export). In my problem test case this is:
WHERE birthdate < '1990-01-01'
This would result in the SELECT query:
SELECT * from table_1 WHERE birthdate < '1990-01-01'
This query throws a MySQL error:
"[...] check the manual that corresponds to your MySQL
server version for the right syntax to use near '\'1990-01-01\' "
Obviously the date doesn't get escaped correctly. But when I enter exactly this query to my MySQL workbench, the server executes and returns a correct set of results. Even more, the workbench doesn't care if I use single quote (') or double quote (").
Further, I tried to manually remove those escape chars using PHP str_replace. The function then returns values, but obviously interpreted as int and breaking other queries (like equal ID). Same goes for msqli.real-escape-string (http://php.net/manual/de/mysqli.real-escape-string.php).
Another approach I tried was to change the dataType of the Ajax function a little bit - but basically I am sending form-encoded data, so the default type for this should be fine?
So why does (only) the date field get escaped in a wrong manner? Is there any rather quick fix for this, before I have to write my own PHP functions for accessing the DB?
Related
Trying to make a little Search feature for a user, so he can type in a date on a webpage made with HTML/PHP, and see which people in the db have registered as member on or after (a date). My user inputs the date in format 2015-10-01. This gets sent to a PHP page with a jqxGrid on it, populated with member details of members conforming to my query on the MySQL database (using PDO).
The query uses the operator >= on a string passed as (for example) "2015-10-01" in the WHERE clause, so I am using STR_TO_DATE to make the comparison work:
WHERE `lastUpdated` >= STR_TO_DATE( ? , '%Y-%m-%d');
With PDO, the ? later gets bound to the date (which was passed in as a string).
The db column for registration date is in DATETIME format, and in the db values look like: "2015-10-12 17:12:52".
My query returns an empty array every time, - and this after many hours of trying every conceivable permutation of date format, both in the MySQL statement and on the page that prepares the data for populating the grid.
Can someone show me what's wrong here?
Thanks!!
SP
Make it
WHERE `lastUpdated` > ?
and check your data and stuff.
Basically, you should never touch PDO until you get raw SQL to work.
okay, so here is the PDO version that works - passing in ? instead of the date:
function getJSONAllMembersByDate($PDOdbObject, $regDate)
{
try
{
$membersByDateSQL = "SELECT `id`, `name_first`, `name_last`, `organization`,`email`, `phone`,`source`,`comments`,`language_id`, `lastUpdated` FROM `member` WHERE lastUpdated>=?";//'$regDate'
$get=$PDOdbObject->prepare($membersByDateSQL);
$get->execute(array($regDate));
$rows = $get->fetchAll(PDO::FETCH_ASSOC);
$json=json_encode($rows);
return $json;
}
The fact that it works proves there were other errors in the file containing the jqxwidget (the version before I posted here). I certainly tried about a million different things to get this working.
I don't know if this counts as an answer, but at least it WORKS! There are so many variables in this problem - json, jqxgrid, pdo ... not forgetting that there are several ways to use PDO. I probably had several errors in different places.
(#apokryfos, the STR_TO_DATE was indeed unnecessary.)
In the end, this is what works:
In the PHP page containing the jqxGrid, the url sent to the server is:
url: 'my-json-responses.php?fct=getJSONAllMembersByDate®Date=<?php echo $fromDate ?>'
This $fromDate comes from the $_POST when the user typed in a date (in the format 2015-10-01) on the input page. When the PHP page containing the jqxGrid loads, it does
$fromDate = $_POST['regDate'];
The url "transits" through the file my-json-reponses.php, which contains many functions. It finds the right one:
if ($_GET['fct'] == 'getJSONAllMembersByDate')
{
$result = getJSONAllMembersByDate($connectionObject, $_GET['regDate']);
echo $result;
}
The $result is called on the file that contains all my PDO database requests, including:
function getJSONAllMembersByDate($PDOdbObject, $regDate) { try
{
$membersByDateSQL = "SELECT `id`, `name_first`, `name_last`, `organization`,`email`, `phone`,`source`,`comments`,`language_id`, `lastUpdated` FROM `member` WHERE lastUpdated>='$regDate'";
$get=$PDOdbObject->query($membersByDateSQL);
$rows = $get->fetchAll(PDO::FETCH_ASSOC);
$json=json_encode($rows);
return $json;
}
catch (PDOException $e)
{
echo "There was a problem getting all members with this search query.";
echo $e->getMessage();
}}
Note that I couldn't make the version using "?" in the query work at all, hence passing in the variable $regDate directly, with single quotes around the variable just to make life interesting.
This returns a nice list of all my users as of 2015-10-01 - but is presumably still open to MySQL injection attacks ...
But after this marathon of debugging I am happy enough for now. (All improvements welcomed, naturally!)
SP
I've seen similar error messages, but most have to do with comparing int or float to uniqueidenifier, which makes sense why you'd get an error. My error is this:
SQLSTATE[22018]: Invalid character value for cast specification: 206 [Microsoft][SQL Server Native Client 11.0][SQL Server]Operand type clash: text is incompatible with uniqueidentifier (SQLExecute[206] at /usr/src/php-5.4.8/ext/pdo_odbc/odbc_stmt.c:254)
I'm building a PHP ZF2 application and attempting to call a user-defined function with parameters. The first four parameters are UNIQUEIDENTIFIER values. The last four are BIT values. Here is my code:
public function getCustomerInspectionDocuments($fkCustomer) {
/** #var \Zend\Db\ResultSet\ResultSet $result */
$result = $adapter->query("
SELECT
createUser.FullName AS CreateUser,
udf.CreateTime,
udf.CompleteTime,
modifyUser.FullName AS ModifyUser,
udf.ModifyTime,
udf.Source,
udf.id AS InstanceID
FROM
udfDocumentInstances(
:fkCustomer,
:fkDocumentQueue,
:fkDocumentType,
:fkADUser,
:Completed,
:Deleted,
:LinkByXXX,
:LinkByOwnership
) udf
LEFT JOIN ADUser createUser
ON udf.fkCreateUser = createUser.pkid
LEFT JOIN ADUser deleteUser
ON udf.fkDeleteUser = deleteUser.pkid
LEFT JOIN ADUser modifyUser
ON udf.fkModifyUser = modifyUser.pkid
ORDER BY
ModifyTime DESC
", array(
':fkCustomer' => $fkCustomer,
':fkDocumentQueue' => '57B5829B-3EAE-46FF-8130-8A432176DE2A',
':fkDocumentType' => '7E5D5187-B38A-E211-B52D-0F1256A21434',
':fkADUser' => null,
':Completed' => null,
':Deleted' => 0,
':LinkByXXX' => 0,
':LinkByOwnership' => 0,
));
// do some other stuff and return the records
}
This code works absolutely fine when executed against SQL Server 2012, but when I switch it back to 2008, it chokes with that error message. I'm using the same driver, so the only thing that changes is the server.
The problem seems to be a combination of the fact it's executed against a UDF and that I'm binding the parameters via PDO. If I change it to a simple SELECT query (from a normal table), it works. Or if I put the parameter values directly into the query, that also works. But I need to call this UDF, and I would prefer to use the parameterized query.
Is there a way to see exactly what is being sent to the SQL Server (i.e. the completely assembled query)? Or is it possible that it's passed to the server as a parameterized query and it's the SQL Server that's assembling it?
Based on what I found out from SQL Profiler (thanks #davek), it looks like the query is assembled on the SQL Server side, and PDO is passing all of the parameters as TEXT, which cannot be implicitly converted to UNIQUEIDENTIFIER or BIT. I'm not sure how to get it to pass them as CHAR by default. I'm also not sure why it doesn't cause an issue when using a parameterized query with a normal table.
Here is the temporary workaround I've come up with. It's ugly, but it works for now. Hopefully someone else can come up with a better answer.
'...
FROM
dbo.udfDocumentInstances(
CAST(:fkCustomer AS CHAR(36)),
CAST(:fkDocumentQueue AS CHAR(36)),
CAST(:fkDocumentType AS CHAR(36)),
CAST(:fkADUser AS CHAR(36)),
CAST(:Completed AS CHAR(1)),
CAST(:Deleted AS CHAR(1)),
CAST(:LinkByDEA AS CHAR(1)),
CAST(:LinkByOwnership AS CHAR(1))
) udf
...'
So I'm casting the values from TEXT to CHAR, and then the SQL Server will implicitly convert them to UNIQUEIDENTIFIER or BIT.
If anyone can come up with a way to pass them as CHAR instead of TEXT, without having to explicitly cast each one, please post your response.
I am getting stuck with an example I found on SO about this very topic.
See original article: How do I show next result in MySQL on "onclick" in JavaScript?
I followed this example to the T, with the exception of using some updated functions. Anyway, I am getting stuck on one step, was hoping someone could explain.
within the jquery below, the code is setting $number and then passing number in the POST action to the php file. My problem is is that when echo 'count', it echos "$number". I am not sure why it is not passing an actual number such as "0" rather than the string "$number". I am probably doing something seriously wrong, but not sure what is going on.
jquery
$(function(){
$('#showMore').click(function(event) {
event.preventDefault();
$number = $('.result').size();
$.ajax({
type: "POST",
url: "getNext.php",
data: "count=$number",
success: function(results){
$('#results').append(results);
}
});
});
PHP
I am passing count into a variable so that I can use it in a query, like so:
$pst = $_POST['count'];
SQL
$sql = "SELECT * FROM tablename LIMIT $pst,1";
I went ahead and captured the error I am receiving (see below) - as mentioned previously it is inserting "$number" instead of an actual number.
"Fatal error: Query Failed! SQL: SELECT * FROM tablename LIMIT $number,1
any help would be much appreciated
Try changing this line:
data: "count=$number",
To this
data: "count=" + $number,
Javascript doesn't "read" strings for variables like php does, so you need to concat the value manually.
problem is you are sending count as string which is $number in your case.
your data should be
data: {"count":$number}, //notice `"`
send it as object.
or
$data:"count=" + $number,
concate the var
i prefer data as object which is more readable.
i am using mysqlclient,
in one of my query, as shown below
sprintf (query, "select user from pcloud_session where id = '%s'", sid);
here some time this sid is with % sign in it like the example
2Cq%yo4i-ZrizGGQGQ71eJQ0
but when there is this % this query always fail, i think i have to escape this %, but how ?
i tried with \ and %% , but both of this not working, please help me here
UPDATE:
When using session.hash_bits_per_character = 6, in php session ,the default charset contains a character (comma) that will always be urlencoded(here it is %2C). This results in cookie values having this %2C in it, but session db having a comma instead of it. any idea about fixing this problem ?.. sorry for the confusion
Thanks
There's no need to escape a literal '%' in MySQL query text.
When you say the query "always fail", is it the call to the mysql_query function that is returning an error? Does it return a SQL Exception code, or is it just not returning the resultset (row) you expect?
For debugging, I suggest you echo out the contents of the query string, after the call to sprintf. We'd expect the contents of the string to be:
select user from pcloud_session where id = '2Cq%yo4i-ZrizGGQGQ71eJQ0'
And I don't see anything wrong with that SQL construct (assuming the id column exists in pcloud_session and is of character datatype. Even if id was defined as an integer type, that statement wouldn't normally throw an exception, the string literal would just be interpreted as integer value of 2.)
There should be no problem including a '%' literal into the target format of an sprintf. And there should be no problem including a '%' literal within MySQL query text.
(I'm assuming, of course, that sid is populated by a call to mysql_real_escape_string function.)
Again, I suggest you echo out the contents of query, following the call to sprintf. I also suggest you ensure that no other code is mucking with the contents of that string, and that is the actual string being passed as an argument to mysql_query function. (If you are using the mysql_real_query function, then make sure you are passing the correct length.)
UPDATE
Oxi said: "It does not return a SQL Exception code, it just does not return the result[set] I expect. I did print the query, it prints with % in it."
#Oxi
Here's a whole bunch of questions that might help you track down the problem.
Have you run a test of that query text from the mysql command line client, and does that return the row(s) you expect?
Is that id column defined as VARCHAR (or CHAR) with a length of (at least) 24 characters? Is the collation on the column set as case insensitive, or is it case sensitive?
show create table pcloud_session ;
(I don't see any characters in there that would cause a problem with characterset translation, although that could be a source of a problem, if your application is not matching the database charactarset encoding.)
Have you tested queries using a LIKE predicate against that id column?
SELECT id, user FROM pcloud_session WHERE id LIKE '2Cq\%yo4i-%' ESCAPE '\\'
ORDER BY id LIMIT 10 ;
SELECT id, user FROM pcloud_session WHERE id LIKE '2Cq%'
ORDER BY id LIMIT 10 ;
Are you getting no rows returned when you expect one row? Are you getting too many rows returned, or are you getting a different row than the one you expect?
That is an oddball value for an id column. At first, it looks almost as if the value is represented in a base-64 encoding, but it's not any standard encoding, since it includes the '%' and the '-' characters.
If you're going to do this in C without an interface library, you must use mysql_real_escape_string to do proper SQL escaping.
There shouldn't be anything intrinsically wrong with using '%inside of a string, though, as the only context in which it has meaning is either directly inprintftype functions or as an argument toLIKE` inside of MySQL.
This proves to be really annoying, but it's absolutely necessary. It's going to make your code a lot more complicated which is why using low-level MySQL in C is usually a bad idea. The C++ wrapper will give you a lot more support.
You really shouldn't escape the string yourself. The safest option is to let the MySQL API handle it for you.
For a string of maximum length n, start by allocating a string of length 2*n+1:
int sidLength = strlen(sid);
// worst-case, we need to escape every character, plus a byte for the ASCIIZ
int maxSafeSidLength = sidLength * 2 + 1;
char *safeSid = malloc(maxSafeSidLength);
// copy "sid" to "safeSid", escaping as appropriate
mysql_real_escape_string(mysql, safeSid, sid, sidLength);
// build the query
// ...
free(safeSid);
There's a longer example at the mysql_real_escape_string page on dev.mysql.com, in which they build the entire query string, but the above approach should work for supplying safeSid to sprintf.
I just discovered this, I use MySQL as my database.
When i do something like:
$query = $this->db->where('id', '6rubbish')->get('posts', 1);
it generates and executes this SQL:
SELECT *
FROM (`posts`)
WHERE `id` = '6rubbish'
LIMIT 1
The surprising thing is that it actually fetches the post with the ID 6.
I find this very vulnerable in same cases because i'm trying to exactly match the ID, not to do a LIKE query.
Any ideas?
Yes.
Read Type Conversion in Expression Evaluation
Use intval() PHP function to extract the integer part of the variable
Or use is_int to exclude any variable that is not a pure integer
But the origin of the problem is that your query generator library doesn't understand the variable types, PHP being a dynamic typed language doesn't help too.
I don't know what library you're using, maybe there is an option to tell that you're passing an int? It should protect you from SQL injection, I hope, try with:
$query = $this->db->where('id', "don't")->get('posts', 1);
and see if the generated SQL has the single quoted escaped (doubled or preceded by backslash).
That is because SELECT 6 = '6rubbish' will give you true, and your id is number type.
Your id field is, no doubt, a numeric data type.
When MySQL evaluates expressions, it converts operands to compatible types (see docs).
'6rubbish' (a string) gets converted to 6 (a number) and, hence, you get a match.