Right format for query - php

I am making a test program in which I need to use this code:
"href=\"testmng.php?manageqn=" . htmlspecialchars_decode($r['testname'], ENT_QUOTES) . "?subjectname=". htmlspecialchars_decode($r['subname'], ENT_QUOTES)
My question is what is the right format when manageqn and subjectname have the right values:
else if ((isset($_REQUEST['manageqn'])) && (isset($_REQUEST['subjectname']))) {
$testname = $_REQUEST['manageqn'];
$subname = $_REQUEST['subjectname'];
$result = executeQuery("select testid from test where testname='" . htmlspecialchars($testname, ENT_QUOTES) . "';");
if ($r = mysql_fetch_array($result)) {
$_SESSION['testname'] = $testname;
$_SESSION['subjectname'] = $subname;
$_SESSION['testqn'] = $r['testid'];
header('Location: prepqn.php');
}
}

Assuming you're using mysqli to connect to the database, you need to escape the string using the myqli_real_escape_string() PHP function, otherwise you risk adding sql injection to your application:
executeQuery("select testid from test where testname='" . myqli_real_escape_string($testname) . "';");
I'd recommend however to switch to a parametrized query approach, by using the prepared statements feature that mysqli provides. You can then have executeQuery() like this:
executeQuery("select testid from test where testname=?", $testname)
no need to escape strings in order to have a safe query.
If you're using the deprecated mysql driver, then you should use mysql_real_escape_string().

Related

PDO/prep statement/whitelisting/set charset, is that safe enough to prevent injection

