I would like your opinion about my code. Is it secure enough against any injections.
Thanks for your replies.
class Product extends DB {
public function __construct() {
$db = $this->DB();
if(isset($_POST['productName'])) {
foreach ($_POST as $key => $value) {
if (ini_get('magic_quotes_gpc'))
$_POST[$key] = stripslashes($_POST[$key]);
$_POST[$key] = htmlspecialchars(strip_tags($_POST[$key]));
}
$this->AddProduct();
}
}
public function AddProduct() {
$sSQL = "INSERT INTO ".PREFIX."product (productName, productPrice)
VALUES (:productName,:productPrice)";
$query = $this->db->prepare($sSQL);
$query->execute(array(
":productName" => $_POST['productName'],
":productPrice" => $_POST['productPrice']
));
}
}
Using query parameters is enough to make it secure against SQL injection vulnerability.
The code that calls htmlspecialchars and strip_tags is not relevant to SQL injection. It might be called for to prevent Cross-Site Scripting vulnerabilities, but that's a separate issue. I don't recommend doing those steps as you insert data into the database. Just filter against XSS vulnerabilities when you output to HTML. Otherwise, you get literal & sequences stored in your database, and that's premature. You aren't necessarily going to use the data to display in HTML every time. Just encode it when you output, not when you input.
I never bother with compensating for the possible magic_quotes_gpc. Test for it when you deploy your app, and abort the deployment. It's not valid for any PHP instance to set magic_quotes_gpc in 2014.
Related
This question already has answers here:
How can prepared statements protect from SQL injection attacks?
(10 answers)
Closed 5 years ago.
After my prior post PHP Looping through elements and adding to Database
I went back to the drawing board, as I'm terrified of injection and would like some advice. Is the below "safe":
$stmt = $conn->prepare("INSERT INTO responses (skey, rtext) VALUES (?, ?)");
$stmt->bind_param("is", $skey, $rtext);
$skey = 1;
$rtext = mysqli_real_escape_string($conn,$_POST['Q1Answer']);
if(!$stmt->execute()){trigger_error("there was an error....".$con->error, E_USER_WARNING);}
$skey = 2;
$rtext = "Hello";
if(!$stmt->execute()){trigger_error("there was an error....".$con->error, E_USER_WARNING);}
$stmt->close();
$conn->close();
I could also call the below function:
function SanitizeForSQL($str)
{
if( function_exists( "mysql_real_escape_string" ) )
{
$ret_str = mysql_real_escape_string( $str );
}
else
{
$ret_str = addslashes( $str );
}
return $ret_str;
}
Would any of the above help prevent injection?
When you bind variables to placeholder values your job is done. mysqli or PDO or whatever database driver you're using is responsible for safely encoding it from that point forward.
There is absolutely no need to add more sanitization functions on top of that, and many of these do more harm than good. The most important thing should be ensuring you never put unescaped data in your query, and the safest way to do that is to be extremely disciplined about using placeholder values.
If you pre-escape things then you'll have to un-escape them later. Data you assume is going to be used in HTML may very well turn up in a JSON document when you add an API to something, so now it has to be de-HTML-escaped before properly JSON escaping. This is really obnoxious.
Keep the data in your database as neutral as possible. That is, as raw as you can manage. If you're using Markdown, for example, keep the raw markdown in the database. If you're allowing certain HTML tags, put in whatever the user put in and scrub with a white-lister later. You may want to change your rules in the future to be more relaxed, and if you've already purged that content it's gone forever.
You should use access token. For each database access you should define access token for client and without token query won't be executed.
You can define your function like this:
function SanitizeForSQL($str,$token)
{
if($token=="Predefined token")
{
if( function_exists( "mysql_real_escape_string" ))
{
$ret_str = mysql_real_escape_string( $str );
}
else
{
$ret_str = addslashes( $str );
}
return $ret_str;
}
else return "Token is invalid".
}
I'm building a PDO wrapper that have select, insert, delete, update function, actually I made the update functon that look's like this:
/**
* #param string $table name of the table
* #param array $data data to update
* #param string $where where clause
* #return bool result
*/
public function update($table, $data, $where)
{
ksort($data);
$fieldDetails = NULL;
foreach($data as $key=> $value)
{
$fieldDetails .= "`$key`=:$key,";
}
$fieldDetails = rtrim($fieldDetails, ',');
$sth = $this->prepare("UPDATE $table SET $fieldDetails WHERE $where");
foreach ($data as $key => $value)
{
$sth->bindValue(":$key", $value);
}
return $sth->execute();
}
an example of usage:
$postData = array(
'username' => 'foo',
'role' =>'admin'
);
$this->db->update('login', $postData, "`id` = {$data['id']}");
I don't know if this is secure or I missed something important, someone have suggestione to improve this?
Thanks in advance.
Nope, it is not safe, for two reasons.
First, $data['id'] is put directly to the query and thus is not safe. It have to be bound as well.
Second, $key is put directly to the query and thus is not safe. I wrote an article that demonstrates such a vulnerability and offers a solution: An SQL injection against which prepared statements won't help
Third, $table is put directly to the query and thus is not safe. I know it is meant to be hardcoded in the function call, but you asked here whether this function is safe, while we cannot know how you're calling it.
Besides, as the project grows, it's gets hard to keep the manual control on the data flow. So eventually a table name would become a variable and some day may be accepted from user input. Therefore, it's better to make your function impenetrable by itself, irrelevant to whatever external matters. Just like prepared statements do.
To fix the two latter vulnerabilities you should whitelist your keys, or, at the very least, format them properly. And it should be done in this very function. Otherwise by no means you can call it safe.
I stumble on your question coming from https://laurent22.github.io/so-injections/ which is a statistics of the php related questions on SO with SQL Injection vulnerabilities in them.
So, no. This is not secure as it is now.
This is what is reported as vulnerable and I am quoting it here for future references.
$sth = $this->prepare("UPDATE $table SET $fieldDetails WHERE $where");
Without a doubt, this is used in a lot of code, in a lot of software. Hopefully this will be useful to others too.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Do I need to escape data to protect against SQL injection when using bind_param() on MySQLi?
I have a class that simplifies doing a queries. Here is a code example how to insert data:
$dbi->prepare("INSERT INTO `temp` (`name`,`content`) VALUES (?,?);")->execute($name,$content);
There is a function in class like this:
public function execute(){
if(is_object($this->connection) && is_object($this->stmt)){
if(count($args = func_get_args()) > 0){
$types = array();
$params = array();
foreach($args as $arg){
$types[] = is_int($arg) ? 'i' : (is_float($arg) ? 'd' : 's');
$params[] = $arg;
/*
or maybe $params[] = $this->connection->real_escape_string($arg);
*/
}
array_unshift($params, implode($types));
call_user_func_array(
array($this->stmt, 'bind_param'),
$this->_pass_by_reference($params)
);
}
if($this->stmt->execute()){
$this->affected_rows = $this->stmt->affected_rows;
return $this;
}
else {
throw new Exception;
}
}
else {
throw new Exception;
}
}
When I declare $params[] I have like this $params[] = $arg; Should I put $params[] = $this->connection->real_escape_string($arg); or not?
Thanks.
No. When you use parameters, you don't need to escape the strings.
In fact, you must not escape the strings, because you'll end up storing strings in your database containing literal backslashes. This is not what you want.
According to PHP Manual on mysqli and prepared statements:
Escaping and SQL injection
Bound variables will be escaped automatically by the server. The
server inserts their escaped values at the appropriate places into the
statement template before execution. A hint must be provided to the
server for the type of bound variable, to create an appropriate
conversion. See the mysqli_stmt_bind_param() function for more
information.
The automatic escaping of values within the server is sometimes
considered a security feature to prevent SQL injection. The same
degree of security can be achieved with non-prepared statements, if
input values are escaped correctly.
So no, you don't have to take care about escaping, server will take care about escaping for you.
Escaping manually will result in double escaped values. And in my personal opinion the greatest advantage of placeholders ? in sql statements is that you don't have to take care about escaping.
This is similar to this question - Are Dynamic Prepared Statements Bad? (with php + mysqli), however since it is 4 years old I wanted to get a more upto date answer.
I've written a class which, although I haven't tested it on more copmlex sql queries, it has worked without fail on simple sql queries, however I'm not sure if doing so has bypassed one of the main reasons for prepared statements - security.
I have made use of the call_user_func_array which was easy enough with the bind_param statements however with the bind_result was a little trickier. I originally used get_result however the host I've gone with doesn't have mysqlnd available, but I managed to get around using the metadata. This is the full class I have written.
Do you think this is secure?
The passed in values are:
$sql is the passed in sql statement:
SELECT * FROM users WHERE id = ? AND created_timestamp > ?
$mysqli is the mysqli connection
$para is the placeholder in the prepared statement:
array ($types = 'ii', 23, 1235376000)
The class:
class crudModel {
function ps($sql, $mysqli, $para) {
//this function should work for just about any simple mysql statement
//for more complicated stuff like joins, unions etc,. we will see
if ($prep = $mysqli->prepare($sql)) {
call_user_func_array(array($prep, 'bind_param'), $this->makeValuesRef($para, $mysqli));
$prep->execute();
$meta = $prep->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($prep, 'bind_result'), $parameters);
while ($prep->fetch()) {
foreach ($row as $key=>$val) {
$x[$key] = $val;
}
$data[] = $x;
}
return $data;
}
}
function makeValuesRef($array, $mysqli) {
$refs = array();
foreach($array as $key => $value) {
$array[$key] = $mysqli->real_escape_string($value); //i don't think escaping is necessary, but it can't hurt (??)
$refs[$key] = &$array[$key];
}
return $refs;
}
}
What you're doing here isn't a dynamic prepared statement. You're just putting some syntatic sugar on top of the MySQLi API (which sucks).
In short, there aren't really any security concerns present from the code you've shown here. In fact, this sort of practice is quite good, because it makes it easier to verify that you're doing it correctly later (since the MySQLi API sucks).
So you're fine. I would worry about the areas you're generating the queries, and ensuring that you're not accidentally putting user-data into them without white-listing...
I realize there are a lot of questions already about this. But my method isn't the same as theirs, so I wanted to know. I think I understand SQL, but I don't want to risk making a mistake in the future, so thanks for any help. (This is just a project I'm doing, not homework or anything important).
function checkLogin($username, $password) {
$username = strtolower($username);
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");
$dbpassword = "";
while($row = mysql_fetch_array($result))
{
$rowuser = $row['username'];
if($username != $row['username']) continue;
$dbpassword = $row['password'];
}
if($dbpassword == "") {
return false;
}
$genpass = generatePassword($password);
return $genpass == $dbpassword;
}
So hit me with your best shot :)
And I don't think my method is as efficient as it could be. I don't understand php enough to understand what $row = mysql_fetch_array($result) is doing unfortunately.
Because you are taking an arbitrary string and placing it directly into an SQL statement, you are vulnerable to SQL injection.
( EDITED based on a comment below. )
The classic example of SQL injection is making a username such as the following:
Robert'); DROP TABLE users;--
Obligatory XKCD link
Explanation:
Given the "username" above, interpolation into your string results in:
SELECT * FROM `users` WHERE username='Robert'); DROP TABLE users;--'
The comment symbol -- at the end is required to "get rid" of your closing quote, because I just substituted one of mine to end your select statement so that I could inject a DROP TABLE statement.
As #sarnold pointed out, PHP's mysql_query only executes a the first query in the string, so the above example (known as query stacking) does not apply. The function is explained here: http://php.net/manual/en/function.mysql-query.php.
A better example can be found here. Here they use a username of
' OR 1 OR username = '
which interpolated becomes
SELECT * FROM `users` WHERE username='' OR 1 OR username = ''
and which would cause your application to retrieve all users.
The short answer is yes.
A perhaps more helpful answer is that you should never trust user input; prepared statements are the easiest way to protect against this, if you have PDO available. See PDO Prepared Statements
<?php
$stmt = $dbh->prepare("SELECT * FROM `users` WHERE username=?");
if ($stmt->execute($username)) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
?>
The other answers are an excellent description of your problem, however, I think they both overlook the best solution: use PHP's PDO Prepared Statements for your queries.
$stmt = $dbh->prepare("SELECT * FROM users where username = ?");
if ($stmt->execute(array($username))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
This is a small, simple example. There are more sophisticated ways of using PDO that might fit your application better.
When you use PDO prepared statements you never need to manually escape anything and so long as you use this slightly different style, you will never write an SQL injection vulnerability and you don't have to maintain two variables per underlying "data" -- one sanitized, one as the user supplied it -- because only one is ever required.
I would say yes it is open to SQL injection.
This is because you are taking user input in the form of $username and putting it into your SQL statement without making sure it is clean.
This is a function that I like to use in my applications for the purpose of cleaning strings:
function escape($data) {
$magicQuotes = get_magic_quotes_gpc();
if(function_exists('mysql_real_escape_string')) {
if($magicQuotes) {
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
}
else {
if(!$magicQuotes) {
$data = addslashes($data);
}
}
return $data;
}
Then you can use it like this:
$username = escape(strtolower($username));
connectToDatabase();
$result = mysql_query("SELECT * FROM `users` WHERE username='$username'");