Can you mix user input data with fixed data in a prepared statement security wise or does each query condition have to have a placeholder?
For example:
$code = htmlspecialchars($_GET['code']); // USER INPUT DATA
$status = 'A'; // FIXED
$stmt = $connect->prepare("SELECT s_id FROM events WHERE s_code = ? AND s_status = ?") or die(mysqli_error());
$stmt->bind_param('ss', $code, $status);
$stmt->execute();
$stmt->bind_result($reference);
Or is this also acceptable?
$code = htmlspecialchars($_GET['code']); // USER INPUT DATA
$stmt = $connect->prepare("SELECT s_id FROM events WHERE s_code = ? AND s_status = 'A'") or die(mysqli_error());
$stmt->bind_param('s', $code);
$stmt->execute();
$stmt->bind_result($reference);
Both approaches are acceptable. Obviously there's no security impact in binding a fixed value from your code, but it may have some performance benefits if various parts of your application (or even different applications) use different hard-coded values for that query.
To complement the answer provided by Mureinik, you should also focus on the user input a bit. Prepared statements are effective at preventing SQL injection attacks, but they are not a universal antidote against all types of attacks.
Judging by your example I would say you expect the $_GET['code'] to be an integer. As an extra layer of security you can (and should) sanitize and also validate the user input. Something along these lines:
// avoid accessing directly super-globals like $_GET, $_POST
// #see Sanitize filters: http://nl1.php.net/manual/en/filter.filters.sanitize.php
$code = filter_input(INPUT_GET, 'code', FILTER_SANITIZE_NUMBER_INT);
// #see Validate filters: http://nl1.php.net/manual/en/filter.filters.validate.php
$options = array(
'options' => array(
'min_range' => 1,
'max_range' => 1000000,
));
if (!filter_var($code, FILTER_VALIDATE_INT, $options)) {
echo 'Invalid code!';
}
Related
If i have a php file which is receiving a $_GET['value'] is it safe from sql injection or code-injection for me to start my php file with
if (in_array($_GET['value'], $allowed_values);)
{ /* normal page code handling the $_GET['value'] */
} else { unset($_GET['name'])
}
$allowed values is obviously an array of all values which i am expecting as safe for $_Get['value']. Is this still unsafe? Thank you.
Yes, that's a common and safe technique that can be used in situations where query parameters can't be used. For instance, if the value will be used as a table or column name, you can't provide it as a query parameter, you have to substitute it directly into the SQL string. Whitelisting like this is the recommended way to ensure that this is safe.
It depends on the values in the $allowed_values array, and how you are interpolating the value into your SQL query.
For example:
$allowed_values = [ 'a word' ];
if (in_array($_GET['value'], $allowed_values)) {
$sql = "SELECT * FROM mytable WHERE id = {$_GET['value']};";
}
Definitely not safe. It results in the SQL:
SELECT * FROM mytable WHERE id = a word;
This is a syntax error.
Why would you not just use SQL query parameters? Then you don't need to worry if it's safe or not. Query parameters separate the values from the SQL parsing, so there's no way any kind of value can cause SQL injection.
You won't have to have an $allowed_values array. You won't have to remember to check if the GET input is in the array. You won't have to worry about quoting or escaping.
It's true that query parameters only work for values, that is in place of a quoted string literal or quoted datetime literal or numeric literal. If you need other parts of your query to be dynamic, like the table name or column name or SQL keywords, etc. then use an allow-list solution like you are showing.
But the more common case of interpolating dynamic values is better handled by query parameters:
$sql = "SELECT * FROM mytable WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt-execute( [ $_GET['value'] ] );
let's discuss this thing in little details:
Your code is like this :
if (in_array($_GET['value'], $allowed_values);) {
...........
$sql = "SELECT * FROM mytable WHERE id = $_GET['value']";
...........
}
else {
unset($_GET['name'])
}
now let's assume, you have some values :
the in_array() function will allow only some pre-defined values, you couldn't have the option to take custom user input by $_GET, but as only pre-defined values are allowed,any SQL command will be safe inside if statement.
now take this example of $allowed_values array :
$allowed_values = ['some details' , 'another details' ,3, ' 105; DROP TABLE mytable;', 22 , 'ok'];
If any of these array values have a string that can have potential SQL injection capability, then there will be an issue. but I think you will not put any such string in the array $allowed_values. ( in this above-mentioned example, index 3, ' 105; DROP TABLE mytable;' can delete the table mytable ). else the SQL command will be safe.
now you can add an extra layer of safety in the code, by using PDO for any SQL query. (in this example you do not need that, as in_array() function is 100% safe unless you yourself put any malicious code in the array, as per my above-mentioned example). but for other types of user input where you have to do some SQL query depend on the user input, you can use PDO -prepared statement.
a PDO example is this :
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("INSERT INTO photos (username, kname) VALUES (?, ?)");
$stmt->execute([ $username , $kname ]);
For more info, try w3school link: https://www.w3schools.com/php/php_mysql_prepared_statements.asp
I'm working on a dynamic query that uses variables to specify a table, a field/column, and a value to search for. I've gotten the query to work as expected without the variables, both in phpMyAdmin (manually typing the query) and from within the code by concatenating the variables into a complete query.
However, when I use bindParam() or bindValue() to bind the variables, it returns an empty array.
Here's my code:
function search_db($db, $searchTerm, $searchBy, $searchTable){
try{
$stmt = $db->prepare('
SELECT
*
FROM
?
WHERE
? LIKE ?
');
$stmt->bindParam(1, $searchTable);
$stmt->bindParam(2, $searchBy);
$stmt->bindValue(3, '%'. $searchTerm.'%');
$stmt->execute();
} catch(Exception $e) {
return array();
}
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// database initialization, creates the $db variable
require(ROOT_PATH . "include/database.php");
$matches = search_db($db, 'search term', 'myColumn', 'myTable');
var_dump($matches);
Expected results: an array of rows from the database
Actual results: an empty array
Unfortunately, placeholder can represent a data literal only. So, a very common pitfall is a query like this:
$opt = "id";
$sql = "SELECT :option FROM t";
$stm = $pdo->prepare($sql);
$stm->execute([':option' => $opt]);
$data = $stm->fetchAll();
This statement will return just a literal string 'id' in the fieldset, not the value of the column named id.
So, a developer must take care of identifiers oneself - PDO offers no help for this matter.
To make a dynamical identifier safe, one has to follow 2 strict rules:
to format identifiers properly
to verify it against a hardcoded whitelist.
To format an identifier, one has to apply these 2 rules:
Enclose the identifier in backticks.
Escape backticks inside by doubling them.
After such formatting, it is safe to insert the $table variable into query. So, the code would be:
$field = "`".str_replace("`","``",$field)."`";
$sql = "SELECT * FROM t ORDER BY $field";
However, although such formatting would be enough for the cases like ORDER BY, for most other cases there is a possibility for a different sort of injection: letting a user choose a table or a field they can see, we may reveal some sensitive information, like a password or other personal data. So, it's always better to check dynamical identifiers against a list of allowed values. Here is a brief example:
$allowed = array("name","price","qty");
$key = array_search($_GET['field'], $allowed);
$field = $allowed[$key];
$query = "SELECT $field FROM t"; //value is safe
For keywords, the rules are same, but of course there is no formatting available - thus, only whitelisting is possible and ought to be used:
$dir = $_GET['dir'] == 'DESC' ? 'DESC' : 'ASC';
$sql = "SELECT * FROM t ORDER BY field $dir"; //value is safe
I am new to PHP security and trying to implement the solutions other than PDO.
I have read several articles here on stackoverflow and googled many articles.
I have tried to write my own code to secure the user input.
I would request the experts here to please have a look and guide me if I have left anything here or have i used anything unnecessary here.
Also I am missing CSRF prevention. Is there anything else other than random token generation? Can this be implemented using any functions?
extract($_POST);
$stuid = filter_input(INPUT_GET, 'stud_id', FILTER_SANITIZE_SPECIAL_CHARS); //php filter extension
$stuid = trim($stuid);
$stuid = strip_tags($stuid);
$stuid = iconv('UTF-8', 'UTF-8//IGNORE', $stuid); //remove invalid characters.
$stuid = htmlspecialchars($stuid, ENT_COMPAT, 'UTF-8'); // manual escaping
$stuid = mysql_real_escape_string($stuid);
$stuid = htmlspecialchars($stuid, ENT_COMPAT, 'UTF-8'); //Cross site scripting (XSS)
$email = filter_input(INPUT_POST, $email, FILTER_SANITIZE_EMAIL);
$pass=md5($pass);
Thanks in advance.
in a case where my user has submitted a piece of data for the database to store, then i need to be sure i have sanitized it and use a parametized query:
/* Prepare an insert statement */
$query = "INSERT INTO myTable (DangerousData, MoreDangerousData) VALUES (?, ?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param($val1, $val2);
// white listing is always the MOST secure since we control the data
switch ($_POST['DangerousData']) {
case 'Lamb': $val1 = 'Lamb'; break;
case 'Sheep': $val1 = 'Sheep'; break;
// so if they send something not allowed, we have a default
default: $val1 = 'WolfinsheepsClothing';
}
// otherwise, the parametization of the statement will
// clean the data properly and prevent any SQL injection
$val2 = $_POST['MoreDangerousData'];
/* Execute the statement */
$stmt->execute();
For the purposes of Email, you need to study examples on the internet of how to properly sanitize the input coming from the user for the purpose you wish to use it - most people use regular expressions for verifying the safety and validity of an email.
Stackoverflow can help you validate an email.
Stackoverflow can help sanitize user input, too.
I am working on codeigniter project. How can I know queries function in my models are preventing the sql injection.
Even I'm using different ways to insert data but how can i make sure that which one is safe .
Here's my code:
1) *****************
$data = array(
'name' => $_POST['name'],
'email' => $_POST['email'],
'phone' => $_POST['phone'],
'city' => $_POST['city'],
'current_salary' => $_POST['current_salary'],
'expected_salary' => $_POST['expected_salary'],
'reume_link' => $file_name,
'status' => 0,
);
$this->db->insert('my_table_name', $data);
2) **************************
$query = $this->db->query('SELECT distinct(name) as name FROM `my_table_name` WHERE city like "%'.$_POST['state'].'%" ');
$res = $query->result_array();
3) **************************
$query = $this->db->query("insert into my_table_name(nid,sid,cid,data) values('766','$sid',1,'".$_POST['adm_name']."')");
Are the codeigniter function prevent sql injection default or I strictly need to use prepare statement / bind parameter.
Are the simple CI function not safe to use ?
Here is an example of how to use prepared statements -
$sql = 'SELECT distinct(name) name FROM `my_table_name` WHERE city like ?';
$query = $this->db->query($sql, array("%$_POST[state]%"));
means you have to substitute actual data with ? marks and pass it in the form of array as a second parameter.
Most of ActiveRecord methods (like insert, get and such) are safe too, as long as you are following guidelines.
You should use $this->db->escape_str for every variable you put inside your query. Another option (even a better one) is to use prepared statements.
If you were using CodeIgniter's Active Record methods it'll automatically escape queries for you to prevent injection.
$this->db->select('*');
$this->db->from('table_name');
$this->db->where('column_name', $val1);
$this->db->get();
If you don't want to use CI Active Records then there's a function i.e $this->db->escape() in CI
$data1 = $this->db->escape($data1);
$this->db->query("SELECT * FROM table_name WHERE var = '$data1'");
Or you can use query bindings as
$sql = 'SELECT * FROM table_name WHERE var = ?';
$this->db->query($sql, array($var));
Even instead of using $_POST and $_GET CI have its method of $this->input->post() and $this->input->get() respectively
For protection always use $this->input->post('name_of_input') and $this->input->get('name_of_input') instead of $_POST[] & $_GET[]
check this link for documentation
in config.php set
$config['global_xss_filtering'] = True;
or use xss_clean in validation, check the link for documentation
Do I need to use mysql_real_escape_string() on my input (such as $_POST and $_GET) when I use the PDO library?
How do I properly escape user input with PDO?
If you use PDO you can parametize your queries, removing the need to escape any included variables.
See here for a great introductory tutorial for PDO.
Using PDO you can seperate the SQL and passed parameters using prepared statements, this removes the need to escape strings, as because the two are held seperately then combined at execution, the parameters are automatically handled as stings, from the above source:
// where $dbh is your PDO connection
$stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
/*** bind the paramaters ***/
$stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
$stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
/*** execute the prepared statement ***/
$stmt->execute();
Note: sanitization occurs during variable binding ($stmt->bindParam)
Other resources:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
http://www.phpeveryday.com/articles/PDO-Prepared-Statement-P550.html
http://php.net/manual/en/pdo.prepared-statements.php
The important point when using PDO is:
PDO will only sanitize it for SQL, not for your application.
So yes, for writes, such as INSERT or UPDATE, it’s especially critical to still filter your data first and sanitize it for other things (removal of HTML tags, JavaScript, etc).
<?php
$pdo = new PDO(...);
$stmt = $pdo->prepare('UPDATE users SET name = :name WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first
$name = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING); // <-- filter your data first
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO
$stmt->bindParam(':name', $name, PDO::PARAM_STR); // <-- Automatically sanitized for SQL by PDO
$stmt->execute();
Without sanitizing the user input, a hacker could have saved some javascript into your database and then, when output it into your site you would have been exposed to a threat!
http://www.phptherightway.com/#pdo_extension