I am converting from extension mysql to PDO and after reading all I could from you gurus in SO and elsewhere, I have some residual doubts. I came up with the following to address sql injection for a typical query. I am just wondering if that's enough or may be I am going a bit overboard with the whitelisting, before I replicate this to all my application.
It's not clear to me if I did the whitelisting properly, ie, if I should also escape somehow.
Also, I am not sure if I should setAttribute emulate to false for every query or just once for the script.
$link = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password);
$link->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$arr_i=$arr_k='';
$m_act=$v_act='Y';
$table = array('prices', 'versions', 'models');
$allowedTables = array('prices', 'versions', 'models');
$field = array('model_id', 'version_id', 'price', 'models.active', 'versions.active');
$allowedFields = array('model_id', 'version_id', 'price', 'models.active', 'versions.active');
if(count( array_diff($field, $allowedFields))==0 AND count( array_diff($table, $allowedTables))==0){
$sql = "SELECT COUNT(DISTINCT `" . $field[0] . "`) as ctmod FROM `" . $table[0] . "`
INNER JOIN `" . $table[1] . "` USING (`" . $field[1] . "`)
INNER JOIN `" . $table[2] . "` USING (`" . $field[0] . "`)
WHERE `" . $field[2] . "` BETWEEN :arr_i AND :arr_k
AND " . $field[3] . " = :m_act
AND " . $field[4] . " = :v_act";
$stmt = $link->prepare($sql);
$stmt->bindParam(':arr_i', $arr_i, PDO::PARAM_INT);
$stmt->bindParam(':arr_k', $arr_k, PDO::PARAM_INT);
$stmt->bindParam(':m_act', $m_act, PDO::PARAM_STR);
$stmt->bindParam(':v_act', $v_act, PDO::PARAM_STR);
for ($i=0; $i < $ctpri; $i++){
$k=$i+1;
$arr_i=$arr_pri[$i]+1;
$arr_k=$arr_pri[$k];
$stmt->execute();
while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) {
$ctmod[] = $r['ctmod'];
}
}
}
else{die();}
I suspect that you indeed going a bit overboard with the whitelisting. And not only with whitelisting but even with prepared statements too. And to satisfy your wrong views, you over-engineered your query to the point of totally uncomprehensible mess.
What you need to understand is that any constant value is safe by design. So, there is absolutely no point in using nor whitelisting nor prepared statements for it.
So, instead of
AND " . $field[3] . " = :m_act
you should write just
AND versions.active = 'Y'
without any binding or whitelisting.
All you need to protect is dynamical values only. So, you have to use prepared statements for $arr_i and $arr_k only. All other query parts have to be written into query directly, just like you did it before.
Yes, your code is thoroughly safe from SQL injection. Good job.
Though as #YourCommonSense points out, there's no reason in the example you show to make table and columns names into variables at all. It would be simpler to just write them into the query literally.
Therefore, I assume you're asking this question because you do sometimes choose table and column names through application logic or variables, even though you haven't shown it in this particular example.
The only tips I would offer are:
All the string concatenation, with ending double-quotes, using . and re-starting double-quotes makes the code look untidy and it can be confusing to keep track of which double-quotes you've started and stopped. An alternative style of PHP string interpolation for variables is to enclose in curly braces, like the following:
$sql = "SELECT COUNT(DISTINCT `{$field[0]}`) as ctmod FROM `{$table[0]}`
INNER JOIN `{$table[1]}` USING (`{$field[1]}`)
INNER JOIN `{$table[2]}` USING (`{$field[0]}`)
WHERE `{$field[2]}` BETWEEN :arr_i AND :arr_k
AND `{$field[3]}` = :m_act
AND `{$field[4]}` = :v_act";
And for yet another alternative, you can use here documents, so you don't have to worry about delimiting the string at all. Nice if you have literal double-quotes inside your string, because you don't have to escape them:
$sql = <<<GO
SELECT COUNT(DISTINCT `{$field[0]}`) as ctmod FROM `{$table[0]}`
INNER JOIN `{$table[1]}` USING (`{$field[1]}`)
INNER JOIN `{$table[2]}` USING (`{$field[0]}`)
WHERE `{$field[2]}` BETWEEN :arr_i AND :arr_k
AND `{$field[3]}` = :m_act
AND `{$field[4]}` = :v_act
GO;
Finally, it has nothing to do with SQL injection, but a good practice is to check the return value from prepare() and execute(), because they return false if an error occurs in parsing or execution.
if (($stmt = $link->prepare($sql)) === false) {
trigger_error(PDO::errorInfo()[2], E_USER_ERROR);
}
(That example uses PHP 5.4 syntax to dereference an array returned from a function.)
Or else you can configure PDO to throw exceptions, so you don't have to check.
$link->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

mysql_real_escape_string does it allow no quotes within a sql query?

Is this code buggy?
<?php
if (isset($_GET['Submit'])) {
// Retrieve data
$id = $_GET['id'];
$id = mysql_real_escape_string($id);
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result);
$i=0;
while ($i < $num) {
$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
}
?>
Despite the fact that it uses mysql_real_escape_string , an attacker can still inject code without quotes.
It's part of DVWA vulnerable web application.
Does the function mysql_real_escape_string() blocks quotes but to me it seems to be allowing no quotes at all...
Am I correct?
mysql_real_escape_string() should make SQL injection attacks less likely however, it is strongly advised that you use the MySQL database APIs that are not deprecated such as MySQLi (MySQL Improved) and PDO, which have stronger security in place.
I also believe you are calling a non existent function where you assign the $num variable it should be mysql_num_rows() not mysql_numrows.
mysql_real_escape_string() puts the functions argument into a form that is usable inside quotes within SQL. They MUST be used. And they can always be used, even when dealing with numeric values.
Not using the quotes in sql prevents the security of escaping the string.

PHP MySQL Query Syntax Error?

