transferring data by get, php in the url and security - php

Here is the first question and I need your help.
I transfer form data from first page using header location method in php to second page.
On the second page I accept the data using get.
Now here the url of 2nd page, after the data is sent (i.e. form is submitted)
http://mydomain.com/site1/form1_conf.php?id=123
When user is on second page, the data on second page is being displayed according the id number from the mysql database.
Now the problem is that when the user is on second page and he changes the number (for ex. 123 to say 78) the data of id=78, from the database is displayed, which is no good.
How can I stop that?
Please Note: I can't use post, nor can I use sessions.
EDITE:
php code on first page, to transfer to second page:
// after all validations are okay
$insert = //insert into database
$result = mysql_query($insert);
if($result)
{
echo("<br>Input data is succeed");
$lastInsertedId = mysql_insert_id();
header('Location:form1_conf.php?id='.$lastInsertedId); //THIS IS THE IMPORTANT LINE
}
else
{
$message = "The data cannot be inserted.";
$message .= "<br />" . mysql_error();
}

Your problem is not with the URLs: to a power user changing cookies or POST-variables is as trivial as editing GET-variables for a regular user. You'll need some way to 'sign' the requests as being valid.
Easiest to do this is with a "pre-shared key", which you use with one-way hashes to validate requests.
Redirector:
$newURL = '/newpage?id='.$id.'&hash='.sha1('mypresharedkey'.$id);
header('HTTP/1.1 303 See other');
header('Location: '.$newURL);
die;
The other page:
$idToShow = $_GET['id'];
$hash = sha1('mypresharedkey'.$id);
if($hash != $_GET['hash'])
die("Tssss, don't play with the address bar!");
else
RenderThePage();
This ensures end users can only access pages they've been allowed to by the submit.
For your specific code:
...all prior code
$lastInsertedId = mysql_insert_id();
$timestamp = time();
header('Location:form1_conf.php?'.http_build_query([
'id' => $lastInsertedId,
'time' => $timestamp,
'hash' => sha1('some-generated-key'.$timestamp.$lastInsertedId)
]);
In the other page, including a timebomb if you want (otherwise just comment it out):
$id = $_GET['id'];
$time = $_GET['time'];
if($_GET['hash'] != sha1('some-generated-key'.$time.$id))
die('URL was tampered with');
if(time() - $time > 300)
die('URL was only valid for 5 minutes');

You need to track the user and the id that they have in your database to make sure that they haven't changed the number. So when you get the information via the GET you make sure that it is legit.
Users can change the id or even attempt to go directly to that page via the url. So you need some sort of server-side check to verify that it is ok.

You could complicate this "cheating" a bit, if you didn't pass the ID number directly, but somehow encrypted it.
Let's say, you define a salt:
define(SALT, 'long weird salt with special characters etc.');
Here comes the first part you want:
$lastInsertedId = mysql_insert_id();
$querytag = base64_encode($lastInsertedId); // just to make it less readable
$checksum = md5($querytag . SALT); // and make a hash
header('Location:form1_conf.php?id=' . $querytag . '&checksum=' . $checksum);
At the beginning of form1_conf.php, you put this:
$encodedId = $_GET['id'];
$oldChecksum = $_GET['checksum'];
$newChecksum = md5($encodedId . SALT);
$id = base64_decode($encodedId);
if($newChecksum != $oldChecksum) {
die('You Are Cheating!');
}
... do something with the $id ...
The point is that since you add SALT to the hash, some user can't simply use md5 on a changed ID, because he's missing the SALT you used.
It'd be even better if the salt wasn't the same every time.

You should never trust the url because there is always a way to manipulate the data.
So you should do validation after retreiving the data. If the result does not fit you: for example the loggedin user with the ID = 1 requests the settings page from the userid = 3 you do not show the result.
<?php
$userID = $_GET['id'];
if($userID != $expectedResult)
{
//show errormessage, redirect or show the page with the users data
}
?>

Related

Protect your delete.php links sended by email

How to protect my delete.php files
$id = (int) $_GET['id'];
$delete = $connection->prepare("DELETE FROM `articles` WHERE `id` = :id");
$delete->execute(['id' => $id]);
if($delete->rowCount() > 0){
echo 'SUCCESS';
}else{
echo 'ERROR';
}
Lets say am logged in my website panal and my session is on
$_SESSION['user_id'] = My_ID;
And some hacker send a link in my email while i am logged in
<img src="http://my.website.com/panel/articles/delete.php?id=353">
That link is going to delete my article OR WORST a complete main section of my website and that is VERY DANGEROUS. So how can i secure my delete links from that
This is a very good example, why it is stated in the HTTP RFC, that GET method should be used only to request the data, while for the data manipulation it's POST method should be used.
And to protect POST forms from this kind of fraud, a usual CSRF protection have to be used. In short, each form should be signed with a secret token, stored in the session as well. So a site will be able to verify, whether the form was issued by the engine.
An example can be found here, preventing csrf in php
You can build two step confirm form
When user going to articles/delete.php?id=353, you just calculate some hash 'qweadasdasdqw' and provide link just like this articles/delete.php?id=353&hash=qweadasdasdqw
And just build some form with question 'Do you really want to delete article?'
button 'yes' will provide user to url articles/delete.php?id=353&hash=qweadasdasdqw ,
button 'no' wil provide user to list of article.
so if hash is correct, you will be delete this article
Take a look on the code
$hash = isset($_GET['hash']) ? $_GET['hash'] : null;
$id = (int) $_GET['id'];
if (isset($hash) && (md5($id) == $hash)) { // Check hash
$delete = $connection->prepare("DELETE FROM `users` WHERE `id` = :id");
$delete->execute(['id' => $id]);
if($delete->rowCount() > 0){
echo 'SUCCESS';
}else{
echo 'ERROR';
}
} else {
$hash = md5($id); // Generate hash todo use more security function
echo 'Do you really want to delete this article? yes';
}
You could do few things ,
Use post method for data manipulations instead of get.
You can also make use of tokens, make sure you ,regenerate your tokens
Don't just check if user is logged in but also make sure the logged in user has permission to delete / update that record .

How to match cookie with token in database?

I have writen the following script. Everything works in my application, except the validation keeps returning to login. But I have read a lot about my issue, and everything seems right, but of course there should be something wrong otherwise it would work properly.
In my case a user logs in, a token is stored in the database and in a cookie.
For the creation of the token I use:
bin2hex(openssl_random_pseudo_bytes(16));
What I did next is setup a page that first checks if the cookie token and token in the database match. To be sure I first echo them both and both give the same token. I did it like this:
include 'mydatabase.php';
$cookie_name = "My_cookiename";
$result = mysql_query("SELECT * FROM users WHERE token='{$_COOKIE[$cookie_name]}'");
while($row = mysql_fetch_array($result)) {
echo $row['token'];
echo $_COOKIE[$cookie_name];
}
Ok so I am sure at this point the cookie token and database token match.
Now I want to compare them with an if/else. And here I am going wrong, because I can't get it to work. What I have now is this:
$result = mysql_query("SELECT * FROM users WHERE token='{$_COOKIE[$cookie_name]}'");
while($row = mysql_fetch_array($result)) {
if ($row['token'] != $_COOKIE[$cookie_name]) {
header('Location:myloginpage.php'); exit(); } else { // MY PAGE CONTENT IF MATCH }
I think there is something wrong with the line:
if ($row['token'] != $_COOKIE[$cookie_name])
Any help would be great, because I am really stuck at this point.
As the comments on your question have said you are checking things needlessly. The mysql query itself does the token checking for you
include 'mydatabase.php';
$cookie_name = "My_cookiename";
$result = mysql_query("SELECT * FROM users WHERE token='{$_COOKIE[$cookie_name]}'");
if (mysql_num_rows($results) != 1) {
header('Location:myloginpage.php');
exit();
}
// Content for your page goes here, no need for an else because of exit
I think I just solved it :D
I was having the same issue, I took the & sign out of my random token generator. when I surrounded the cookie string with htmlentities() I noticed the & signs were replaced with &amp, because strings usually read & as code. once I removed & from the tokens, it worked. Hope this helps.

Pass generated ID to next page using POST method

In my system when users add a reservation to the database, a transaction ID will be automatically generated:
$insertID = mysql_insert_id() ;
$transactionID = "RESV2014CAI00". $insertID ;
$insertGoTo = "action.php?trans=" . $transact;
But it is very dangerous to pass the $transactionID via address bar. (GET Method) So I need to send transaction to action.php via the POST method.
How can I pass this data using the POST method?
Another approach would be to hash the id as shown below.
$salt = 'your secret key here';
$hash = sha1(md5($transactionID.$salt));
Now you can pass the hash along with the transaction id on the next page and check it match. If it matches then the id wasn't changed. If not then the id was changed. Something like.
$salt = 'your secret key here';
$hash = sha1(md5($_GET['transectionId'].$salt));
if($hash == $_GET['hash']){
//do something
} else {
//Error, id changed
}
If you want to pass the $transactionID from one page to another in your website you can also use $_SESSION.
Information in the basic usage can be found here

$_GET[...] not working - database not updating

I've asked a few questions about this piece of code on this site.
Basically, my database is not updating when I use - $id = $GET_['id']; (at the top of the code below). The id gets passed to this page from the previous page - the url of the page is 'http:// www.21orange.com/CCC/changepassword.php?id=1'. There is an 'id' field in my database.
When I change the above line of code to - $id = '1' - the code runs perfectly and the database is updated. It only stops working when I use the $GET_['id']. Why is this?
// First we execute our common code to connection to the database and start the session
require("common.php");
$id = $_GET['id'];
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Ensure that the user has entered a non-empty username
if(empty($_POST['confirmpassword']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please confirm your password.");
}
if ($_POST['password'] == $_POST['confirmpassword']) {
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "UPDATE Staff SET password=:password, salt=:salt WHERE id=:id";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash.
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$stmt->execute(array(
'password' => $password,
'salt' => $salt,
'id' => $id));
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: stafflist.php");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to stafflist.php");
}
die("Passwords do not match.");
}
I'm new to php, so forgive my naivety. P.s. I know the method I'm using is fairly old school, but it's only a test.
Thanks,
Joe
You can't do both GET and POST in a single HTTP Request.
However, you can use an hidden input field to get around this limitation:
In your HTML markup, you can add the following:
<input type="hidden" name="id"
value="<?php echo htmlspecialchars($_GET['id'], ENT_QUOTES); ?>" />
And your $_GET['id'] should work just fine.
To avoide this error
Undefined index: id in /home/content/47/11368447/html/CCC/changepassword.php on line 6
first test if the index exists :
if(isset($_GET['id'])) {
$id = $_GET['id'];
} else {
// here you can set a value for the id
}
Otherwise, you can add your $id var in your if test :
if(!empty($_POST) && $id)
{
//...
}
It looks like you are passing the 'id' to the action URL but for some reason the $_GET variable is not having it. Please double check:
Are you really passing the 'id' to the URL ? Please make sure.
Please check the code in common.php to see if the $_GET variable is modified in it.
Is that script behind a rewrite settings (e.g in .htaccess) ? if yes, $_GET parameters could be gone due to inappropriate rewrite settings. You can further test it out by putting print_r($_GET); in the beginning and visit that script directly ( GET instead of POST )
$id = $_GET['id'];
first check there is any value in $id to print the $id through echo

Keep a given ID in URL upon successful login

if(!$_POST['username'] || !$_POST['password'])
$err[] = 'All the fields must be filled in!';
if(!count($err))
{
$_POST['username'] = mysql_real_escape_string($_POST['username']);
$_POST['password'] = mysql_real_escape_string($_POST['password']);
$_POST['rememberMe'] = (int)$_POST['rememberMe'];
// Escaping all input data
$row = mysql_fetch_assoc(mysql_query("SELECT id,usr FROM tz_members WHERE usr='{$_POST['username']}' AND pass='".md5($_POST['password'])."'"));
if($row['usr'])
{
// If everything is OK login
$_SESSION['usr']=$row['usr'];
$_SESSION['id'] = $row['id'];
$id = $row['id'];
$_SESSION['rememberMe'] = $_POST['rememberMe'];
// Store some data in the session
setcookie('tzRemember',$_POST['rememberMe']);
}
else $err[]='Wrong username and/or password!';
}
if($err)
$_SESSION['msg']['login-err'] = implode('<br />',$err);
// Save the error messages in the session
$goHere = 'Location: /index2.php?id=' . $id;
header($goHere);
exit;
}
I have the following code that once logged in, it $_GET the id and prepends to the url like index2.php?id=5 . How do I keep this id=5 in the URL no matter WHAT link they click on??
This id is grabbed from this:
$_SESSION['usr']=$row['usr'];
$_SESSION['id'] = $row['id'];
$id = $row['id'];
What I want to do
Well way i have it setup, you login, it then sends you to the homepage such as index2.php?id=[someint] , if you click another link say 'prof.php', it removes the id=[someint] part, I want to keep it there in the url, so as long as a user is LOGGED in -- using my code above, the url might read: index.php?id=5, then go to another page it might read prof.php?id=5, etc, etc. This integer would obviously be dynamic depending on WHO logged in
Instead of passing around an ID in the URL, consider referring to the id value in the $_SESSION variable. That way the user can't modify the URL and see data they aren't supposed to see (or much worse), and you don't have to worry over appending it to every URL and reading it into a value every time you go to process a script. When the user logs in, you determine their ID - read it from a database, determine it realtime, whatever. Then store it in the $_SESSION and refer to it as needed. You can even use this as part of a check to see if the user is logged in - if they have no $_SESSION['id'] value, something is wrong and you make them log in.
The query string isn't the place for that, for a whole host of reasons. The most obvious one is that I can log in with a valid account, then change the number in the URL and it'll think I'm someone else.
Instead, just continue using the session as it's the proper way.
If you REALLY want to do it, you'd probably want to write a custom function for generating links
function makeLink ($link, $queryString = '')
{
return $link . '?id=' . (int) $_SESSION['id'] . ((strpos($queryString, '?') === 0) ? substr($queryString, 1) : $queryString);
}
called like
Click me
As a basic auth example using the ID...
<?php
// Session start and so on here
if (!isset($_SESSION['id']))
{
// Not logged in
header('Location: /login.php');
exit;
}
http://www.knowledgesutra.com/forums/topic/7887-php-simple-login-tutorial/ is a pretty straightforward full example of it.

Categories