I've seen lots of good answers in general to the problem of NULL datetime fields, and the answer is don't use '', just put NULL directly.
That's all great, but all my MySQL queries are built up by doing a str_replace, like so:
$query = "SELECT * FROM ##t1 WHERE ##f1=##v1"
I then use table, field and value arrays with the actual content that will be replaced in
For every thing else it works 100% great
For this I can't find a way of getting a NULL in there without quotes around it
Anything obvious I'm missing?
You shouldn't use str_replace for constructing sql because actually it doesn't work 100% of the time.
Use prepared statements instead
http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
An example of where str_replace fails
What if your table name, field name or data contains one of those "##" tokens? Then you are in trouble because it could get replaced with parameters, depending on what order you do your replacements.
With prepared statements the database handles all of this for you. You also get automatic escaping done correctly, query precompilation for better performance and other security protection that you and I probably haven't even thought about.
Related
My question of to day is. Do i need to escape PDO in my script?
$columns = implode(", ",$column);
$query = ''.$query.' '.$columns.' FROM '.$table.'';
$dbh_query = $dbh->prepare($query);
$dbh_query->execute();
$dbh_querys = $dbh_query->fetchAll();
return $dbh_querys;
The whole script can be found at.
https://github.com/joshuahiwat/crud/blob/master/control/query_connector.class.php
Can someone explain why do i need a escape at this time or why not.
I like to hear from you, thanks a lot!
The parts of your query that are dynamic are the table name and column names. You can't use bind functions for these parts of the query. Bind functions can be used only for the parts of the query that would otherwise be a simple value in an SQL query. Like a numeric constant, or a quoted string or quoted date literal.
To avoid SQL injection from dynamic table names or column names, you have the following choices:
Use values that are predefined in your class, or otherwise certain to be safe. Don't use external content from users or any other source.
Use escaping. Note that the function PDO::quote() doesn't do the kind of escaping you need for table names or column names.
Create a "allowlist" of known table names and the column names for the respective table, and compare the dynamic input to the allowlist. If it doesn't match the allowlist, raise an error.
First of all you need to understand that the word you are using - "escape" - is meaningless.
What you probably mean is "to make your query safe from SQL injection". But, unfortunately, there is no such magic "escaping" that will make some abstract query safe.
The traditional query building assumes that all the query parts beside data values are hard-coded, while data values are bound via placeholders, like this:
$query = 'SELECT col1, col2 FROM some_table WHERE id = ?';
$stmt = $dbh->prepare($query);
$stmt->execute([$id]);
$row = $stmt->fetch();
This kind of a query considered safe.
In your case of a dynamically constructed query, every part is potentially vulnerable.
And here it is very important to understand that a burden of sanitizing all the query parts is entirely on this function. You cannot dismiss the danger simply claiming that your data is coming from the trusted source. That's a slippery ground because people often have no idea whether their source is trusted or not.
So, if take your question as "Do I have to protect this code from SQL injection", than the answer is - YES, YOU HAVE.
In the meantime you are protecting only a small part of your query - the data values. So you still have to protect (this term is much better than "escape") all other parts.
On a side note, your code is connecting to database every time it runs a query, which is highly inefficient and makes it impossible to use some database features.
I'm familiar with prepared statements and I know that they are best practice when it comes to protecting against MySQL injection. But I'm wondering how this PHP/MySQL statement could be at risk of an injection attack:
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".$_POST['name']."';");
It seems to me like the input from the user would be contained inside the single quotes. Can you execute more than one query in one mysqli_query statement?
Also, is making the above safe just as easy as this...
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".mysqli_real_escape_string($_POST['name'])."';");
It seems to me like the input from the user would be contained inside the single quotes
It would unless you include single quotes in the posted name, which would allow you to break out of the quotes. Example, post the name as:
' or 1 or '
The WHERE clause becomes:
WHERE id = '' or 1 or '';
This would match and retrieve all rows in the table because of the or 1 part. As you can see, it breaks out of the quotes to inject some SQL, then it goes back into the quotes to make the query valid.
Can you execute more than one query in one mysqli_query statement?
No, but if it was executed with mysqli_multi_query then yes you could add multiple queries on to the end.
is making the above safe just as easy as mysqli_real_escape_string?
Generally yes but a Prepared Statement would be better. Using escaping, the WHERE clause would become (using my example above):
WHERE id = '\' or 1 or \'';
This is no longer vulnerable because the quotes can't be broken out of, and would only match rows if the name literally matches ' or 1 or ' which is obviously unlikely.
It seems to me like the input from the user would be contained inside the single quotes
All the attacker has to do is put a single quote inside the name POST data, and it won't be any more.
name=' OR 1=1
Also, is making the above safe just as easy as this
That looks OK … but it hurts my eyes. Use prepared statements. They are much easier to read then SQL built by concatenating strings together.
Basic explaination:
If you simply insert $_POST['name'] into the query as per your first example, the resulting SQL string will be invalid if the name variable contains a single quote character.
This will immediately annoy anyone named O'Brien, or similar.
But this can then be exploited by a hacker, who could modify his "name" to include valid SQL code after the single quote. This could be any valid SQL, allowing the hacker to do anything to your DB or query anything from it. Exactly what he can do would depend on other factors in your code, but suffice to say that even in the best case scenario, he could do some pretty devastating things.
To answer your second question: Yes. Escaping using mysqli_real_escape_string() will mitigate this problem.
However, to take things one step further, you might also want to investigate using Prepared Queries, which is a feature of the mysqli extension. This can make your code a lot neater as it avoids having to use that nasty long mysqli_real_escape_string() function name all over the place. It also has other benefits such as improved query caching.
Hope that helps answer the question.
What if I passed the following value for $_POST['name']?
'; DELETE FROM users WHERE name <> '
I would be closing the first single quote, then introducing the damaging query which just has a single open quote at the end, which would be closed by the single quote in your original query.
You second query is fine. Though you really ought to consider use of prepared statements (which are supported by mysqli)
If you're using mysqli you should always be using the SQL placeholder method for doing this. The escaping functions are the hard way.
$stmt = $db->prepare("SELECT name FROM users WHERE id = ?");
$stmt->bind_param('i', $_POST['name']);
$stmt->execute();
If you don't understand the risk here, you really need to read up on SQL injection attacks in general, and read what automated hacking tools can do to those that aren't cautious enough.
I have a MySQL table with ID as a primary key and other for this matter non-important fields.
What I would like to do is delete multiple records by sending a list of IDs for deletion as a parameter to stored procedure.
I know how to do this manually (building a query directly in PHP) but I would like to avoid that and do all my SQL directly in the DB.
Tried searching SO but couldn't find any related questions. Sorry if this is a duplicate.
Thanks
In accordance to http://dev.mysql.com/doc/refman/5.1/en/faqs-stored-procs.html#qandaitem-B-4-1-17 you can't do it directly.
But I think you can try to the following trick:
Create string of you ids in php like 'id1,id2,id3'.
Use prepared statement for binding this sting on fly.
Maybe it helps.
You could try something like
DELETE FROM sometable WHERE FIND_IN_SET(idfield, #param)
no idea if this'd work (and don't have access to a mysql instance right now to test on). Basically the problem is that if you pass in a comma-separated value list into a paramter, it'll just be a string inside the sproc, and doing a WHERE id IN ('1,2,3') would fail, since that's just a simple string and not at all the same as WHERE id IN (1,2,3). The find_in_set() function should take care of that.
I gave +1 to #Marc B for clever use of FIND_IN_SET(). It won't be able to use an index, so the performance won't be good, but it should work.
Another solution that can work (but will be slow as well, because it can't use an index):
DELETE FROM sometable
WHERE CONCAT(',', param, ',') LIKE CONCAT('%,', idfield, ',%')
The solution that #Andrej L describes isn't really parameter binding, it's interpolation of a stored procedure argument into a dynamic SQL string prior to preparing it.
SET sql = CONCAT('DELETE FROM sometable WHERE idfield IN (', param, ')');
PREPARE stmt FROM sql;
EXECUTE stmt;
You can't parameterize a list of values with a single parameter, even if the parameter's value looks like a comma-separated list of integers.
Interpolation can work, and it will benefit from an index, but be careful to filter the string so it contains only numeric digits and commas. Otherwise you introduce a significant risk of SQL injection (debunking the claim that some people make that stored procedures are inherently more secure).
hi how can i insert the array of service id into table into a single field like 2,3,4 in database i am confuse please help followingis my code...i am using this but it is inserted only single id..
$service=implode(",",$_POST['service']);
$sqlQuery="INSERT INTO ".DBPROMOTION." SET
promotion_service_id='".$service."',
promotion_user_id='".$user."',
promotion_discount='".$_POST['discount'].$_POST['type']."',
promotion_title='".$_POST['title']."',
promotion_start_date='".$startDate."',
promotion_end_date='".$endDate."',
promotion_code='".$_POST['code']."',
promotion_description='".$_POST['desc']."'"
You should use two database tables with relation one-to-many to link more than one element with table columnt. That would be better design.
You may insert all id's into db in form of string, but I don't think, that is what you aiming for.
Also, you should sanitize your input from $_POST before entering int to db, to prevent sql injection.
you database-model seem to be not normalized - correct that first before programming an application that will cause some realy big problems in the future.
also, you should never insert posted data directly to avoid sql-injection. use mysql_real_escape_string or, better, PDO for prepared statements.
to your problem directly: i think 'promotion_service_id' is an integer-field - '2,3,4' is a string, so (fortunately) you have no chance to insert it.
Hey there, I'm doing some queries to a MySQL database that involves some user-input.. And I was wondering how to prevent injection attacks, like, for example, if someone were to type
"; a-MySQL query here;"
It would be executed, this would allow people access to compromise my system. I'm wondering how i could protect against things like this, possibly create a function that filters out the query looking for bad statements? or perhaps not allowing the ; character?
To sum it all up:
Whats the common practice now adays to prevent injection attacks?
How should I go about it?
How effective will this solution be against people who know what they are doing from changing my database.
The only way is to properly escape user-submitted data. Others have pointed out some ways of doing so.
There's another way: prepared statements and placeholders. Prepared statements are supported by every modern PHP database interface, including mysqli and PDO.
Let's use PDO as a demonstration. Let's say we wanted to update a bit of data in the table foo submitted by a user.
$sql = 'UPDATE foo SET bar = ? WHERE user_id = ?';
$sh = $db->prepare($sql);
$sh->execute(array( $_POST['bar'], $_SESSION['user_id'] ));
The variables in the array passed to execute replace the question mark placeholders in the query. When this happens, they are automatically escaped and quoted. You don't need to manually escape them to make them safe to put in the database!
On the other hand, you will still need to filter them for unexpected content, like HTML, Javascript, letters where you expect numbers, etc. Making data safe to insert into the database is only half of the battle.
An even better way than calling a mysql_escape_string variant is to use bound queries in PDO, so that it's impossible to forget or miss a case since PDO will do the escaping for you when circumstances require it.
See for more:
http://www.electrictoolbox.com/php-pdo-bound-placeholders/
They best solution is to run every piece of input from the wild (user) through this function PHP.net: MySQL Real Escape String This will clean up most - if not all - Injection issues seen today. You would simply need to do something like the following:
$var = mysql_real_escape_string($_GET['var']);
$query = "INSERT INTO foo (var) VALUES (\"$var\");
It's always good practice to force type casting on variables you know should be a type. For instance a numerical identifier:
$id = (INT)$_GET['id'];
or
$id = ( is_numeric($_GET['id']) ? (INT)$_GET['id'] : -1; // replace -1 with FALSE, or NULL, etc
Which will force that variable to be an Integer so you won't end up with $id being "foo" or some other non-numeric.