I've looked around and have only seen this being used when using SELECT, not INSERT and it has gotten me quite confused.
What I've been trying is:
$statement = $pdo->prepare("INSERT INTO shipments (crate_type_id, username, arrival, quantity, type, status) VALUES ('$crate_type_id', '$username', 'DATE_ADD(NOW() + INTERVAL 1 HOUR)', '$quantity', 'purchase', 'active')");
I don't get any errors, it only inserts "0000-00-00 00:00:00". I've tried troubleshooting in phpmyadmin etc but haven't made any progress.
You should remove the quotes arount the date_add expression and use comma not +
statement = $pdo->prepare("INSERT INTO shipments
(crate_type_id, username, arrival, quantity, type, status)
VALUES ('$crate_type_id', '$username',
DATE_ADD(NOW() , INTERVAL 1 HOUR) ,
'$quantity', 'purchase', 'active')");
First of all, you're using PDO::prepare() incorrectly. The whole purpose of the tool is to separate code and data but you're injecting the latter into the former as if prepared statements had not been invented yet. You have several examples in the manual so I won't repeat that here but in short you have to:
Insert placeholders in your SQL code (? or :foo)
Provide actual values on execution
Secondly, I suggest you configure MySQL in strict mode so you don't end up with junk like 0000-00-00 00:00:00. You can set a proper SQL mode on connection, e.g.:
SET ##SESSION.sql_mode='TRADITIONAL'
Finally, your final SQL code is possibly not even valid, but apparently you aren't being notified about it. I recommend you add something like this to your connection code:
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
... and ensure you've configured your PHP development box to display all errors.
Related
I am creating a php page with a small and simple database.
when I visit it online and try to pass the parameter "length" in the url like: index.php/?length=1 it works fine and fetches the data.
If I add the single quote like index.php/?length=1' I have no SQL error on the page...
but if I use index.php/?length=-1 I see the SQL error in my page.
Does this mean that my page is vulnerable?
How can I further test it and fix the problem?
Edit: added the code
$length = $wpdb->get_results( $wpdb->prepare("SELECT `title`, `website`, `material`, `color`, `width`, `height`, `group`, `category`, `numbers_positive`, `numbers_negative`, `custom` FROM {$wpdb->shirts} WHERE `id` = '%d' ORDER BY `rank` ASC, `id` ASC", intval($shirt_id)) );
if (!isset($shirt[0])) return false;
$shirt= $shirt[0];
$shirt->title = htmlspecialchars(stripslashes($shirt->title), ENT_QUOTES);
$shirt->custom = maybe_unserialize($shirt->custom);
$shirt->color = maybe_unserialize($shirt->color);
if ( $this->hasBridge() ) {
global $lmBridge;
$shirt->shirtColor = $lmBridge->getShirtColor($shirt->color);
}
$shirt = (object)array_merge((array)$shirt,(array)$shirt->custom);
unset($shirt->custom);
return $shirt;
Yes, from the URL examples you have given, it seems like you take user input and directly insert it into your MySQL statement. That is the absolute worst. You should always parse user input because direct input from a user can result in the string being escaped and them deleting every table in your DB. This is a great example: Bobby Tables
Also, this is been a topic of great discussion. There is a great answer here
Edit* Using the WordPress framework and looking at your code, its not as bad as it seemed.
accepting, but generating an error on -1 does not nessicarily mean you are suseptable to an injection attack. As long as you are varifying that the input is an integer and only using the integer compontent, you're fairly safe.
Prepared statements make it even more secure, by seperating the data from the query. Doing that means someone can never 'break out' of what you are supposed to be working on. It's absolutely the right way to use SQL.
We can even take it another step farther, by limiting the abilty of the account to do anything other that run stored queries, and storing your queries on the SQL server side, rather then in your PHP. At that point, even IF they broke out (which they can't), they would only be able to access those defined queries.
So i do a simple query like so:
connection.query("UPDATE workers SET timestamp='"+thedate+"' WHERE id = " + id,function(err,upres){
connection.release();
if(!err) {
console.log('updated record')
console.log(upres);
}
})
console.log reveals the data format as: 2015-04-02 19:29:14
And if i debug the SQL statement, that turns out to be:
UPDATE workers SET timestamp='2015-04-02 21:31:16' WHERE id = 3;
However, when i list the data, the output is:
[{"id":3,"worker":"John Doe","timestamp":"2015-04-01T22:00:00.000Z","duration":30}]
This is way off compared to the time that is being reported?
What is causing this?
You do not know how MySQL is turning your VARCHAR into a date. There are a lot of configuration options. It would be better to use the STR_TO_DATE function to circumvent all of the assumptions. Here is a link to the docs for STR_TO_DATE().
As a side note, I would strongly recommend using prepared statements as a way to safeguard your application against errors and sql injection.
EDITS:
In regards to your questions, the column could be DATETIME, but your value you are assigning is a VARCHAR
'UPDATE workers SET timestamp = ? WHERE id = ?', ['4/2/2015 3:00:00 PM', 3'], [callBackFunction]
Based on what you said about the conversion not working, I am suspicious about the data type for the timestamp column.
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE NAME = 'workers'
A statement like that should give you all of the information about that column. You could also find this in a GUI, if you have access. There are three different date types in MySQL date, datetime, or timestamp. This is most likely a DATE column, that will not be able to hold the time.
I really appreciate your help on my last question. This is related but I didn't want to hide this question inside the other.. I'm having a problem using "DATE_ADD(NOW(), INTERVAL $interval)" inside a placeholder. Before I was using placeholders this line worked just fine, not it comes back as empty.
$store = qq(INSERT INTO main (creator_name,relationship,time) VALUES(?,?,?) );
my $sth = $dbh->prepare($store);
$sth->execute($data{creatorname},$data{relationship}, "DATE_ADD(NOW(), INTERVAL $interval)");
Is there another way to add DATE_NOW so it's synatically proper? I tried adding it back in like
$store = qq(INSERT INTO main (creator_name,relationship,time) VALUES(?,?, DATE_ADD(NOW(), INTERVAL $interval)) );
and it errored out saying the syntax was wrong. At least with the code on the top it executes, it just leaves the value empty. With this attempt it won't even try.
Would you expect the following to print ABC?
$x = 'uc "abc"';
print $x;
No, that would make no sense. Same thing in SQL.
The string DATE_ADD(NOW(), INTERVAL ...) is surely not a valid value for the time field.
it errored out saying the syntax was wrong.
I'm pretty sure the message was more specific than that. You also didn't provide the value of $interval, without which we have no way of knowing what resulted in a syntax error.
Changing your qq() to qq{} for clarity, this should be the fix:
$store = qq{
INSERT INTO main (creator_name,relationship,time)
VALUES(?, ?, DATE_ADD(NOW(), INTERVAL ? SECOND) )
};
my $sth = $dbh->prepare($store);
$sth->execute($data{creatorname},$data{relationship},$interval);
You'll need to change the word "SECOND" to the appropriate units, and $interval needs to be an integer.
Kudos for using prepared statements, but the reason why this doesn't work the way you tried it is very important: placeholders ? are not simply places where arbitrary text will be substituted into your query. This is why we make such a big deal about the importance of using prepared statements as one way to protect against sql injection. Placeholders are a hard delineation between "what is the query" and "what is the data" and they are sent to the server separately, not munged together.
Thus...
my $sth = $dbh->prepare(qq{SELECT * FROM user WHERE username = ? AND password = ?});
$sth->execute("hacker", "' OR 1 = 1; --");
...cannot be misinterpreted to become the dreaded...
SELECT * FROM user WHERE username = 'hacker' AND password = '' OR 1 = 1; --'
...when done in a prepared statement, as it would if you constructed your query with primitive string concatenation, because the server will never be confused between the two.
A placeholder substitutes exactly one scalar literal value, which "DATE_ADD(NOW(), INTERVAL $interval)" isn't. (Yes, it's a scalar string in Perl, but it contains an expression, not a literal value).
I have to insert more than 200000 records at one go, into mysql db table, Insert query is resulting in performance issue, what could be the substitute to this.
Below is the code I am using
$xml = simplexml_load_file("247electrical.xml");
foreach($xml->merchant as $merchant){
define('API', 'PS');
require_once('constants.inc.php');
require_once('classes/class.ClientFactory.php');
$oClient = ClientFactory::getClient(API_USERNAME, API_PASSWORD, API_USER_TYPE); $merchattrs=$merchant->attributes();
$aParams100 = array('iMerchantId' => array($merchattrs->id)); $merchantinfo= $oClient->call('getMerchant', $aParams100);
//Get Products
foreach($xml->merchant->prod as $product){
$attrs=$product->attributes();
//Insert Products into DB
mysql_query('INSERT INTO productstemp (merchant_id, merchant_name, aw_product_id, merchant_product_id, product_name, description, category_id, merchant_category, aw_deep_link, aw_image_url, search_price, delivery_cost, merchant_image_url, aw_thumb_url, brand_name, delivery_time, display_price, in_stock, merchant_thumb_url, model_number, pre_order, stock_quantity, store_price, valid_from, valid_to, web_offer, merchantimage, cleancompany) VALUES("'.$merchattrs->id.'","'.$merchattrs->name.'","'.$attrs->id.'"," ","'.$product->text->name.'","'.$product->text->desc.'","'.$product->cat->awCatId.'","'.$product->cat->mCat.'","'.$product->uri->awTrack.'","'.$product->uri->awImage.'","'.$product->price->buynow.'","'.$product->price->delivery.'","'.$product->uri->mImage.'","'.$product->uri->awThumb.'","'.$product->brand->brandName.'","'.$product->delTime.'","'.$product->price->buynow.'","'.$attrs->in_stock.'","'.$product->uri->mThumb.'","'.$product->modelNumber.'","'.$attrs->pre_order.'","'.$attrs->stock_quantity.'","'.$product->price->store.'","'.$product->valFrom.'","'.$product->valTo.'","'.$attrs->web_offer.'","'.$merchantinfo->oMerchant->sLogoUrl.'","247electrical" ) ')
or die(mysql_error());
}
}
Thanks
I dont think that the INSERT queries per se are the problem. 200.000 inserts arent that much for mysql after all.
First I guess reading the file is slow. SimpleXML is convenient but for large files it results in a huge memory overhead. Think about a streaming XML reader like PHP´s XMLReader.
You are sending individual statements to the mysql server which is way slower then sending one huge statement. Also, your single insert statement should be wrapped in a transaction. What happens if you processed 10.000 records and inserted them and then your script dies/mysql server dies etc.? How do you safely start the script again without manual work (clearing table, lookup which were already processed etc.).
Apart from that, one single INSERT statement with many VALUES should be way faster. I would make your PHP script output the query so it looks in the end like this:
INSERT INTO table(field_1, field_2, field 3)
VALUES('foo 1', 'bar 1', 'baz 1'),
VALUES('foo 2', 'bar 2', 'baz 2'),
...
And then import that file via:
$ mysql ... credentials options etc ... < output.sql
If thats still too slow… buying more hardware might help, too.
I've been using the following snippet in developements for years. Now all of a sudden I get a DB Error: no such field warning
$process = "process";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
but it's fine if I use numerics
$process = "12345";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
or write the value directly into the expression
$create = $connection->query
(
"INSERT INTO summery (process) VALUES('process')"
);
if (DB::isError($create)) die($create->getMessage($create));
I'm really confused ... any suggestions?
It's always better to use prepared queries and parameter placeholders. Like this in Perl DBI:
my $process=1234;
my $ins_process = $dbh->prepare("INSERT INTO summary (process) values(?)");
$ins_process->execute($process);
For best performance, prepare all your often-used queries right after opening the database connection. Many database engines will store them on the server during the session, much like small temporary stored procedures.
Its also very good for security. Writing the value into an insert string yourself means that you must write the correct escape code at each SQL statement. Using a prepare and execute style means that only one place (execute) needs to know about escaping, if escaping is even necessary.
Ditto what Zan Lynx said about placeholders. But you may still be wondering why your code failed.
It appears that you forgot a crucial detail from the previous code that worked for you for years: quotes.
This (tested) code works fine:
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,'$thing')");
$sth->execute;
But this next code (lacking the quotation marks in the VALUES field just as your first example does) produces the error you report because VALUES (3,$thing) resolves to VALUES (3,abcde) causing your SQL server to look for a field called abcde and there is no field by that name.
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,$thing)");
$sth->execute;
All of this assumes that your first example is not a direct quote of code that failed as you describe and therefore not what you intended. It resolves to:
"INSERT INTO summery (process) VALUES(process)"
which, as mentioned above causes your SQL server to read the item in the VALUES set as another field name. As given, this actually runs on MySQL without complaint and will fill the field called 'process' with NULL because that's what the field called 'process' contained when MySQL looked there for a value as it created the new record.
I do use this style for quick throw-away hacks involving known, secure data (e.g. a value supplied within the program itself). But for anything involving data that comes from outside the program or that might possibly contain other than [0-9a-zA-Z] it will save you grief to use placeholders.