Saving User Input by taking care of security as well - php

Following is my script which I am usng to make insertion in my database. I didn't use stripslashes or mysqli_escape because I read that when you are using prepared statement we don't need to use that. The issue I am facing is that will the following method is good in terms of security and whenever I make insertion then for special characters garbage text got stored in my database like following example. Kindly let me know how can I modify the query so it stores the values as they are being saved by user. Thanks,
You shouldn’t move them to the next level of delegation.
$storeit = $_POST['value'];
$stmt = $con->prepare("INSERT INTO db_table (thevalue) VALUES (?)");
$stmt->bind_param("s", $storeit); /// s means only string input is allowed
if ($stmt->execute())
{
echo "true";
}
else
echo "false".$stmt->error;

You have to call $con->set_charset() after connect, and pass the actual encoding of the web page, i.e.:
$con->set_charset('utf8');
if your page is sending utf-8 as content charset.

Your problem seem to be related to encoding.
Be sure that you have the same encoding in:
database
table
database connection
user data (html form)
php file itself when stored
About the input data, you can check and/or adjust the data to whichever character set you want to use. You can check the Multibyte String functions or the iconv functions.

Related

Storing images in bytea fields in a PostgreSQL database

I stored an image in a PostgreSQL database with column type bytea using PHP. The problem is every time I try to load the image in a browser it does not appear. The Firefox developer console says the image is either truncated or corrupt.
The PHP code:
//code for inserting into the database
if(array_key_exists('submit_pic', $_POST)){
$user=$_SESSION['name'];
if(isset($_FILES['thumbnail'])&&$_FILES['thumbnail']['size']>0){
$fi = $_FILES['thumbnail']['tmp_name'];
$p=fopen($fi,'r');
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data);
$q="update userinfo set image='{$dat}' where email='$user'";
$e=pg_query($q)or die(pg_last_error());
// code for retreving from database
require_once('conn.php');
session_start();
$user=$_SESSION['name'];
pg_query('SET bytea_output = "escape";');
$lquery ="select image from userinfo where email='$user'";
$lq = pg_query($lquery)or die(pg_last_error());
$lqq=pg_fetch_row($lq,'image');
header("conent-type:image");
echo pg_unescape_bytea($lqq[0]);
and i need to store the uploaded image in a database- i am actually using heroku thanks
TL;DR:
Delete addslashes($data). It's redundant here.
Double-escaping .. twice
$data=fread($p,filesize($fi));
$data=addslashes($data);
$dat= pg_escape_bytea($data);
You read the data in, escape it as if it were a string literal, then convert it to bytea octal or hex escapes. It could never work that way around even if pg_escape_bytea was sane, which it isn't.
PHP's pg_escape_bytea appears to double-escape the output so it can be inserted into a string literal. This is incredibly ugly, but there doesn't appear to be an alternative that doesn't do this double-escaping, so you can't seem to use parameterised statements for bytea in PHP. You should still do so for everything else.
In this case, simply removing the addslashes line for the data read in from the file is sufficient.
Test case showing that pg_escape_bytea double-escapes (and always uses the old, inefficient octal escapes, too):
<?php
# oh-the-horror.php
print pg_escape_bytea("Blah binary\x00\x01\x02\x03\x04 blah");
?>
Run:
php oh-the-horror.php
Result:
Blah binary\\000\\001\\002\\003\\004 blah
See the doubled backslashes? That's because it's assuming you're going to interpolate it into SQL as a string, which is extremely memory inefficient, ugly, and a very bad habit. You don't seem to get any alternative, though.
Among other things this means that:
pg_unescape_bytea(pg_escape_bytea("\x01\x02\x03"));
... produces the wrong result, since pg_unescape_bytea is not actually the reverse of pg_escape_bytea. It also makes it impossible to feed the output of pg_escape_bytea into pg_query_params as a parameter, you have to interpolate it in.
Decoding
If you're using a modern PostgreSQL, it probably sets bytea_output to hex by default. That means that if I write my data to a bytea field then fetch it back, it'll look something like this:
craig=> CREATE TABLE byteademo(x bytea);
CREATE TABLE
craig=> INSERT INTO byteademo(x) VALUES ('Blah binary\\000\\001\\002\\003\\004 blah');
INSERT 0 1
craig=> SELECT * FROM byteademo ;
x
----------------------------------------------------------------------------
\x426c61682062696e6172795c3030305c3030315c3030325c3030335c30303420626c6168
(1 row)
"Um, what", you might say? It's fine, it's just PostgreSQL's slightly more compact hex representation of bytea. pg_unescape_bytea will handle it fine and produce the same raw bytes as output ... if you have a modern PHP and libpq. On older versions you'll get garbage and will need to set bytea_output to escape for pg_unescape_bytea to handle it.
What you should do instead
Use PDO.
It has sane(ish) support for bytea.
$sth = $pdo->prepare('INSERT INTO mytable(somecol, byteacol) VALUES (:somecol, :byteacol)');
$sth->bindParam(':somecol', 'bork bork bork');
$sth->bindParam(':byteacol', $thebytes, PDO::PARAM_LOB);
$sth->execute();
See:
PHP: Large Objects, which has an example of exactly what you want;
PDOStatement::bindParam
how to store serialized object with namespace in database using pdo php
Bind BYTEA to PGSQL PDO Prepared Statement in PHP5
You may also want to look in to PostgreSQL's lob (large object) support, which provides a streaming, seekable interface that's still fully transactional.
Now, on to my soap box
If PHP had a real distinction between "byte string" and "text string" types, you wouldn't even need pg_escape_bytea as the database driver could do it for you. None of this ugliness would be required. Unfortunately, there are no separate string and bytes types in PHP.
Please, use PDO with parameterised statements as much as possible.
Where you can't, at least use pg_query_params and parameterised statements. PHP's addslashes is not an alternative, it's inefficient, ugly, and doesn't understand database specific escaping rules. You still have to manually escape bytea if you're not using PDO for icky historical reasons, but everything else should go through parameterised statements.
For guidance on pg_query_params:
Bobby tables, PHP section.
The PHP manual on pg_query_params
It is better to use postgres large objects if you really have to store images in your database. In the userinfo table instead of the image itself store just a link to it as loid (large object id).
Insert an image into the database:
pg_query("begin"); // pg_lo functions need to be run in a transaction
$loid = pg_lo_import('full_path_and_file_name');
pg_query("update userinfo set loid=$loid where email='$user'");
pg_query("commit");
Retrieve an image from the database:
$rs = pg_query("select loid from userinfo where email='$user'");
$loid = pg_fetch_row($rs, 0)[0];
pg_query("begin");
$blob = pg_lo_open($loid, "r");
header("Content-type: image");
pg_lo_read_all($blob);
pg_lo_close($blob);
pg_query("commit");
The loid field is of type oid (of course you can name it as you want).
Consider using the lo type from the lo extension instead of using the oid type. Using lo gives you automatic "orphan removal", where deleting a row from a table will automatically remove the associated large object, so it's good for cases where a table row "owns" a large object.
Storing links to images is especially convenient in case you use one image more than one time. However, you should pay attention to delete unused images from your database (PHP function pg_lo_unlink()).
Large objects in postgres documentation.
PHP manual: pg_lo_import.
I found a strange way of getting this to work too without using PDO.
Use a text field in postgresql instead of bytea.
On insert, prep your data like this:
$imgdta = pg_escape_string(bin2hex($filedata));
Then when you want to display the file after your query, use:
echo pack("H*", $img["filedata"]);
I'm not going to pretend I get why, but this worked for me!
As the source of your data is a file in the file system so it seems to me efficient to find an inspiration here:
In your db create an auxiliary function, run as superuser:
create or replace function bytea_import(p_path text, p_result out bytea)
language plpgsql as $$
declare
l_oid oid;
begin
select lo_import(p_path) into l_oid;
select lo_get(l_oid) INTO p_result;
perform lo_unlink(l_oid);
end;$$
security definer;
In your php execute a query like:
#make sure that postgres will have access to the file
chmod($_FILES['thumbnail']['tmp_name'], 0644);
pg_query("update userinfo set image=(select bytea_import('".$_FILES['thumbnail']['tmp_name']."')) where email='$user'");

Insert mysql error when parsing a webpage

Hi when ever I want to insert a comment into my database, I sanitize the data by using Mysql Escape String function this however inserts the following verbatim in field. I print the comment and it works fine and show me the text however when ever I sanitize it, it literally inserts the following into my db
mysql_real_escape_string(Comment)
This is my insert statement, The Id inserts correctly however the comment doesn't it just inserts the "mysql_real_escape_string(Comment)" into the field. what can be wrong?
foreach($html->find("div[class=comment]") as $content){
$comment = $content->plaintext;
$username = mysql_real_escape_string($comment);
$querytwo = "insert into Tchild(Tid,Tcomment)values('$id','$username')";
$resulttwo = $db -> Execute($querytwo);
}
If I'm reading the documentation correctly, you should make the call like this:
$db->Execute("insert into Tchild(Tid,Tcomment)values(?, ?)", array($id, $username));
That will account for proper escaping. Having unescaped values in your query string is dangerous and should be avoided whenever possible. As your database layer has support for SQL placeholders like ? you should make full use of those any time you're placing data in your query.
A call to mysql_real_escape_string will not work unless you're using mysql_query. It needs a connection to a MySQL database to function properly.
Since you're using ADODB, what you want is probably $db->qstr(). For example:
$username = $db->qstr($comment, get_magic_quotes_gpc());
See this page for more information: http://phplens.com/lens/adodb/docs-adodb.htm

PHP PDO PARAMS required if using stored procedures?

I'm new to PHP, but not programming. Have come from an ASP [classic] background. In brief, I'm using PHP 5.4, with FastCGI on IIS7 and SQL Server 2005 Express. I've learnt the fundamentals, and have spent quite some time looking into security.
I'm sanitising both GET and POST input data. My db connection strings are in a separate file placed outside the web root. I'm using PDO prepared statements [though I've heard query+quote perform faster] with named placeholders along with db stored procedures.
I'm trying to understand why I would need to use additional arguments within the bindParam function, particularly data type options "PDO::PARAM_STR, 12" [second argument in that example represent the data length right?].
What are the benefits of specifying the data type and length within the bindParam? Is it needed if I'm using stored procedures in which the data type and length is already specified? Also, I believe I need to use something like "PDO::PARAM_INPUT_OUTPUT" to return a value from a stored proc?
Thanks!
** EDIT **
For some reason, if I use the PDO::PARAM_STR argument, my stored procs don't seem to write data into the db. So I omitted that argument. Here's my code:
$sql1 = $conn->prepare("EXEC insert_platts :userAgent, :userIp, 1, :source");
$sql1->bindParam(':userAgent', $userAgent);
$sql1->bindParam(':userIp', $userIp);
$sql1->bindParam(':source', $source);
$sql1->execute();
Also, rather than returning the identity value from the stored proc, I'm using lastInsertId() instead:
$lastRow = $conn->lastInsertId();
print $lastRow;
No, data type and data length are not needed. I'm using mysql stored procs and the parameters are never typed values, all though I validate them of course. I guess that the reason is extra security and INOUT params. Quote:
To return an INOUT parameter from a stored procedure, use the bitwise
OR operator to set the PDO::PARAM_INPUT_OUTPUT
have you tried this?
$params = array(
':userAgent'=>$userAgent,
':userIp' => $userIp,
':source' => $source
);
$sql1 = $conn->prepare("EXEC insert_platts :userAgent, :userIp, 1, :source");
$sql1->execute($params);
About special characters: are you using correct encodings? I mean, the same encoding in the php app and the DB... sometimes is hard to work with one encoding in the scripts and other in the database.. and very often problems like that arise...

Storing values with apostrophes in the database

What is the industry standard inserting user input that may contain apostrophes into a database? Such an input will be displayed back to users on a webpage. For example, a user updates some field to "I'm cool". I insert it into my database with this function:
public function updateDatabase($value) {
$value = mysql_real_escape_string($value);
Database::instance()->query(
'UPDATE myTable
SET myColumn = ' . $value . '
WHERE foo = "bar"'
);
}
The database will now store "I\'m cool". To display this value properly and safely back to any user, I would have to clean it with this function:
public function toSafeDisplay($userGeneratedValue) {
return stripslashes(
htmlentities(
$userGeneratedValue
)
);
}
My concern is that doing stripslashes and htmlentities on everything I want to display on a webpage will be very processor intensive. The general concensus on StackOverflow is to not do htmlentities before inserting into the database, so that the data is as raw as possible. This would allow it to be later displayed in any medium, not just websites. So we're forced to do htmlentities at display time. Is this also true with stripslashes? Or is it possible to remove all the slashes before the apostrophes before updating the database without introducing SQL injection attacks?.
That's not how it works at all. If you escape an apostrophy going into the insert/update when you read it back into php it will NOT be escaped. If you want HTML safe data coming out of the database then make it safe before you put it in.
Notice to developers that use mysql_real_escape_string properly:
If magic_quotes_gpc is enabled, first apply stripslashes() to the data. Using this function on data which has already been escaped will escape the data twice.
If you found out that magic_quotes_gpc is set to On turn it Off in php.ini!
Check also: http://gr.php.net/manual/en/function.mysql-real-escape-string.php
The database should not store it as I\'m cool, but rather as I'm cool. The escape is to allow the apostrophe to be included as part of the data updated in myColumn. I have seen cases where a site displays I\'m cool back to the user, but that is probably a case of double-escaping.
Edit:
mysql_real_escape_string does not store slashes in the database. It escapes the value in the SQL statement. The only way you would get extra slashes in the database is if you did something equivalent to mysql_real_escape_string(mysql_real_escape_string($value)).

Should I mysql_real_escape_string all the cookies I get from the user to avoid mysql injection in php?

When a user goes to my site, my script checks for 2 cookies which store the user id + part of the password, to automatically log them in.
It's possible to edit the contents of cookies via a cookie editor, so I guess it's possible to add some malicious content to a written cookie?
Should I add mysql_real_escape_string (or something else) to all my cookie calls or is there some kind of built in procedure that will not allow this to happen?
What you really need to do is not send these cookie values that are hackable in the first place. Instead, why not hash the username and password and a (secret) salt and set that as the cookie value? i.e.:
define('COOKIE_SALT', 'secretblahblahlkdsfklj');
$cookie_value = sha1($username.$password.COOKIE_SALT);
Then you know the cookie value is always going to be a 40-character hexidecimal string, and can compare the value the user sends back with whatever's in the database to decide whether they're valid or not:
if ($user_cookie_value == sha1($username_from_db.$password_drom_db.COOKIE_SALT)) {
# valid
} else {
#not valid
}
mysql_real_escape_string makes an additional hit to the database, BTW (a lot of people don't realize it requires a DB connection and queries MySQL).
The best way to do what you want if you can't change your app and insist on using hackable cookie values is to use prepared statements with bound parameters.
The point of mysql_real_escape_string isn't to protect against injection attacks, it's to ensure your data is accurately stored in the database. Thus, it should be called on ANY string going into the database, regardless of its source.
You should, however, also be using parameterized queries (via mysqli or PDO) to protect yourself from SQL injection. Otherwise you risk ending up like little Bobby Tables' school.
I only use mysql_real_escape_string before inserting variables into an SQL statement. You'll just get yourself confused if some of your variables are already escaped, and then you escape them again. It's a classic bug you see in newbies' blog webapps:
When someone writes an apostrophe it keeps on adding slashes ruining the blog\\\\\\\'s pages.
The value of a variable isn't dangerous by itself: it's only when you put it into a string or something similar that you start straying into dangerous waters.
Of course though, never trust anything that comes from the client-side.
Prepared statements and parameter binding is always a good way to go.
PEAR::MDB2 supports prepared statements, for example:
$db = MDB2::factory( $dsn );
$types = array( 'integer', 'text' );
$sth = $db->prepare( "INSERT INTO table (ID,Text) (?,?)", $types );
if( PEAR::isError( $sth ) ) die( $sth->getMessage() );
$data = array( 5, 'some text' );
$result = $sth->execute( $data );
$sth->free();
if( PEAR::isError( $result ) ) die( $result->getMessage() );
This will only allow proper data and pre-set amount of variables to get into database.
You of course should validate data before getting this far, but preparing statements is the final validation that should be done.
You should mysql_real_escape_string anything that could be potentially harmful. Never trust any type of input that can be altered by the user.
I agree with you. It is possible to modify the cookies and send in malicious data.
I believe that it is good practice to filter the values you get from the cookies before you use them. As a rule of thumb I do filter any other input that may be tampered with.
Yegor, you can store the hash when a user account is created/updated, then whenever a login is initiated, you hash the data posted to the server and compare against what was stored in the database for that one username.
(Off the top of my head in loose php - treat as pseudo code):
$usernameFromPostDbsafe = LimitToAlphaNumUnderscore($usernameFromPost);
$result = Query("SELECT hash FROM userTable WHERE username='$usernameFromPostDbsafe' LIMIT 1;");
$hashFromDb = $result['hash'];
if( (sha1($usernameFromPost.$passwordFromPost.SALT)) == $hashFromDb ){
//Auth Success
}else{
//Auth Failure
}
After a successful authentication, you could store the hash in $_SESSION or in a database table of cached authenticated username/hashes. Then send the hash back to the browser (in a cookie for instance) so subsequent page loads send the hash back to the server to be compared against the hash held in your chosen session storage.
mysql_real_escape_string is so passé... These days you should really use parameter binding instead.
I'll elaborate by mentionning that i was referring to prepared statements and provide a link to an article that demonstrates that sometimes mysl_real_escape_string isn't sufficient enough: http://www.webappsec.org/projects/articles/091007.txt
I would recommend using htmlentities($input, ENT_QUOTES) instead of mysql_real_escape_string as this will also prevent any accidental outputting of actual HTML code. Of course, you could use mysql_real_escape_string and htmlentities, but why would you?

Categories