i have a from, 2 of the fields are the following
<select name="sortfield">
<option value="name" selected="selected">Name</option>
<option value="price">Price</option>
<option value="id">ID Code</option>
</select>
<select name="sortdir">
<option value="asc" selected="selected">Ascending</option>
<option value="desc">Descending</option>
</select>
this are obtained via $_REQUEST[] on the next page which is then added to the query to determin how the results of a query are sorted, the quick, dirty, un-sanitized way is this
$query .= "ORDER BY ".$_REQUEST['sortfield']." ".$_REQUEST['sortdir'];
obviously this can be a problem with sql injection, one way i can fix this is to change the values and have a switch case when making the query to swap out the values, something like this
switch($_REQUEST['sortfield'])
{
case '5524879':
$query .= "ORDER BY name";
break;
case '4587532':
$query .= "ORDER BY price";
break;
default:
$query .= "ORDER BY id";
}
while this can help prevent sql injection it's not very dynamic since page will have to be changed every time if the query/table structure changes, i am wondering if there is a more dynamic way to sanitize the strings rather than having to use a switch ... case
A more dynamic way would be to create an array of table fields with show fields, and use that as a white list when checking the value of of your sortfield
if ( !in_array( $_REQUEST['sortfield'], $table_fields ) ) {
// error
}
I also recommend not using $_REQUEST at all. You should know where your variables are coming from.
The variables in $_REQUEST are provided to the script via the GET,
POST, and COOKIE input mechanisms and therefore could be modified by
the remote user and cannot be trusted. The presence and order of
variables listed in this array is defined according to the PHP
variables_order configuration directive.
Yes, for the order by clause you can only format dynamical identifier, without checking it against a whitelist.
if (!empty($_GET['sortfield']))
{
$query .= "ORDER BY `".str_replace("`","``",$_GET['sortfield'])."`";
} else {
$query .= "ORDER BY id";
}
Note that it will protect you from injection but won't save from SQL syntax error - so, it's better to verify the field name anyway.
Also, there could be no id field in the table, and thus this code is not very dynamic too. So, it's better to stick to a whitelist anyway. After all, it is not that hard. Sometimes manual amendments are better then automated ones.
Related
Ok so what I'm trying to do is make a query evaluation tool for a project. I get the user input as a php variable and use it in my query, so it's like the user is submitting the query. I am using mysqli
I'm currently doing this with my my query
$sql = "SELECT * FROM `idk` where `alias` = '".$s."'";
I get the values from the form below
$s = $_GET['val'];
<select name="selector" id = "selector">
<option value="">Select a criteria:</option>
<option value="0">id</option>
<option value="1">name</option>
<option value="2">email</option>
<option value="3">alias</option>
<option value="4">position</option>
I'm wondering if I can add user input for the where clause condition(=,!=) and get it like i get the value for the column instead of hard coding it.
Keep in mind that this is for a uni project and no one is going to use it or delete my database, but any security suggestions would be appreciated.
The shortest possible answer: yes. You can control everything. All you need to achieve this is add another choice element into your form that will let the user control the way values are compared. I'll use a simplified example.
If you have an input for the user's value:
<input type="number" name="age" value="" />
add a choice field before it:
<select name="age_comparison">
<option value="equal">equal to</option>
<option value="not_equal">not equal to</option>
<option value="less_than">less than</option>
<option value="greater_than">greater than</option>
</select>
<input type="number" name="age" value="" />
Note: the position isn't relevant for handling - at all. I just think it's a better user experience if users read your page just like they would a piece of text in real life. Meaning that when input comes later it will read as "age greater than X", which reads better than "age X, greater than", if you put the input first.
For the same reason (better user experience), I tend to label the options with something that reads more like a human, hence "equal to" instead of just "equal". But I omitted it from the option values since it doesn't add any valuable information to the code ("equal" tells the whole story as a parameter value).
Then, after your form is submitted, you can detect your user's choice and build the query string accordingly:
if (isset($_GET['age'])) {
$age = $_GET['age'];
}
if (isset($_GET['age_comparison'])) {
switch ($_GET['age_comparison']) {
case 'equal':
$ageComparison = '=';
break;
case 'not_equal':
$ageComparison = '!=';
break;
case 'less_than':
$ageComparison = '<';
break;
case 'greater_than':
$ageComparison = '>';
break;
}
}
// if we have all the parameters, we can query the database
if (isset($age) && isset($ageComparison)) {
// the following line is very unsafe - we'll be on that in a minute
$queryString = 'SELECT * FROM students WHERE age '.$ageComparison.$age;
...
Note: I used $_GET because you used it in your question. If you're going to have multiple parameters in your form, I suggest you rather use the post method and avoid having a whole bunch of parameters added to the url of your page. Your form values will be under the same keys, only in the $_POST variable. Plus, when you use post, it's enough to detect if the name of the submit is set - if it is, the rest of the inputs from the same form is guaranteed to be present as well. With get you have to check every parameter individually.
So there you have it. A query with variable comparison operators.
BUT
We're not done yet. There are some bad practices to shed away and some good practices to pick up on the way.
First and foremost, never build queries by directly inserting parameters:
$sql = "SELECT * FROM `idk` where `alias` = '".$s."'";
That makes you wide open to SQL injection. You should use prepared statements instead. Prepared statements have built-in protection mechanisms and also take care of all your quoting needs (meaning you don't have to manually put uote marks around parameters that are strings). So you do it like this:
// the question mark means we'll be adding a parameter in that position
$queryString = 'SELECT * FROM students WHERE age '.$ageComparison.' ?';
// here I assume you have mysqli statement ready
// you can see an example about creating one in the link about prepared statements
$statement->prepare($queryString);
$statement->bindParam('i', $age);
You might say "But wait a minute! You just added the comparison operator directly! Isn't that dangerous?" - no, because it didn't come directly from the user. We decided its value in the switch statement, meaning we took user's input (which might be compromised) and turned it into a value that we control. The user can't decide what comes out of the switch statement, we can. Hence, it's safe to directly concatenate it into the query string because we know we've defined some safe values.
Speaking of the switch statement, there's an improvement to be made there, too. What if we add another option in the select, but we forget to add it in the switch statement? Or a malicious user compromises the value of the option being sent? We will end up with an error, since then no cases in the switch will be matched (a case for a value outside of the 4 we put there is not defined) and thus the variable $ageComparison will never be created and we'll never execute the query, because our if condition will fail. So how do we fix it? We add a default case (default executes when none of the cases are matched):
// remainder of statement cut for length
...
case 'greater_than':
$queryComparison = '>';
break;
default:
throw new Exception('Unsupported value for age comparison: '.$ageComparison);
}
Exceptions halt execution when not handled (the proper term is caught), but I'll leave it to you if you wish to explore that topic on your own (seems a bit much for a beginner, plus there's quite a bit of text here already).
i have this html:
<p>
<strong>category:</strong>
<input type="text" name="categorytext">
<select name="categoryselect">
<option value="">-select-</option>
<option value="Paper">Paper</option>
<option value="Envelopes">Envelopes</option>
<option value="Mail Supplies">Mail Supplies</option>
<option value="Software">Software</option>
<option value="Labels">Labels</option>
<option value="Misc">Misc</option>
</select>
i then want to grab the value of the field that has been filled in and put it in a insert or update query. so if text field is filled out, then that value will go into the 'category' field of table. if a selection was made in the dropdown box, then that value will be inserted into the 'category' field of table.
so let's say this is my insert line:
INSERT INTO inventory_vendors (category) VALUES ('" . $_POST["category"] . "')
i want to put something before that line that let's php know which value to use.
something like:
if ($_POST["categorytext"] == "") {
$category = $_POST["categoryselect"];
} else {
$category = $_POST["categorytext"];
}
will something like that work? is there a better way?
EDIT:
i guess i would need to change insert line if i wanted to use a variable. would the following work:
INSERT INTO inventory_vendors (category) VALUES ('" . $category . "')
EDIT #2:
what can i do if someone tries to type text and select an option at the same time?
EDIT #3:
how is escaping done? where in code do i update it to include escaping?
The piece of code you provided should work, but beware of security issues.
You can also use JavaScript to check for fields values and fill in an hidden "category" field, that will be POSTed with the form.
Generaly, you shouldn't directly use POSTed data in textual queries like you do. One can fill in your forms with SQL code and make what is called SQL injection.
Example: If i fill in your form with a category named "'); truncate table inventory_vendors; select ('1" => I just drop your entire table. Always escape incoming data and restrict the rights of your SQL user. The best practice is to use an ORM or a framework to handle your queries.
I would like to create some chaining filters that a user can use to narrow down a list of data from the database.
For instance, I have 5 filters types:
by date
by location
by type
by topic
by keyword
The user is free to use all, some or none of these filters at once.
My first impression is that I would have to store a variable with all the "AND's" and "OR's" of my WHERE clause stacked one after the other but it seems tedious as I may have to programmatically add/delete some filter types later.
Is there a way not to use the whole mysql data that I filter once with one huge request but rather chain the targeted requests that I can add/withdraw if necessary?
I mean, can I filter one after the other the result of each previous filtered result?
For instance, instead of doing this:
SELECT * FROM mytable WHERE (A=3 AND B=4 AND ((C>date1) OR (D<date2)) AND D LIKE '%sample%' )
Is it possible to do something that would achieve this idea:
request 1 = SELECT * FROM mytable WHERE A=3
request 2 = SELECT * FROM "result of request 1" WHERE B=4
request 3 = SELECT * FROM "result of request 2" WHERE C>date1 OR D<date2
request 4 = SELECT * FROM "result of request 3" WHERE E LIKE '%sample%'
The first approach is very hard to manage if the filters are meant to change over time while the second approach is just a matter of adding/withdrawing the desired filter to the chain.
Not to mention that the more filters you have, the more the first approach becomes unreadable and unmanageable.
This could be obtained in pure php if I decide to filter the data after retrieving the whole mysql table but is seems counter productive and more verbose while Mysql has all I need to filter the data directly and it seems useless to get the whole data from Mysql when I only need part of it at the end.
Thank you for your help.
I like the first scenario better, I assume you're submitting a form to decide what to filter. The way I would go about making it scalable and easier to read, is just name the form fields the same as your DB fields. Then you can either keep an array containing the fields you want to use, or make a db call to check if a field is one you want to use in the filter. a simple example.
$filterFields = array('startDate'=>'>',
'endDate'=>'<',
'location'=>'=',
'keyword'=>'LIKE');
function buildQuery($filterFields, $mysqli)
{
$partCount = 0;
$query = "SELECT * FROM mytable WHERE ";
foreach($_POST as $key=>$value)
{
//is the field one we want to use? Does it contain a value?
if(in_array($key, array_keys($filterFields) && !empty($value))
{
if($partCount > 1)
$query .= ' AND ';
if($filterFields[$key] == 'LIKE')
$query .= " `".$key."` LIKE '%".mysqli->real_escape_string($value)."%' ";
else
$query .= " `".$key."` ".$filterFields[$key]." '".mysqli->real_escape_string($value)."' ";
++$partCount;
}
}
return $query;
}
Hopefully that makes sense. If that's not doing what you want or you need clarification, let me know. Also this is not tested, just wrote it here.
I know how to perform mysql searches using for example the WHERE word. But my problem is that i need to search on different values, but these can vary in number. For example:
I can search for 3 variables Name, LastName, Age
BUT
I in other search, i can look for 2 variables Name, Age.
Is there a way to perform a MYSQL search with the same script, no matter the quantity of values i search.??
Ot it is a better practice to "force" the search of a fixed amount of variables.??
Thanks.!
Roberto
IMHO, it is far better to limit the search to a fixed number of variables. That way you are answering a specific question for a specific reason, not trying to fit a general answer to your specific question. Limiting the search criteria makes the statement(s) easier to debug and benchmark for performance.
Hope this helps.
Just use a variable for your search parameters and inject that into your query. Just ensure that in the function/method you put the variable into the proper format (which will depend on how you select the different values.)
SELECT *
FROM db
$variable;
There will be no WHERE clause seen, unless it is passed your values (meaning you can use this same query for a general search of the db) without fear of having an empty/required $variable.
Your $variable when constructed would need to have to have the WHERE clause in it, then each value you add, insert it (in a loop perhaps) in the proper format.
Hope this makes sense, if not let me know and I will try to clarify. This is the same method most people use when paginating (except they put the variable in the LIMIT instead of the WHERE)
EDIT:
Also make sure to properly sanitize your variable before injection.
Simple example of dynamically building a query:
$conditions = array();
if (...) {
$conditions['age'] = $age;
}
if (...) {
$conditions['name'] = $name;
}
...
if (!$conditions) {
die('No conditions supplied');
}
// if you're still using the mysql_ functions and haven't done so before:
$conditions = array_map('mysql_real_escape_string', $conditions);
foreach ($conditions as $field => &$value) {
$value = "`$field` = '$value'";
}
$query = 'SELECT ... WHERE ' . join(' AND ', $conditions);
It's really not hard to dynamically cobble together the exact query you want to create. Just be careful you don't mess up the SQL syntax or open yourself to more injection vulnerabilities. You may want to look at database abstraction layers, which pretty much allow you to pass a $conditions array into a function which will construct the actual query from it, more or less the way it's done above.
Basically, i have a working form where the user inputs details about their laptop to sell to my shop.
I give them a quote once they have submitted the Specs of the laptop.
At the moment i have got option boxes and checkboxes which each have a value-- for example these. ---
<label for="state">State</label><br>
<select name="state">
<option value="10">Excellent</option>
<option value="5">Good</option>
<option value="0">Poor</option>
</select><br>
The Values of the options they have selected get added up at the end and that gives them the quote - in the above example - "10" means £10 extra for a excellent condition laptop etc.
I use $_POST[state] to get the value of it to add onto the other options for the quote.
But my problem lies when i POST them to a database (so we can check when they come in).
When they get added to the database, obviously it just comes out as the values not the actually name of it like "excellent" or "good". just says "10" or "5".
Is there anyway to put the name of the option into the database instead of the value?
sure... just make sure that's what you want to do. It's usually not considered a good database practice to create denormalized tables like that, but you could do it. When you collect your post data, simply create another variable and assign a value to it based off the state value like so:
$stateText = '';
switch ($state){
case 10:
$stateText = 'Excellent';
break;
case 5:
$stateText = 'Good';
break;
case 0:
$stateText = 'Poor';
break;
default:
// bad value
$stateText = '';
}
...then store this to the database in a new column.
This is just one of many ways to do this.
You can only do it if you have a lookup, be it an array or in another table that stores the keys and values.
You should be carefuly not to store the post data directly into your database without sanitizing it, otherwise you might become subject to sql injection.
Is there anyway to put the name of the option into the database instead of the value?
There is, but it involves doing it explicitly (converting "10" into "Excellent" before inserting the value) rather than just basically tossing $_POST into the database as-is. You can make this very simple if you are building the <option>s with an array in the first place by reading the the array again and swapping the values with the keys.
$values = array(
10 => 'Excellent',
5 => 'Good',
0 => 'Poor',
);
$post_value = $_POST['state'];
$db_value = $values[$post_value];
// further validation: make sure the array key exists or use a default value
// further usage: build your HTML <options> with this array
However:
If you're going to do that, you're much better off storing the values as numbers and converting them to words when you display them (assuming the numbers do have some meaning). This also allows you to localize by providing translations.
Response to comments:
I would recommend a rating system, like 1 through 5, and calculate your price modifications internally - not directly from the user input or from a hardcoded value (in the database). This allows you to tweak the price changes from within your app, rather than from database values that were created at an earlier time, like if you decide an "Excellent" condition warrants an increase of 11 rather than 10 - unless you specifically want the prices "locked in" permanently at the time the product was posted.
Whatever you do, make sure to validate the input - I can't think of any good reason to use direct user input to calculate prices - it should be done internally based on product ids, and any other conditions. HTML source can be modified on-the-fly to post values you didn't expect from the dropdown.
You can't get it via the HTML form. But you can still do a server side that would map the values to the appropriate condition.
You can use a switch statement or an if statement to map them.
if(value == 10){
$condition = 'Excellent';
} else {//....}