I think i'm doing something wrong here, I'm very new to PHP and only using it to interface my database with my client software through a WWW call, I have a Insert script, which works, but as for my Update script im stumped... here are the queries I tried:
the newest one:
$query = "UPDATE accounts SET moonscore= ' " . $moonscore . " ', sunscore = ' " . $sunscore . " ' WHERE name = ' " . $name . "';";
and I also tried, which I figured was wrong after awhile.
$query = "UPDATE accounts SET moonscore = $moonscore, sunscore = $sunscore WHERE name =$name;
Would really appreciate the help from all you PHP gurus.
try,
$query = "UPDATE accounts
SET moonscore = '$moonscore',
sunscore = '$sunscore'
WHERE name ='$name'";
As a sidenote, the query is vulnerable with SQL Injection if the value(s) of the variables came from the outside. Please take a look at the article below to learn how to prevent from it. By using PreparedStatements you can get rid of using single quotes around values.
How to prevent SQL injection in PHP?
you should use single quotes around the variables ,try this
$query = "UPDATE accounts SET moonscore = '$moonscore' , sunscore = '$sunscore' WHERE name ='$name';
tips: try to use PDO or MYSQLI instead of mysql
Your query is open for SQL Injections. I've added a simple function that always served me well.
function inject($value)
{
// Stripslashes
if (get_magic_quotes_gpc()) {
$value = stripslashes($value);
}
// Quote if not integer
if (!is_numeric($value)) {
$value = "'" . mysql_real_escape_string($value) . "'";
}
return $value;
}
$query = "UPDATE accounts SET moonscore = ".inject($moonscore).", sunscore = ".inject($sunscore)." WHERE name =".inject($name);
Take a look at prepared statements to avoid having to think about protecting your queries against injection with some fancy functions. http://php.net/manual/en/pdo.prepared-statements.php
Here's a video that might give you more insight as a beginner: http://www.youtube.com/watch?v=_bw54BqS2UE

How to protect an ODBC query from SQL injection

