Secure $_GET & mysql usage? - php

So I am trying to make some sort of page with stories for example
if(!empty($_GET['page']) && ctype_digit($_GET['page'])) {
$id = mysql_escape_string($_GET['page']); //in case ctype_digit didnt work well.
$pDatabase = Database::getInstance();
$query = "SELECT * FROM stories WHERE id = '$id'";
$result = $pDatabase->query($query) or die('Query failed: ' . mysql_error());
if(mysql_num_rows($result) >0){
//displaying current story
}
else {
header('Location: ' . $_SERVER['HTTP_REFERER']);
}
}
else {
//Show all short stories
}
I want to make sure it is completely safe using $_GET method and mysql query this way , if its not please tell what will be better in this case. Also another thing that bothers me is that I use the query all the time , is it right or should I have some function that would preread all database info before even site launched ? I Mean , what if I want to store Tags or site Title in the Database? Will it be wrong executing (mysql query) title and tags of every page within every page load?

You should not be using MySQL functions built in to PHP, because they are deprecated. Use MySQLi or PDO and learn about prepared statements. I will start by guiding you in the right direction. Trust me, it is worth your time to worry about this, because not using prepared statements could increase chances of SQL injection.
MySQLi - http://php.net/manual/en/intro.mysqli.php
More about prepared statements with MySQLi - http://www.php.net/manual/en/mysqli.quickstart.prepared-statements.php
PDO - http://www.php.net/manual/en/intro.pdo.php
More about prepared statements with PDO - http://www.php.net/manual/en/pdo.prepared-statements.php

Related

Rolling out prepared statements using bind_result & fetch

