how to safely pass variable in php with url rewriting? - php

i have a list of linked thumbnails. Each thumbnail has a link with one variable.
<a href="index.php?id=1"><img src="thumb1.jpg">
<a href="index.php?id=2"><img src="thumb2.jpg">
etc...
now, i've updated site to use url rewriting. Idea is that i have links like this
<a href="gallery/?id=1"><img src="thumb1.jpg">
<a href="gallery/?id=2"><img src="thumb2.jpg">
or something simillar.
On the landing page, i use $id to execute MySQL query and show all pictures from gallery with that id.
$pictures = mysql_query("SELECT * FROM t_gallery where id=$id",$db);
Can it be done, and main thing, how can i prevent that passing id poses a security threat?
Cheers,
Aleks

The url-rewriting part does not itself really introduce any new security-threats, the issue is the usage of mysql_* functions (which are deprecated) and not escaping the $id request-variable.
If you are scared of SQL-Injections (as one should be), either escape the $id variable before using it in the query or rather use prepared statements (and then switch to either mysqli or PDO, which you should do in any case cause of mysql_* being deprecated!).
Always validate and escape anything that you are about to use in a database query.

Please be aware of SQL injection vulnerabilities and make sure you sanitize any user input or consider using prepared statements.

For a particular implementation I leave this encapsulated piece of code
function ControllerAction($id=null) {
// I like to formalize on entry; this is supposed to be an absolute integer and nothing else
$id = abs((int)$id);
$q = 'SELECT * FROM t_gallery where id = ?'; // placeholder
// use PDO, it's safe and very comfortable
$pdo = get_pdo_connection($whatever_you_need);
// prepare it because it has placeholders, and because there is external input comming in
$stmt = $pdo->prepare($q);
// execute it
$stmt->execute(array($id)); // read the reference documentation to understand this clearly
// now the stmt object holds the results
return $stmt->fetchObject();// whatever you like here, I like to make a DAO
}
In this example that simple incomming parameter has already been prepared for use but I still use prepared statements for consistency (my own code convention)
Alert: This uses some form of half-assed MVC/MVP/MVVM (I'm not sure anymore) type architecture
you could actually make your href URLs like this if you are using Url Rewriting to further implement your SEO links
protocol://host/controller/action/getParam1/getParam2
// usage
http://yourhost/browsethumbs/aNumber

Related

how to protect from sql injection when using php?id=

Hello I need help finding a way to protect from sql injection on my current project, Im making bash tutorial site but ive run into a problem. I put most my content in database and depending on what link the user clicks it will pull different data onto the page.
This is how im doing it
apt-get <br>
And on bash_cmds.php
<?php
require_once("connections/connect.php");
$dbcon = new connection();
$bash = $_REQUEST['id'];
$query2 = "SELECT * FROM bash_cmds WHERE id = $bash ";
$results = $dbcon->dbconnect()->query($query2);
if($results){
while($row = $results->fetch(PDO::FETCH_ASSOC)){
$bash_cmd = $row['bash_command'];
$how = $row['how_to'];
}
} else { return false; }
?>
<?php echo $bash_cmd ?>
<br />
<table>
<tr><td><?php echo $how ?> </td></tr>
</table>
However this leaves me vulnerable to sql injection, I ran sqlmap and was able to pull all databases and tables. Can someone please help I would appreciate it a lot the infomation would be invaluable.
There are a couple of ways to do this. I believe the best way is to use some database abstraction layer (there's a good one built into PHP called PDO) and use its prepared statements API. You can read more about PDO here, and you can see the particular function which binds a value to a ? placeholder here.
Alternatively, you could use the mysqli_real_escape_string API function, which should escape any SQL inside your $bash variable.
Of course, in this particular case, simply ensuring the ID is an integer with (int) or intval() would be good enough, but the danger of using this approach in general is that it's easy to forget to do this one time, which is all it takes for your application to be vulnerable. If you use something like PDO, it's more "safe by default," one might say - it's more difficult to accidentally write vulnerable code.
You could bind the values to a prepared statement.
But for something simple as a numeric variable a cast to an integer would be good enough:
$bash = (int) $_REQUEST['id'];
Using this, only a number would get stored into $bash. Even if someone enters ?id=--%20DROP%20TABLE%20xy;, as this will get casted to 1;
I've found one of the easiest ways to protect against injection is to use prepared statements.
You can do this in PHP via PDO, as CmdrMoozy suggested.
Prepared statements are more secure because the placeholders ? can only represent values, and not variables (ie: will never be interpreted as a table name, server variable, column name, etc. It {currently} can't even represent a list of values). This immediately makes any modification to the logic of the query immutable, leaving only possible unwanted values as injection possibilities (looking for an id of 'notanid'), which in most cases isn't a concern (they'd just get a blank/wrong/error page, their fault for trying to hack your site).
Addendum:
These restrictions are what is in place when the prepared statements are done on the server. When prepared statements are simulated by a library instead of actually being server side the same may not be true, but often many of these are emulated.

get id from url security

I'm creating a basic blog and I'm using the following code.
It's collecting the id (always a number) from the url and before I use, I wondered if anyone could check the security of the code and let me know if its ok?
I really don't want any injections, etc, and I want to keep it as much secured as possible.
<?php
if(is_numeric($_GET['id']) && $_GET['id'] > 0){
include("connectionfile.php");
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
$qProfile = "SELECT * FROM #### WHERE id='$ic' ";
$rsProfile = mysql_query($qProfile);
$row = mysql_fetch_array($rsProfile);
extract($row);
$title = trim($title);
$post = trim($post);
$date = trim($date);
mysql_close();
}else{
echo 'hack error here';
}
?>
$ia = intval($_GET['id']);
$ib = mysql_real_escape_string($ia);
$ic = strip_tags($ib);
strip_tags is useless, because it is only relevant in an HTML context. Any one of the other two methods would be sufficient to prevent SQL injection. Generally, just use the appropriate escaping mechanism for the language you're dealing with. In this case you're dealing with SQL, so mysql_real_escape_string alone is fine. See The Great Escapism (Or: What You Need To Know To Work With Text Within Text) for a step-by-step approach to escaping.
Better yet, learn PDO with prepared statements instead of the deprecated mysql_ functions, which solves the issue of SQL injection much better.
Don't use mysql_ functions. They are deprecated. Use mysqli or
PDO.
Use parameterized queries
Don't use "extract" as it pollutes the local scope. There are rare cases where it's safe, usually internal to an ORM, where it's
within the object. This is dangerous otherwise as all forms of
nasty variable names could be introduced, especially with successful
SQL injection.
Do exception handling so that database errors do not break the page entirely, and in the case of a bad query somehow forced via SQL Injection, nothing is displayed to indicate that the query was broken.
Even after you do all the above, still make sure you use htmlentities() or otherwise validate the data is what you expect before you display.
This code is a mess ;-)
if statement can be simplified "if (($id = (int)$_GET['id']) > 0) {"
if you acknowledge my 1. point, then $ia, $ib and $ic can be deleted
don't trim() database data! data should be trimed before INSERT into database.
read what #FilmJ has answered you

php PDO UPDATE statements: which is safer?

I'm learning PDO, and finding it tricky to make sure my statements work correctly. I have a PHP function which is updating my database by simply adding the number 1 to the total.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v1
$sql = "UPDATE places_ratings SET ? +1 WHERE place_id=?";
$q = $db->prepare($sql);
$q->execute(array($rating_id,$place_id));
}
I tried variations of this, none of which I could get to work. I don't know if I was using question marks wrong. I was following this guide and also a previous SO question. In the end I tried a different method which worked first time, so I am tempted to re-use it as it also seems a lot simpler.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v2
$query = "UPDATE places_ratings SET $rating_id = ($rating_id +1) WHERE place_id = $place_id";
$update = $db->query($query);
}
My question is: which statement is better/safer? And secondly, what am I doing wrong with the first version with question marks? Thanks...
In general prepared statements as in your first example are safer because they are immune to SQL injection.
Your example doesn't work because you can't specify field names using a ? parameter in a prepared statement. Even if you could your SQL still would be wrong, this would expand to
UPDATE places_ratings SET whatever +1 WHERE place_id=?
which is not valid.
If your $rating_id is generated in code and not taken from user input you could combine both approaches.
Prepared statements are not simply like copy'n'pasting variables into a piece of text. Prepared statements separate between the query logic and the values the query should work on. They're there so you're able to tell your database "You're supposed to do this", let the database understand it, then give it the values it's supposed to do that something with. The logic itself cannot be variable, it needs to be complete the first time.
Therefore, you can only use placeholders for values. Your query needs to read UPDATE ... SET FIELD = VALUE WHERE FIELD = VALUE. The FIELD parts need to be in the statement, the VALUE parts you can use placeholders for. It looks like your $rating_id variable is a variable field name. First of all, that's a bad idea. You should not make field names variable if possible. But if you have to, you cannot use prepared statement placeholders for them. Instead, you'll have to do it like this:
$rating_id = 'field_name';
$query = "UPDATE places_ratings SET `$rating_id` = `$rating_id` + 1 WHERE `place_id` = ?";
$stmt = $db->prepare($query);
$stmt->execute(array($place_id));
It's up to you to make sure $rating_id is safe and contains known, whitelisted values. Don't let the user supply the value for it in any way.
Please, go an learn what prepared statements are. And you could also use a tutorial, that does not promote bad practices and vulnerable code.
A correctly created and used prepared statement will always be more secure then concatenated query string, because prepared statements send query logic and data separately.
Also , if you are using PDO, then quite often the use of bindParam() method should be preferred over passing the values directly in the execute() method as an array. This is because, when passing values in execute(), the values are bound as PDO::PARAM_STR, even if DB column expects and integer.
P.S. Stop using global in your code !!

How to use $_GET securely?

I need to use a get function to retrieve $title variable from a url.
$title=$_GET["title"];
The $title is later used in a MySQL query.
The question is how to make this secure?
In other words, how to neutralize any malicious codes sent through the URL.
(For a value of "secure" equal to "to prevent it breaking the database"): use any database API that uses bound parameters.
Bound parmeters tend to let the database handle the escaping (so uses escaping routines written by the database authors rather then the language authors) and uses a syntax that is less prone to being forgotten about for that one vital escape then manually escaping each piece of input data with (for example) mysql_real_escape_string.
You might need to take other steps later before you do something with the data in a different context (e.g. to make it safe to insert into an HTML document)
You must use mysql_real_escape_string() to escape all characters that could interfere with you database. If you're displaying this title, you should also make use of htmlentities() or striptags()
As of PHP 5.2, you can use filter_input() and filter_input_array() to sanitize and validate the the $_GET or $_POST data.
For example:
$my_string = filter_input(INPUT_GET, 'my_string', FILTER_SANITIZE_STRING);
Read more about that in this article here.
For SQL queries, it's very recommended that you use PDO with prepared statements to protect from SQL injections. You can read about PDO in the PHP Manual here.
You can use mysql_real_escape_string function (Escapes special characters in a string for use in an SQL statement)
Php Manuel
Use query parameters. There is a number of different ways to connect to mysql from PHP, and they way to use parameters varies a little from framework to framework. Here is an example using PDO:
$dbh = new PDO('mysql:dbname=test;host=127.0.0.1', 'username', 'password');
$sth = $dbh->prepare("select * from table where title = :title")
$sth->execute(array(':title' => $_GET["title"]));
$rows = $sth->fetchAll();
var_dump($rows);

PHP security, intval and htmlspecialchars

<?php
$id = intval($_GET['id']);
$sql = mysql_query("SELECT username FROM users WHERE id = $id");
$row = mysql_fetch_assoc($sql);
$user = htmlspecialchars($row['username']);
?>
<h1>User:<?php echo $user ?></h1>
Can you see any threats in the above code? Do I have to use htmlspecialchars on everything I output? And should i use is_numeric or intval to check so that the get is numeric?
I'm just building a minimal site. I'm just wondering if the above code is vulnerable to sql injection, xss?
Generally speaking mysql_real_escape_string() is preferred but since it's a number, intval() is OK. So yes, it looks OK from a security perspective.
One thing though, on many platforms, ints are limited to 32 bits so if you want to deal in numbers larger than ~2.1 billion then it won't work. Well, it won't work how you expect anyway.
These sorts of security precautions apply to any form of user input including cookies (something many people forget).
I would strongly recommend using PDO and prepared statements. While your statement above looks safe, you're going to have problems as soon as you do more complex queries.
Instead of puzzling over whether a particular query is safe, learn about prepared statements and you won't have to worry. Here is your example, re-written with PDO:
# Make a database connection
$db = new PDO('mysql:dbname=your_db;host=your_db_server', 'username',
'password');
# The placeholder (:id) will be replaced with the actual value
$sql = 'SELECT username FROM users WHERE id=:id';
# Prepare the statement
$stmt = $db->prepare($sql);
# Now replace the placeholder (:id) with the actual value. This
# is called "binding" the value. Note that you don't have to
# convert it or escape it when you do it this way.
$stmt->bindValue(':id', $id);
# Run the query
$stmt->execute();
# Get the results
$row = $stmt->fetch();
# Clean up
$stmt->closeCursor();
# Do your stuff
$user = htmlspecialchars($row['username']);
I've added a lot of comments; it's not as much code as it looks like. When you use bindValue, you never have to worry about SQL injection.
Well,
You are casting the received id to an int ; so no possible SQL injection here.
And the rest of the DB query is "hard-coded", so no problem there either.
If id was a string in DB, you'd have to use mysql_real_escape_string, but for an integer, intval is the right tool :-)
About the output, you are escaping data too (and, as you are outputting HTML, htmlspecialchars is OK) ; so no HTML/JS injection.
So, this short portion of code looks OK to me :-)
As a sidenote, if you are starting developping a new website, it is the moment or never to take a look at either mysqli (instead of mysql), and/or PDO ;-)
It would allow you to use functionnalities provided by recent versions of MySQL, like prepared statements, for instance -- which are a good way to protect yourself from SQL injection !

Categories