What would be the best way to protect this query from sql injection?
This example is just an example, I've read a few articles on internet but can't get my head around parametrised queries. Any links to useful articles will get a vote up but I think seeing this example would help me best.
$id = $_GET["id"];
$connection = odbc_connect("Driver={SQL Server};Server=SERVERNAME;Database=DATABASE-NAME;", "USERNAME", "PASSWORD");
$query = "SELECT id firstname secondname from user where id = $id";
$result = odbc_exec($connection, $query);
while ($data[] = odbc_fetch_array($result));
odbc_close($connection);
Thanks,
EDIT: I didn't make it obvious but I'm using SQL Server not mysql.
This is just an example, it won't always be a number I'm searching on.
It would be nice if the answer used parametrised queries as many people suggest this and it would be the same for all query's instead of different types of validation for different types of user input.
I think PDO objects are the best.
In a nutshell, here is how you use them.
$databaseConnection = new PDO('mysql:host='. $host .';dbname=' . $databaseName, $username, $password);
$sqlCommand = 'SELECT foo FROM bar WHERE baz=:baz_value;';
$parameters = array(
':baz_value' => 'some value'
);
$preparedStatement = $databaseConnection->prepare($sqlCommand);
$preparedStatement->execute($parameters);
while($row = $preparedStatement->fetch(PDO::FETCH_ASSOC))
{
echo $row['foo'] . '<br />';
}
The values you would enter for the SELECT criteria are replaced with parameters (like :field_value) that begin with a colon. The paramters are then assigned values in an array which are passed separately.
This is a much better way of handling SQL queries in my opinion.
The parameters are sent to the database separately from the query and protects from SQL injection.
Use prepared statements. First build a statement with the odbc_prepare() function, then pass the parameters to it and execute it using odbc_execute().
This is much more secure and easier than escaping the string yourself.
Lewis Bassett's advice about PDO is good, but it is possible to use prepared statements with ODBC without having to switch to PDO.
Example code, untested!
try {
$dbh = new PDO(CONNECTION_DETAILS_GO_HERE);
$query = 'SELECT id firstname secondname from user where id = :id';
$stmt = $dbh->prepare($query);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$result = $stmt->execute();
$data = $stmt->fetchAll();
} catch (PDOException $e)
echo 'Problem: ', $e->getMessage;
}
Note: $e->getMessage(); may expose things you don't want exposed so you'll probably want to do something different on that line when your code goes live. It's useful for debugging though.
Edit: Not sure if you wanted a PDO or ODBC example but it's basically the same for both.
Edit: If you're downvoting me please leave a comment and tell me why.
To begin with, be careful with the variables you use in your queries, specially those that come from external sources such as $_GET, $_POST, $_COOKIE and $_FILES. In order to use variables inside your queries you should:
Cast numeric data to integer or float (whichever is appropriate)
Use appropriate escaping to escape other data
A simple example for mysql databases:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . mysql_real_escape_string($name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = '\' OR \'\' =\''
For other database, the escaping practice varies. But generally you're supposed to escape the ' character with '', so:
$id = $_GET["id"]; // contains: OR 1 = 1
$name = $_GET["name"]; // contains: ' OR '' ='
$query = "SELECT * FROM table WHERE id = " . intval($id) . " AND name = '" . str_replace("'", "''", $name) . "'";
// SELECT * FROM table WHERE id = 0 AND name = ''' OR '''' ='''
Having said that, perhaps you might want to switch to PDO. It allows you to use prepared statements, the PDO driver does all the escaping.
The mysql variant came with a method called mysql_real_escape_string, which was appropriate for the version of SQL being targeted. The best thing you can do is write a method to escape the Id. It's important that your escape method is appropriate for the target database. You can also do basic type checking like is_numeric for numeric inputs will reject SQL string injections immediately.
See How to escape strings in SQL Server using PHP?
and follow some of the related links for explicit examples

How to monitor SQL (which uses prepared stataments)?

When first developing an PHP app (MySQl, but using ODBC interfaces to allow for future expansion), I was simply assigning my SQL to a variable and calling odbc_exec().
That made debugging simple, as I just had to examine my variable $sql.
Of course, I soon realized that I have to use prepared statements to sanitize user input.
My question is how to discover the exact SQL which is being executed in the databse, in order to debug my prepared statements.
I reazlise that I can't do it from PHP, but are their any external monitor tools which can interecpt the SQL? Or even a MySql command to echo, if I leave a console window open?
Use the MySQL query log.
You can start mysql server by --log[=file_name] to have a log file.
Here's some code I cam up with to aid debugging within PHP code.
However, to be absolutely certin of what is being executed by MySql, the other posters are correct. Look at the query log.
function OdbcPrepareAndExecute($sql, $parameter_array)
{
if (substr_count($sql, '?') != count($parameter_array))
{
ob_start();
var_dump($parameter_array);
$parameter_array_dump .= ob_get_clean();
ReportErrorAndDie('Invalid parameters',
'Request to prepare ODBC statement "' . $sql .'" with ' .
substr_count($sql, '?') . ' place-holders, but ' .
count($parameter_array) . ' parameters in array : ' .
$parameter_array_dump
);
}
LogDatabaseActivity('Prepare SQL "' . $sql . '"');
$prepared_statement = #odbc_prepare($_SESSION['connection'], $sql);
if($prepared_statement === False)
{
ReportErrorAndDie('Database problem',
'Could not prepare ODBC statement "' . $sql .'"');
}
LogDatabaseActivity('Execute prepared SQL statement with the following parameters:');
ob_start();
var_dump($parameter_array);
$parameter_array_dump = ob_get_clean();
LogDatabaseActivity($parameter_array_dump);
$expanded_sql = $sql;
for ($i = 0; $i < count($parameter_array); $i++)
$expanded_sql = substr_replace($expanded_sql,
$parameter_array[$i],
strpos($expanded_sql, '?'),
1);
LogDatabaseActivity('(preapred statement expands to "' . $expanded_sql . '")');
$result = #odbc_execute($prepared_statement, $parameter_array);

Categories