I have a php script which returns some values with special characters, especially single quotes(') and the 'at sign'(#). The values that contain these characters are not inserted into the database. I saw a post in doing this on mysql database at (http://stackoverflow.com/questions/2584066/php-how-to-insert-special-characters-into-a-database).
My question then is how can it be done in Postgresql database.
See below the php code:
<?php
require 'table.php';
// Opens a connection to a PostgresSQL server
$connection = pg_connect("dbname=postgis user=postgres password=local");
// Execute query
foreach ($xml->item as $entry){
$georss = $entry->children($namespaces['georss']);
list($lat, $lng) = explode(' ', (string)$georss->point);
$query = "INSERT INTO geognews(title, link, author, latitude, longitude) VALUES ('" . $entry->title . "', '" . $entry->link . "', '" . $entry->children($namespaces['dc'])->creator . "', '" . $lat . "', '" . $lng . "')";
$result = pg_query($query);
printf ("These values are inserted into the database - %s %s %s", $entry->title, $entry->link, $entry->children($namespaces['dc'])->creator, $lat, $lng);
}
pg_close();
?>
Use pg_query_params(), by far the easiest way to avoid SQL injection. And thus quotes ' and other risky stuff.
You have a few of options:
You could wrap dynamic data pg_escape_string() (and related functions for other types) to properly encode special characters. This will require the least amount of change to the code you've posted.
You could use prepared statements and bind your dynamic data as parameters. See the docs for pg_prepare() for examples on how to do this. Prepared statements are the recommended way to protect against SQL Injection.
You could use PDO with parameterized queries. This gives you the safety and performance benefits of parameterized queries plus a universal database abstraction layer.
The last option is preferred.
Related
I'm having hard time to figure out whats wrong in this code. I tried many variations but still getting error in this line:
$query= "INSERT INTO publish (name, email, title, content)" .
"VALUES ('$row['Name']','$row['Email']',$row['title'],$row['content'])";
What could be wrong?
here's the rest of the code:
<?php
// connect to the database
include('config2.php');
// check if the 'id' variable is set in URL, and check that it is valid
if (isset($_GET['id']) && is_numeric($_GET['id']))
{
// get id value
$id = $_GET['id'];
$dbc = mysqli_connect('localhost', 'x', 'x', 'x')
or die('Error');
$name = $row['Name'];
$email = $row['Email'];
$title = $row['title'];
$content = $row['content'];
$result = mysql_query("select *stories WHERE id=$id")
or die(mysql_error());
$row = mysql_fetch_array( $result );
$query= "INSERT INTO publish (name, email, title, content)" .
"VALUES ('$row['Name']','$row['Email']',$row['title'],$row['content'])";
or die('Error querying database.');
mysqli_close($dbc);
}
?>
Error message: "parse error expecting identifier (t_string) ' or variable (t_variable) ' or number (t_num_string) '"
You probably want to use complex string syntax to properly interpolate those variables. For example:
$query= "INSERT INTO publish (name, email, title, content)" .
"VALUES ('{$row['Name']}','{$row['Email']}',{$row['title']},{$row['content']})";
Though that will only fix one of the issues with the code.
Do note there are plenty of other ways to resolve this one too, such as concatenation instead of interpolation, or string replacements, etc etc.
It might also be worth reading the documentation on strings at some point.
You forgot the "." between your variables and your strings. Like so:
$query= "INSERT INTO publish (name, email, title, content)" .
"VALUES (".$row['Name'].','.$row['Email'].','.$row['title'].','.$row['content'].")";
However, it looks like you may have some additional issues going on there with the actual SQL query.
The best practice in PHP is to use single quote ' for strings. Cos PHP looks for variables inside double quoted strings and keeps on sniffing whether there is a variable (or multiple variables) inside the string.
So for example: "A very very long string... $var1 .. long string .. $var2 string" this will run slower compared to 'A very very long string... ' . $var1 . ' .. long string .. ' . $var2 . ' string'; cos when PHP sees single quote it won't sniff for variables inside it thus it's faster.
From my experience, in my early age I worked on a very large php script and used double quotes everywhere. After the above explanation from an expert I converted the whole script to single quote and the performance was much better.
So for your situation I'd suggest and request to use single quotes and it'll avoid confusions as well. Also using mysql_real_escape_string() is a good practice to avoid SQL Injection.
$query= 'INSERT INTO publish (name, email, title, content)
VALUES (
\'' . mysql_real_escape_string ($row['Name']) . '\',
\'' . mysql_real_escape_string ($row['Email']) . '\',
\'' . mysql_real_escape_string ($row['title']) . '\',
\'' . mysql_real_escape_string ($row['content']) . '\')';
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().
I insert a text variable in a mySQL table. Everything works fine except in the text is a quotation mark. I thought that I can prevent an error by using "mysql_real_escape_string". But there is an error anyway.
My insert statement:
$insertimage= "INSERT INTO image(filename,text,timestamp,countdown) VALUES ('$filename','$text','$timestamp','$countdown')";
mysql_real_escape_string($insertimage);
The error message:
MySQL Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1413885955514','10')' at line 1
You need to escape data that you are putting into the SQL so that any special characters in it don't break the SQL.
You are escaping all the special characters in the final string of SQL; even those that you want to have special meaning.
If you want to use your current approach, you would do something like this:
$filename = mysql_real_escape_string($filename);
$text = mysql_real_escape_string($text);
$timestamp = mysql_real_escape_string($timestamp);
$countdown = mysql_real_escape_string($countdown);
$insertimage= "INSERT INTO image(filename,text,timestamp,countdown) VALUES ('$filename','$text','$timestamp','$countdown')";
… but the PHP mysql_ extension is obsolete and you shouldn't use it.
Modern APIs, such as mysqli_ and PDO support prepared statements, which are a better way to handle user input. This answer covers that in more detail.
The problem with your current code is that you have not correctly escaped the values you're trying to enter into the table.
Better still is to avoid the mysql_* function family entirely. Those functions are now deprecated and bring security risks to the table (along with other concerns).
You'd be better to use PDO and Prepared Statements, for example:
$db = new PDO('param1', 'param2', 'param3');
$sql = $db->prepare( 'INSERT INTO `image` (`filename`, `text`, `timestamp`, `countdown`)
VALUES (:filename, :text, :timestamp, :countdown)' );
$sql->execute( array(':filename' => $filename,
':text' => $text,
':timestamp' => $timestamp,
':countdown' => $countdown )
);
mysql_real_escape_string($insertimage);
You will have to use this function to each variables before writing the query.
$filename = mysql_real_escape_string($filename);
$text = mysql_real_escape_string($text);
$timestamp = mysql_real_escape_string($timestamp);
$countdown = mysql_real_escape_string($countdown);
$insertimage= "INSERT INTO image(filename,text,timestamp,countdown) VALUES ('$filename','$text','$timestamp','$countdown')";
Try this ,
$insertimage = sprintf("INSERT INTO image(filename,text,timestamp,countdown) VALUES ('%s','%s','%s','%s')", mysql_real_escape_string($filename), mysql_real_escape_string($text), $timestamp, $countdown);
Why, because your inputs vars must be escaped before using them in sql
then execute your sql.
Escaping the entire query is not useful. In fact, right now, you are causing syntax errors by doing so.
You should be escaping the individual variables that you inject into it.
Try this:
$filename = mysql_real_escape_string($filename);
$text = mysql_real_escape_string($text);
$timestamp = mysql_real_escape_string($timestamp);
$countdown = mysql_real_escape_string($countdown);
$insertimage = "INSERT INTO image(filename,text,timestamp,countdown) VALUES ('$filename','$text','$timestamp','$countdown')";
mysql_query($insertimage);
Concat the php variables like this:
$insertimage= "INSERT INTO image(filename,text,timestamp,countdown) VALUES (" . $filenamec . "," . $text . ", " . $timestamp . ", " . $countdown . ")";
with the respective single quotes in those that are text fields i.e: "... '" . $text . "' ..."
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);
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