I am in the process of migrating to php7 & msqli. I have a lot of old php files that will need prepared statements using bind_result & fetch. Thus, before I modify all these files I want to be sure I am coding prepared statements properly using bind_result & fetch, such that they are reasonably safe from sql injection. The code in my example works for me (binds & fetches properly), but I just want to be sure I coded them safely. I am still learning to code prepared statements for other implementations as well.
I also tried get_result instead of bind_result, but for my purposes (db interactions) I believe bind_result will suffice.
Here is the example of a php file that I will be using as the template for all my other php files that will need to be modified with prepared statements using bind_result & fetch:
<?php
//mysqli object oriented - bind_result prepared statement
//connect to database
require 'con_db.php';
//prepare, bind_result and fetch
$stmt = $con->prepare("SELECT image, caption FROM tblimages
WHERE tblimages.catID = 6 ORDER by imageID");
$stmt->execute();
$stmt->bind_result($image, $caption);
while ($stmt->fetch()) {
echo "{$image} <br> {$caption} <br> <br>";
}
$stmt->close();
//close connection
mysqli_close($con);
?>
And here is the php file that makes the db connection via "require", i.e. con_db.php:
<?php
//mysqli object oriented connect to db
//MySQL errors get transferred into PHP exceptions in error log
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
//establish connection, any connection errors go to php.errors
$con = new mysqli('localhost','uid','pw',
'db');
?>
I'm hoping I coded the prepared statements in a reasonably secure fashion to prevent sql injection. But any comments or suggestions are welcome. Thank you.
UPDATE: I decided to show (below) an example of the current code I was going to modify with prepared statements (with the bind_result, fetch example above). So below is a representation of the majority of php/mysqli code that currently exists that lives in many php files that would need to be modified. It is the existing mysql SELECT statements that vary the most. However, based on the feedback I have received I believe since there are no variables being passed there is no reason to use prepared statements with binding. However, I DO have some forms that DO pass variables (GET & POST), and I will modify those php files using prepared statements (bind_param, bind_result & fetch). I hope that made sense :-) I just thought it would be more useful to show an example of the code I originally was planning to modify since I may not need to modify much of it based on feedback I have received here, plus what I have read since my original post (on my concern re: sql injection). But please feel free to correct me if I'm wrong. Thank you.
<?php
//mysqli object oriented - mysqli_query & mysqli_fetch_array
//connect to database
require 'con_db.php';
//query and fetch
$result = mysqli_query($con,"SELECT image, caption FROM
tblimages WHERE tblimages.catid = 1");
while($row = mysqli_fetch_array($result))
{
echo $row['image'];
echo "<br />";
echo $row['caption'];
echo "<br />";
}
mysqli_close($con);
?>
You don't actually need bind_result() and fetch().
With PHP7, almost certainly you will have get_result() that will give you a familiar resource-type variable from which you can get the familiar array.
$stmt = $con->prepare("SELECT image, caption FROM tblimages
WHERE catID = 6 ORDER by imageID");
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) {
echo "{$row['image']} <br> {$row['caption']} <br> <br>";
}
so you can keep a lot of your old code intact.
A couple notes:
Like #Dharman said, you don't really need a prepare/bind/execute routine if no placeholder marks are used i the query.
Like #Dharman said, better try PDO instead, it is much easier to use.
That said, you can greatly reduce the overhead with a simple mysqli helper function. Instead of writing this monster of a code (let's pretend the id in the query is dynamical)
$sql = "SELECT image, caption FROM tblimages WHERE catID = ? ORDER by imageID";
$stmt = $con->prepare($sql);
$stmt->bind_param("s", $catId);
$stmt->execute();
$res = $stmt->get_result();
you can have it in just two lines:
$sql = "SELECT image, caption FROM tblimages WHERE catID = ? ORDER by imageID";
$res = mysqli_select($con, $sql, [$id]);

Why is INSERT INTO followed by SELECT LAST_INSERT_ID() not outputting anything?

The PHP code I have inserts the HTML form data from the previous page into the database and in the same SQL statement return the PostID back from the inserted data. The PostID column is AUTO_INCREMENTING. I have been researching this problem for a week or two now and have found no significant solutions.
<?php
include("dbconnect.php");
mysql_select_db("astral_database", $con);
session_start();
$username = $_SESSION['username'];
$forumtext = $_POST["forumtext"];
$forumsubject = $_POST["forumsubject"];
$postquery = 'INSERT INTO Forums (Creator, Subject, Content) VALUES ("$username", "$forumsubject", "$forumtext"); SELECT LAST_INSERT_ID()';
$result = mysql_query($postquery, $con);
if (!$con) {
echo "<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (331: dbconnect experienced fatal errors while attempting to connect)";
die();
}
if ($username == null) {
echo "<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (332: Username was not specified while attempting to send request)";
die();
}
if ($result != null) {
echo "last id: " . $result;
$fhandle = fopen("recentposts.txt", "r+");
$contents = file_get_contents("recentposts.txt");
fwrite($fhandle, json_encode(array("postid" => $result, "creator" => $username, "subject" => $forumsubject, "activity" => time())) . "\n" . $contents);
fclose($fhandle);
mysql_close($con);
header("location: http://astraldevgroup.com/forums");
die();
} else {
die("<b>If you are seeing this, please send the information below to astraldevgroup#gmail.com</b><br>Error (330: Unhandled exception occured while posting forum to website.)<br>");
echo mysql_error();
}
mysql_close($con);
?>
First off, the mysql_query doesn't return anything from the SELECT statement. I haven't found anything that will properly run both the SELECT statement and the INSERT statement in the same query. If I try running them in two different statements, it still doesn't return anything. I tried running the following statement in the SQL console and it ran perfectly fine without errors.
INSERT INTO Forums (Creator, Subject, Content) VALUES ("Admin", "Test forum 15", "This is a forum that should give me the post id."); SELECT LAST_INSERT_ID();
The mysql_query function does not run multiple statements
Reference: http://php.net/manual/en/function.mysql-query.php
mysql_query() sends a unique query (multiple queries are not supported) to the currently active database on the server ...
That's one reason your call to mysql_query isn't returning a resultset.
The most obvious workaround is to not try to run the SELECT in the same query. You could use a call to the mysql_insert_id instead.
Reference: PHP: mysql_insert_id http://php.net/manual/en/function.mysql-insert-id.php
Answers to some of questions you didn't ask:
Yes, your example code is vulnerable to SQL Injection.
Yes, the mysql_ interface has been deprecated for a long time.
Yes, you should being using either PDO or mysqli interfaces instead of the deprecated mysql_ functions.
FOLLOWUP
Re-visiting my answer, looking again at the question, and the example code.
I previously indicated that the code was vulnerable to SQL Injection, because potentially unsafe values are included in the SQL text. And that's what it looked like on a quick review.
But looking at it again, that isn't strictly true, because variable substitution isn't really happening, because the string literal is enclosed in single quotes. Consider what the output from:
$foo = "bar";
echo '$foo';
echo '"$foo"';
Then consider what is assigned to $postquery by this line of code:
$postquery = 'INSERT ... VALUES ("$username", "$forumsubject", "$forumtext")';
Fixing that so that $username is considered to be a reference to a variable, rather than literal characters (to get the value assigned to $username variable incorporated into the SQL text) that would introduce the SQL Injection vulnerability.
Prepared statements with bind placeholders are really not that hard.
$result will never be null. It's either a result handle, or a boolean false. Since you're testing for the wrong value, you'll never see the false that mysql_query() returned to tell you that the query failed.
As others have pointed out, you can NOT issue multiple queries in a single query() call - it's a cheap basic defense against one form of SQL injection attacks in the PHP mysql driver. However, the rest of your code IS vulnerable other forms of injection attacks, so... better start reading: http://bobby-tables.com
Plus, on the logic side, why are you testing for a null username AFTER you try to insert that very same username into the DB? You should be testing/validating those values BEFORE you run the query.

The difference between PDO sql statements and regular sql?

I've learned PHP from a book and its told me to use PDO objects or sql statements (I'm not sure if that's the right terminology, I apologize if it's not).
When I look up sql stuff, a lot of the times I see stuff like this:
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
if (!mysql_select_db('database_name')) {
die('Could not select database: ' . mysql_error());
}
$result = mysql_query('SELECT name FROM work.employee');
But in my code and in the book, I'm doing stuff like this:
global $db;
$query = "SELECT * FROM users WHERE username='$username'";
$results = $db->query($query);
$results = $results->fetch();
What's the difference between these two 'styles'?
First the function those are mysql_* (like mysql_query, mysql_connect etc) are deprecated and will not be supported in PHP future versions. So PDO or Mysqli are preferred way of communication with database.
The PDO 's prepared statements are used for avoiding SQL injection attacks. like in normal mysql_query you will use like this
$query = "SELECT * FROM users WHERE username='$username'";
$results = mysql_query($query);
but in PDO you have to use like this
$params = array(':username' => 'test', ':email' => $mail);
$pdo->prepare('
SELECT * FROM users
WHERE username = :username
AND email = :email');
$pdo->execute($params);
So PDO is recommended way. For more detail you can refer to
http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers
http://php.net/pdo
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
The first style was written long ago, or was written by people who stopped learning PHP before PHP5 came out. mysql_query is deprecated, and has been for a while now, and you should never be using it in a new project.
The second is using PDO, one of the newer database APIs. PDO supports a bunch of things that make working with SQL easier.
It's still pretty hideous as written, though. Most people would recommend using parameterized queries (a form of prepared statements) to separate the data from the SQL. This helps prevent "SQL injection", a process by which someone feeds you data that tricks your database into executing queries you never intended for it to.

Some posts not updating in database

I'm using a forum type system for users to ask questions or say whats on their minds and I'm having a problem related to updating database information. I have no idea what's wrong here but I do know what's happening.
Some posts cannot be edited. Everything will happen as usual but will not update the database. Some posts have the wrong body but the correct title.
It doesn't make sense and I'll do some tests to check if the mysql is working.. but until then, any thoughts?
UPDATE:
This query is passing... but the database isn't updating for this particular row. This one only...
$new_body = $_POST['new_body'];
$old_body = $_POST['old_body'];
mysql_query("UPDATE questions SET body='".htmlspecialchars($new_body, ENT_QUOTES)."' WHERE body='".htmlspecialchars($old_body, ENT_QUOTES)."'") or die(mysql_error());
Also, if someone could enlighten me on SQL Injections and how to prevent them, I'd greatly appreciate it.
The columns are id, pin, locked, body, date, numberofcomments (i know I can just use php to read the amount of comments but I did this prior to learning that) and views.
UPDATE: Works now. Replaced the WHERE body to WHERE id. Stupid mistake. I could still use some sql injection enlightening though!
As I mentioned in comments first of all use a primary key in your WHERE clause to target specific record in your table instead of using body column. That being said your update statement should look something like this
UPDATE questions SET body = ? WHERE id = ?
Now to prevent sql injections use switch to mysqli_* or PDO extension and use prepared statements instead of interpolating query strings.
Your code using prepared statements with mysqli_* might look like
$id = $_POST['id'];
$new_body = $_POST['new_body'];
$old_body = $_POST['old_body'];
//Do validation, sanitation, and encoding if necessary here before you put into database
...
$db = new mysqli('localhost', 'user', 'password', 'dbname');
if ($db->connect_errno) {
die('Connection failed: %s\n' . $db->connect_error); //TODO better error handling
}
$sql = 'UPDATE questions SET body = ? WHERE id = ?';
$stmt = $db->prepare($sql);
if (!$stmt) {
die('Can\'t prepare: ' . $db->error); //TODO better error handling
}
$stmt->bind_param('si', $new_body, $id);
$stmt->execute();
$stmt->close();
$db-close();
Further reading:
How can I prevent SQL injection in PHP? It's the absolute must read
Please use Mysqli or PDO. Mysql_* is deprecated and insecure.
Have you tried checking if the post exists? As it seems a problem that the post doesn't exist or it's not finding it.
Do you get any mysql_error's or any output from mysql?
Also have you tried updating using phpmyadmin - Seeing if it outputs any errors there?
$new_body = $_POST['new_body'];
$old_body = $_POST['old_body'];
mysql_query("UPDATE questions SET body='".htmlspecialchars($new_body, ENT_QUOTES)."' WHERE body='".htmlspecialchars($old_body, ENT_QUOTES)."'") or die(mysql_error());
I haven't used mysql_ in a while, in favour of PDO, so this syntax may be incorrect. But you could try this:*
$new_body = htmlentities($_POST['new_body']);
$old_body = htmlentities($_POST['old_body']);
$sql1=mysql_query("SELECT * FROM questions WHERE body='$old_body'") or die(mysql_error());
if(mysql_num_rows($sql1)>"0")
{
$res=mysql_query("UPDATE questions SET body='$new_body'") or die(mysql_error());
echo 'Updated';
}
else
{
//Insert.
}

How to insert a variable in count() function?

$id = $_GET['id'];
$result = mysql_query("select Count(id='$id') As Total from topics");
The above code is only working if we put count(id) but i want to get count of selected variable. How to insert id='$id' in count function it is not working please help related this.
You want a where clause in your sql query, which I believe would look like this:
select count(id) As Total from topics where id='$id'
note: depending on what type of column you have for your id field, you might need to drop the single quotes.
Warning
your code is vulnerable to sql injection you need to escape all get, post and request and the better approach will be using Prepared statement
Good Read
How to prevent SQL injection in PHP?
Are PDO prepared statements sufficient to prevent SQL injection?
Note
The entire ext/mysql PHP extension, which provides all functions named with the prefix mysql_, is officially deprecated as of PHP v5.5.0 and will be removed in the future. So use either PDO or MySQLi
Good read
The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead
PDO Tutorial for MySQL Developers
Pdo Tutorial For Beginners
Your question isn't very clear but perhaps you're looking for COUNT CASE WHEN id = $id THEN 1 ELSE 0 END (you can even skip the ELSE 0 part I believe).
What actually are you trying to do is pretty unclear in the Question.
But if you are trying to count the number of rows then simple select count(*) as Total where {your condition} from table will will do for you.
$id get values of $_GET['id']
if you want other data, use $id="your data here"
The following should work:
$id = $_GET['id'];
$result = mysql_query("SELECT COUNT(`" . $id . "`) AS `Total` FROM `Topics`");
But do note that this isn't very secure since it will be vulnerable to SQL Injection attacks.
Count can be used as below
<?php
$shoes=array("nike","puma","lancer");
echo count($shoes);
?>
Read the documentation in the PHP manual on Count.For inserting id in count:
$result = mysql_query('SELECT COUNT(id) FROM clients');
$count = mysql_result($result,0);
echo $count;

Categories