I have a PHP script that is generating a MySQL select statement:
select * from words where word = 'Classic'
There is exactly one word in the words table with the variable word equal to Classic.
When my PHP page executes, I get no results from the query. If I echo the string that is being used to execute the query, cut and paste that into the SQL window in PHPMyAdmin in the database, I also get no results. However, if I re-type that EXACT string into the SQL window in PHPMyAdmin (with the same quote characters), I get the proper result of one row.
The word Classic from the select statement is gotten from a PHP GET (see code below). I can echo the $word variable, and get the correct result of 'Classic'. What am I doing wrong?
Here is my code:
<?php
require ('dbconnect.php');
$word = $_GET["word"];
$selectStr = "SELECT * FROM words WHERE word = '" . $word . "'";
if ($results = MySQL($dbName, $selectStr))
{
$rowCount = MySQL_NUMROWS($results);
}
$resultRow = MYSQL_FETCH_ROW($results);
$wordID = $resultRow[0];
?>
Please, please, please sanitize that word. mysql_real_escape_string() should do the trick.
$selectStr = "SELECT * FROM words WHERE word LIKE '" . $sanitized_word_i_promise . "'"; should work :)
Just to explain: "=" should work for exact matches. This includes uppercase / lowercase, spaces etc. You should probably trim that result first too, before using it in the query.
If you have foo stored in the database (note the space at the end) - it won't match foo, without a space. You'll want to use LIKE 'foo%' - probably.
Either way, Sourabh is right, although performance wise, this isn't a big hit when trying to match exact strings, you should look for the problem in other places first (such as, is the item in the database an exact match?).
First off you should not take any user input and directly input it into a query without sanitizing it, or using a prepared statement.
Now that we've gotten that out of the way: have you tried doing a strcmp() with the variable and your string written in? Such as
echo strcmp($_GET['word'], "Classic")
If you get a result other than 0 it means they are not the same, most likely there will be a whitespace of some sort in the $_GET variable. use trim() on it to take out whitespace. Also could be a case sensitivity issue as well.
Related
I have text correctly saved into a mariahDB 10.2 database. The text, to complicate matters, is in fact a combination of Regular Expressions and a hybrid code invented by someone else.It can be used unchanged in another application as a text file - not PHP. But it just text at the end of the day. I want to grab data from this table, change it a small amount, and save it in a new table.
The problem is less so about changing the original data much, but more about SELECTING and saving data that is full of backslashes, single quotes, and double quotes to a new table without it being changed when it is saved. Is there a simple way in PHP and MySQL to take text from a table and resave it exactly as it is so the second table is not different from the the first?
For example the first table has the following in it.
add list to list(%section,$plugin function("XpathPlugin.dll", "$Generic Xpath Parser", $document text, "//p[1]/a[#class=\'result-title hdrlnk\' and 1]", "outerhtml", "False"),"Don\'t Delete","Global")
But if I put this into a variable and then INSERT or UPDATE that to another table, MySQL seems to strip out the backslashes, or add backslashes and throw errors for incorrectly formatted SQL.
For instance Don\'t Delete becomes Don't Delete and in other examples \\ become \
In another case ?=\")" loses the a backslash and becomes ?=")"
I have tried dozens of combinations of PHP function to leave the text alone, such as addslashes(), htmlentities(), preg_replace(), various string substitution and nothing get the data back into the table the same way as it came out.
Does anyone have the trick to do this? I would call the function leave_my_text_alone(); !!
EDIT
To add a few things that did not do the trick to get a variable I could update into the database I tried
$omnibotScript = addcslashes($omnibotScript,"'");
I then found I need to do this twice to consider the backslash being removed from before the apostrophe in Don't Delete....or it would throw a MySQL parsing error..Doing it again fixed that. So then I had to put two backslashes back to have one removed. I then added this to consider a double backslash being reduced to single backslash.
$pattern = '/\\\\"/';
$replacement = '\\\\\\\"';
$omnibotScript = preg_replace($pattern, $replacement, $omnibotScript);
But the list went on.
Use prepared statements.
If you use a prepared statement, MySQL will take care of all the escaping you need to get the string back into the table exactly as it came out of it. For example, using MySQLi:
$query = "SELECT s1 FROM t1";
if (!$result = $con->query($query)) {
exit($con->error);
}
$row = $result->fetch_assoc();
$value = $row['s1'];
$query = "INSERT INTO t2(s1) VALUES (?)";
$stmt = $con->prepare($query);
$stmt->bind_param('s', $value);
$stmt->execute();
The value of s1 in t2 will be exactly the same as the value in t1.
I am running a very simple SELECT query in MySQL and it's not working.
SELECT string_name FROM table_name;
This is giving me required output. Like
This is string one.
This is string two.
This is string three.
and so on...
But if I am running a query like this
SELECT * FROM table_name WHERE string_name='This is string one'
It's not giving any output. I even tried TRIM function.
SELECT * FROM table_name WHERE TRIM(string_name)=TRIM('This is string one')
But it's still not giving any output.
Please suggest what I am missing here. Is it because of some formatting or am I doing any silly mistake. By the way, Strings are saved as VARCHAR in the database.
To reiterate from comments; sometimes "non-printing" control characters (like newlines) can make their way into data they were never intended to be a part of. You can test for this by checking CHAR_LENGTH of field values versus what you actually see. Obviously, on large amounts of data this can be difficult; but if you know of one problematic value already, you can use this method to confirm this is the problem on that row before attempting to identify the offending character.
Once this problem is confirmed, you can use queries with MySql's ASC() and substring functions to identify character codes until you find the character; it can be best to start from the end of the string and work back, as often the offending characters are at the end.
The character or characters identified in known problem rows are often the cause of other problem rows as well, so identifying the issue in one known row can actually help resolve all such problems.
Once the character code(s) are identified, queries like WHERE string_name LIKE CONCAT('%', CHAR(13), CHAR(10)) should work (in this case for traditional Windows newlines) to identify other similar problem rows. Obviously, adjust character codes and wildcards according to your circumstances.
If no row should ever have those characters anywhere, you should be able to clean up the data with an update like this:
UPDATE theTable SET theString = REPLACE(REPLACE(theString, CHAR(10), ''), CHAR(13), '') to remove the offending characters. Again, use the codes you've actually observed causing the problem; and you can convert them to spaces instead if circumstances are better handled that way, such as a newline between two words.
Have you tried using LIKE for debugging purposes?
SELECT * FROM table_name WHERE string_name LIKE 'This is string one'
/!\ Don't just switch from = to LIKE, read about why here
TLDR:
= is apparently 30x faster.
Use = wherever you can and LIKE wherever you must.
First of all, I must acknowledge the points made by #Uueerdo were actually the the main cause of this issue. Even I was somewhat sure that there are some hidden characters in the string causing all the issue but I was not sure how to find and fix that offending character.
Also, the approach suggested by #Uueerdo to check and replace the offending character using the ASCII code seems quite legit but as he himself mentioned that this process will take lot's of time and one have to manually check every string for that one offending character and then replace it.
Luckily after spending couple of hours on it, I came up with a much faster approach to fix the issue. For that, first of all I would like to share my use case.
My first query was for selecting all the strings from a database and printing the result on page.
$result = mysqli_query($conn, "SELECT * from table_name");
while($row = mysqli_fetch_array($result)){
$string_var = $row["string_name"];
echo $string_var;
echo "<br>";
}
The above code was working as expected and printing all the string_name from the table. Now, if I wanted to use the variable $string_var for another SELECT query in the same table, it was giving me 0 results.
$result = mysqli_query($conn, "SELECT * FROM table_name");
while($row = mysqli_fetch_array($result)){
$string_var = $row["string_name"];
echo "String Name : ".$string_var."";
$sec_result = ($conn, "SELECT * FROM table_name WHERE string_var='$string_name'");
if(mysqli_num_rows($sec_result) > 0){
echo "Has Results";
} else {
echo "No Results";
}
}
In this snippet, my second query $sec_result was always giving me No Results as output.
What I simply did to fix this issue.
$result = mysqli_query($conn, "SELECT * FROM table_name";
while ($row = mysqli_fetch_array($result)){
$string_var = $row["string_name"];
$row_id = $row["id"];
$update_row = mysqli_query($conn, "UPDATE table_name SET string_name='$string_var' WHERE id=$row_id");
}
This step updated all the strings from the table without any hidden/problem causing character.
I am not generalising this approach and I am not sure if this will work in every use case but it helped me fix my issue in less than a minute.
I request #Uueerdo and others with better understanding on this to post a more generic approach so that it can help others because I think many people who can't find a right approach in such conditions, end up using LIKE in place of = but that completely changes the core idea of the query.
I am making a site that has an autocomplete product search bar. When a user types in a few letters, I compare them to the database with the LIKE statement and get products back that look similar. A user can see a max of 4 products that the user was thinking of in a drop down under the search bar. Now once the user clicks on a result, I pass that back to the PHP PDO statement.
If the product doesn't contain a %, it works fine. But I have products such as '100% Product A', and I can't seem to get around it. I know % is used in the LIKE statement. I used like, but the results weren't great. I was getting a different variation of the product I wanted. How do I get passed this?
$pdo = new PDO($dsn, $user, $passwd);
//Retrieving Product Name
$prodName = $_GET['name'];
$stm = $pdo->prepare("SELECT * FROM products WHERE productName = ?");
$stm->bindValue(1, $prodName);
$stm->execute();
$row = $stm->fetch(PDO::FETCH_ASSOC);
$results[0] = $row["productName"];
$results[1] = $row["price"];
$results[2] = $row["quantity"];
$result=implode("','", $results);
echo $result;
Since % has special meaning, you simply need to escape it to make it used literally, using usual \:
\% A % character; see note following the table
So you just need to str_replace() all % with \%.
See: https://dev.mysql.com/doc/refman/8.0/en/string-literals.html
There is not a single problem with retrieving a row containing % character with LIKE operator, let alone with a comparison operator used in your code.
Even with like it could only give you extra results, but never would make a problem with getting the existing result.
To claim such a problem exists, you you must prove your words by posting a Minimal Complete Verifiable example so everyone would be able to run your code and confirm the problem exists indeed.
However, in such a simple case, instead of a proof, you will just discover some simple mistake, completely irrelevant to % sign. For example, your search string gets severely encoded/escaped by some cargo cult code and naturally becomes a different string that doesn't have any matches in the database
Well, i would just remove % from the search string using str_replace
//Retrieving Product Name
$prodName = str_replace('%', '', $_GET['name']);
You could also escape the percentage by putting it in brackets or using backslash:
//Retrieving Product Name
$prodName = str_replace('%', '[%]', $_GET['name']); // \% would work too instead of [%]
Answer found (syntax): The column name of my string had to be encased in backticks " ` " as they contained spaces. Note that this means that the majority of this post has no relevance to the issue. The code has been corrected in case someone wants to do something similar.
So, I am doing a foreach loop to assign a value (1/0) to non-static columns in my database (it needs to support addition/deletion/editing of columns). I am using $connectionvar->query($queryvar); to do my queries which worked fine up until now when I'm trying to use a custom built string as $queryvar in order to change the column name to a variable within the loop. I've been outputting this string through echo and it looks exactly like my functional queries but somehow doesn't run. I've attempted to use eval() to solve this but to no avail (I feel safe using eval() as the user input is radio buttons).
Here's the loop as well as my thought processes behind the code. If something seems incoherent or just plain stupid, refer to my username.
foreach($rdb as $x) { //$rdb is a variable retrieved from $_POST earlier in the code.
$pieces = explode("qqqppp", $x); //Splits the string in two (column name and value) (this is a workaround to radio buttons only sending 1 value)
$qualname = $pieces[0]; //Column name from exploded string
$qualbool = $pieces[1]; //desired row value from exploded string
$sql = 'UPDATE users SET '; //building the query string
$sql .= '`$qualname`';
$sql .= '=\'$qualbool\' WHERE username=\'$profilename\''; //$profilename is retrieved earlier to keep track of the profile I am editing.
eval("\$sql = \"$sql\";"); //This fills out the variables in the above string.
$conn->query($sql); //Runs the query (works)
echo ' '.$sql.' <br>'; //echoes the query strings on my page, they have the exact same output format as my regular queries have.
}
}}
Here's an example of what the echo of the string looks like:
UPDATE users SET Example Qualification 3='1' WHERE username='Admin2'
For comparison, echoing a similar (working) query variable outside of this loop (for static columns) looks like this:
UPDATE users SET profiletext='qqq' WHERE username='Admin2'
As you can see the string format is definitely as planned, yet somehow doesn't execute. What am I doing wrong?
PS. Yes I did research this to death before posting it, as I have hundreds of other issues since I started web developing a month ago. Somehow this one has left me stumped though, perhaps due to it being a god awful hack that nobody would even consider in the first place.
You need to use backticks when referring to column names which have spaces in them. So your first query from the loop is outputting as this:
UPDATE users SET Example Qualification 3='1' WHERE username='Admin2'
But it should be this:
UPDATE users SET `Example Qualification 3`='1' WHERE username='Admin2'
Change your PHP code to this:
$sql = 'UPDATE users SET `'; // I added an opening backtick around the column name
$sql .= '$qualname`'; // I added a closing backtick around the column name
$sql .= '=\'$qualbool\' WHERE username=\'$profilename\'';
Example Qualification 3 : Is that the name of your Mysql Column name ?
You shouldnt use spaces nor upper / lower case in your columnname.
Prefere : example_qualification_3
EDIT :
To get column name and Comment
SHOW FULL COLUMNS FROM users
For a search feature I wrote a MySQL query to be executed by a PHP script. I'm not doing a fulltext search. Instead, I'm doing a search using the following method:
... WHERE field LIKE '%etc%' AND field REGEXP '[[:<:]]etc[[:>:]]'
Now, my idea is to prepare these dynamic values in PHP, like:
$word = '2*3%5_1^0'; // just an example
$wordLike = strtr($word,array('\\'=>'\\\\','%'=>'\\%','_'=>'\\_'));
// instead of my old solution:
// $wordLike = preg_replace('~([%_])~', '\\\\$1', $word);
$wordLike = $db_con->escape('%' . $wordLike . '%');
$spaces = '[[:blank:]]|[[:punct:]]|[[:space:]]';
// I'm not sure about the difference between blank & space, though
$wordRX = preg_quote($word);
$wordRX = $db_con->escape('(^|'.$spaces.')'.$wordRX.'($|'.$spaces.')');
// instead of my old solution:
// $wordRX = $db_con->escape('[[:<:]]' . $wordRX . '[[:>:]]');
and then use these values like in…
... WHERE field LIKE '$wordLike' AND field REGEXP '$wordRX'
which, with this example input, results in
...
WHERE field LIKE '%2*3\\%5\\_1^0%' AND
field REGEXP '[[:<:]]2\\*3%5_1\\^0[[:>:]]`
A couple of notes…
In my actual code I'm making it handle multiple words, this is just the pseudo code.
The method I'm using to search the word(s) -with LIKE & REGEXP together- was the fastest one among the approaches I tried.
I know I should use PDO instead, please don't input anything about that unless it's relevant to my issue
Q1: Is this the right way to go?
Q2: Is this secure enough against SQL injections?
Some additional info
About MySQL REGEXP…
Following characters are escaped by preg_quote()
. \ + * ? [ ^ ] $ ( ) { } = ! < > | : -
Following is the list of [occasionally] special characters in REGEXP
. \ + * ? [ ^ ] $ ( ) { } | -
There are also additional constructs in REGEXP but they're all surrounded by single/double brackets, and because I know all the brackets will be escaped by preg_quote() I feel like I shouldn't be concerned about them.
About MySQL LIKE…
The only 2 special characters in LIKE are
_ %
So escaping them seems enough a workaround.
Please correct me if I'm missing anything.
Appart from what you mention mysql_real_escape_string() function should do fine for sanitization against SQL injection.
You just have to properly escape whatever user input using the appropiate escaping function(s), if you picture it as chained processing blocks processing this user input you will know in which order (from last to first) and what to escape/unescape and when, and you should be okay as far as securing a clean input goes (validation is a different issue).
And, as you already seem to know, quote() on PDO or Mysqli prepare() are a better approach.
try this use mysql_real_escape_string()
$word = '2*3%5_1^0';
$query = 'SELECT * FROM TABLE_NAME WHERE field REGEXP "(.*)[[:<:]]'.mysql_real_escape_string($word).'[[:>:]](.*)" ';
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
then:
$word = clean($_POST['whatever post']);
then trim word and your good to go. what this php function does is take all literals and turns them into strings so no one can lets say delete your db etc