Prevent Users from Performing an Action Twice - php

We have some problems with users performing a specific action twice, we have a mechanism to ensure that users can't do it but somehow it still happens. Here is how our current mechanism works:
Client side: The button will be disabled after 1 click.
Server side: We have a key hash in the URL which will be checked against the key stored in SESSIONS, once it matches, the key is deleted.
Database side: Once the action is performed, there is a field to be flagged indicating the user has completed the action.
However, with all these measures, still there are users able to perform the action twice, are there any more safer methods?
Here is the partial code for the database side:
$db->beginTransaction();
// Get the user's datas
$user = $db->queryRow("SELECT flag FROM users WHERE userid = {$auth->getProperty('auth_user_id)}");
if ($user['flag'] != 0) {
$db->rollback();
// Return with error
return false;
}
// Proceed with performing the action
// --- Action Here ---
// Double checking process, the user data is retrieved again
$user = $db->queryRow("SELECT flag FROM users WHERE userid = {$auth->getProperty('auth_user_id)}");
if ($user['flag'] != 0) {
$db->rollback();
// Return with error
return false;
}
// --- The final inserting query ---
// Update the flag
$db->query("UPDATE users SET flag = 1 WHERE userid = {$auth->getProperty('auth_user_id)}");
$db->commit();
return true;

It is good to see that you have taken all measures to defeat the bad guys. Speaking in terms of bad guys:
Client side: This can easily be bypassed by simply disabling javascript. Good to have anyways but again not against bad guys.
Server side: This is important, however make sure that you generate a different hash/key with each submission. Here is a good tutorial at nettutes on how to submit forms in a secure fashion.
Database side: Not sure but I suspect, there might be SQL injection problem. See more info about the SQL Injection and how to possibly fix that.
Finally:
I would recommend to you to check out the:
OWASP PHP Project
The OWASP PHP Project's goal (OWASP PHP Project Roadmap) is to enable developers, systems administrators and application architects to build and deploy secure applications built using the PHP programming language.

Well the JS method and Hash method may be cheated by some notorious guy, but 3rd method seems to be very good in order to protect the redundancy. There must be some programming flaw to get passed this.
Why don't u just check the flag field on the page where you are inserting the values rather than where user performing the action (if you are doing it now)

Pseudocode follows:
<?
$act_id; // contains id of action to be executed
$h = uniqid('');
// this locks action (if it is unlocked) and marks it as being performed by me.
UPDATE actions SET executor = $h WHERE act_id = $act_id AND executor = '';
SELECT * FROM actions WHERE executor = $h;
//
// If above query resulted in some action execute it here
//
// if you want to allow for executing this exact action in the future mark it as not executed
UPDATE actions SET executor = '' WHERE act_id = $act_id;
Important things:
First query should be update claiming
the action for me if it is yet
unclaimed.
Second should be query
grabbing action to execute but only
if it was claimed by me.

Related

I want to make a secure link

i want to make a code generator link,like
www.website.com/register?code=29U3HTR3H219URH923UH419H94RH1298491U2HERUH1?plan_bought=LowReseller
in a functions file on php that is redirecting an user on that link.
$planned = htmlspecialchars($_GET["planbought"]);
// connect to database
$db = mysqli_connect('localhost', 'root', 'pass');
mysqli_select_db($db,"ronp");
function generateRandomString($length = 16)
{
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
$code_secure = generateRandomString(17); // OR: generateRandomString(24)
$query = "INSERT INTO codes (code, expired, 'date', plan)
VALUES('$code_secure', '0', date, '$planned')";
mysqli_query($db, $query);
header('Location: register?code=', $code_secure);
?>
Process: After payment,paypal will redirect user on https://website.com/functions_generate-ak9esysgenthos.php?planbought=Low
That link will create a code in database,and will redirect user on https://website.com/register?code_secure=(code)
Now the problem is,i get redirected on "https://website.com/register?code=",not "https://website.com/register?code=(the code created in database,like 'J2498JT9UJ249UTJ293UJ59U123J9RU9U')"
If you look at the documentation for header() you'll see that the second parameter is a boolean value. This parameter specifies if the header should be "forcefully" be replaced. You are incorrectly passing your "secure"* code as that parameter.
What you want to do is concatenate the strings instead of passing your "secure" code as a second parameter. What you want to get is
header('Location: register?code=' . $code_secure);
*The "secure" code you are generating is predictable (as you used this code), if you need a secure code you might want to look into openssl_random_pseudo_bytes before PHP 7.0 and random_bytes() in PHP 7.0 or higher, as demonstrated by this answer.
Furthermore, as mentioned by Raymond Nijland your code is vulnerable to SQL injections. See this excellent answer on how you can prevent SQL injections.
Besides the issues mentioned in Tom Udding's answer, there are at least two other issues:
functions_generate-ak9esysgenthos.php can be accessed without any authentication at all. Moreover, it generates a "secure code" blindly, without determining whether a user is logged in or otherwise authorized to access that page (e.g., without determining whether a payment operation is in progress). This could allow anyone with knowledge of the URL to access functions_generate-ak9esysgenthos.php; depending on how your application is implemented, this could cause orders that weren't paid for or even a denial of service attack (due to the additional order codes clogging your database).
You are generating a random "secure code" without checking whether that code was already used. What if /register?code=... finds two records with the same code? Can your application tolerate the risk of generating the same code for different records? (See also my section on unique random identifiers.)

Which protocol will be less "expensive"?

I am adding some server-side form validations (using php) in case one of the users of my site has javascript turned off. On one form, there are 10 separate input fields that can be changed. Could someone please tell me which protocol will use less system resources? In the first, I write some mySQL variables to check the user's current settings, and compare these with the posted settings. If all 10 posted values are identical to the current values, don't UPDATE database, else UPDATE the database:
$login_id = $_SESSION['login_id'];
$sql1 = mysql_fetch_assoc(mysql_query("SELECT value1 FROM login WHERE login_id =
'$login_id'"));
$sql1a = $sql1['value1'];
// Eight More, then
$sql10 = mysql_fetch_assoc(mysql_query("SELECT value10 FROM login WHERE login_id =
'$login_id'"));
$sql10a = $sql10['value10'];
$Value1 = $_POST['Value1'];
// Eight More, then
$Value10 = $_POST['Value10'];
//Other validations then the following
if (($sql1a == $Value1)&&($sql2a == $Value2)&&.......($sql10a == $Value10)) {
echo "<script>
alert ('You haven't made any changes to your profile');
location = 'currentpage.php';
</script>";
}
else {
$sqlUpdate = mysql_query("UPDATE login SET value1 = '$Value1',....value10 = '$Value10'
WHERE login_id = '$login_id'");
echo "<script>
alert ('Your profile has been updated!');
location = 'currentpage.php';
</script>";
}//End php
OR is it less expensive to just use the user-posted values (keep the $_POST variables) and avoid checking with the comparison line: (($sql1a == $Value1)&&($sql2a == $Value2)&&.......($sql10a == $Value10)) and just go right to
//Other validations then the following
$sqlUpdate = mysql_query("UPDATE login SET value1 = '$Value1',....value10 = '$Value10'
WHERE login_id = '$login_id'");
echo "<script>
alert ('Your profile has been updated!');
location = 'currentpage.php';
</script>";
Thanks for any input on this!
If I understand correctly, your question is whether it's OK for performance to check the profile for modifications. For me, after I've checked your code, this is about much more than just performance...
Let's start with the performance: AFAIK MySQL queries are slower than basic PHP comparisions, that's true - but on this scale, I really don't think it matters much. We're talking about two very basic queries which won't handle a lot of data.
Let's think about what the end user will see (UX): in the second scenario, the user will not have the most exact feedback telling him/her that no modification has been done. On a profile modification screen, I suppose that might not be intentional, so I would tell that we haven't modified anything. (Also, performing an unnecessary UPDATE query is not the most elegant.)
#aehiilrs is right, please pay attention to that comment. This style of MySQL usage is particularly bad for security - if you keep going with this, you will create a lot of security holes in your PHP code. And those are really easy to discover and exploit, so please, have a good look on the alternatives, starting with PDO as mentioned. Any good PHP book out there will show you the way. You can also have a look at a great Q/A here on StackOverflow: How can I prevent SQL injection in PHP?
I wonder whether it's a good idea to try to update the user interface like you did - I would strongly prefer loading another PHP without any <script> magic in the output. In the result PHP, you can always display something like a CSS-styled statusbar for displaying info like that.

Update database using JQuery ajax.Post()

Help! I'm writing some code to update a mySQL database using similar to the code below:-
$.post('http://myURL.com/vote.php?personID=' + personID + '&eventID=123');
The vote.php code takes the querystring values and inserts a record into a database with those values in it.
This kind of code is working fine, but I've realised the problem is that people could just type something like:
http://myURL.com/vote.php?personID=5&eventID=123
into their address bar and essentially spam the app...
Is there a straightforward way I can ensure this doesn't happen? I'm reasonably new to these technologies so not aware of how everything works or fits together, but I'm learning fast so any pointers would be super useful.
It is not a good idea to use GET parameters for data that goes to a database. Generally, you want to use POST parameters which are not visible in the URL. So instead of :
$.post('http://myURL.com/vote.php?personID=' + personID + '&eventID=123');
You would do it like this :
$.post('http://myURL.com/vote.php', { "personID" : personID, "eventID" : 123 });
And in your PHP script, you would access your data with the $_POST array like this :
$personID = $_POST['personID'];
$eventID = $_POST['eventID'];
However, don't forget to properly filter input before saving to the database to prevent bad things like SQL Injection.
This is not a silver bullet : spam will still be possible because any HTTP client will be able to send a post request to your site. Another thing you can look at is Security Tokens to make it even less vulnerable to spam. Or implement a system that limits the number of request/minute/user... but I'm getting too far from the original question.
Correct syntax of $.post is
$.post(url,data_to_send,callback_function)
By using this method your user will never be able to damage your site.Use like
$.post('http://myURL.com/vote.php',{"personID":personID,"eventID":123);
Whether you're using POST or GET, you could always consider signing important fields in your page by using hash_hmac. This prevents people from changing its value undetected by adding a signature that no one else can guess.
This also makes CSRF more difficult, though not impossible due to fixation techniques. It's just yet another technique that can be put in place to make it more difficult for "fiddlers".
The following function adds a salt and signature to a given person id to form a secured string.
define('MY_SECRET', 'an unguessable piece of random text');
function getSecurePersonId($personId)
{
$rnd = uniqid("$personId-", true);
$sig = hash_hmac('sha1', $rnd, MY_SECRET);
return "$rnd-$sig";
}
You would pass the output of getSecuredPersonId() to JavaScript to pass as data in the $.post() or $.get(); posting would be recommended btw.
When the form is submitted your person id would end up in either $_GET['personID'] or $_POST['personID'] depending on the request method. To validate the given value, you run it through this function:
function validateSecurePersonId($securePersonId)
{
if (3 != count($parts = explode('-', $securePersonId))) {
return false;
}
// reconstruct the signed part
$rnd = "{$parts[0]}-{$parts[1]}";
// calculate signature
$sig = hash_hmac('sha1', $rnd, MY_SECRET);
// and verify against given signature
return $sig === $parts[2] ? $parts[0] : false;
}
If the value is properly signed, it will return the original person id that you started out with. In case of failure it would return false.
Small test:
$securePersonId = getSecurePersonId(123);
var_dump($securePersonId);
if (false === validateSecurePersonId($securePersonId)) {
// someone messed with the data
} else {
// all okay
}

Prevent off site form posts?

I'm looking for the safest way to not allow my web form, which uses PHP and MySQL, to be posted to from off site. I've done some searching and found that most people suggest setting a hidden field in the form and a session variable with a md5() hash value and check for it on form submission. But that doesn't seem very secure because the md5() hash value can be seen in the source of the form in the hidden value.
Here is my idea to not allow off site form submissions. It's a bit more resource intense with the database calls but looks to be more secure because the code hash is never sent to the client side.
Please take a look at it and see if you can poke any holes in this security measure to prevent off site form posts.
// First time form loads
if (!$_POST) {
session_start();
$code_options = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9');
for ($i=1; $i<=20; $i++) {
$code .= array_rand(array_flip($code_options), 1);
}
// Insert new record
$con = connect_to_db(); // connects to db
$sql = "INSERT INTO security_table (form_code) values('$code')";
$result = run_query($sql, $con); // runs the query
$_SESSION['formcode'] = $code;
$_SESSION['formid'] = mysql_insert_id();
}
// If form was posted to
if ($_POST) {
session_start();
$con = connect_to_db();
$form_code = mysql_real_escape_string($_SESSION['formcode']);
$form_id = mysql_real_escape_string($_SESSION['formid']);
$sql = "SELECT form_code FROM security_table WHERE form_code = '$form_code' AND form_id = '$form_id '";
$result = run_query($sql, $con);
if (mysql_num_rows($result) > 0) {
// Process the form
// If form processes successfully
$_SESSION['formcode'] = "";
$_SESSION['formid'] = "";
}else{
// Error
}
}
Instead of doing this in your application, you could control access to your site though your web server configuration. Nice separation of concerns if you can do it this way -- the server deals with requests and the application only deals with the logic.
Assuming you're using Apache and you have read/write access to your apache http.conf or a local .htaccess, you can add a rule like this:
<Limit GET POST>
order deny,allow
deny from all
allow from 199.166.210.
allow from .golden.net
allow from proxy.aol.com
allow from fish.wiretap.net
</Limit>
So, deny everyone, except for the few IP or network addresses you chose to allow.
See the Apache docs for the nitty gritty details.
But that doesn't seem very secure because the md5() hash value can be seen in the source of the form in the hidden value.
It doesn't matter. If you encrypt it well enough, the token will be greek to anyone, and therefore impossible to recreate unless you know the key mechanism in the php script. Since you regenerate the token after each post, seeing what it looks like won't help any bad guy.
Your way of securing your form is basically "if you have the session, you're fine". So if the spamming machine has visited your page once, the security layer has been passed. The reason for why you have a client side token is that the spammer has to provide something that only is attainable in your actual form.
There are lots of discussions about secure forms at stackoverflow, have a look at Good Form Security - no CAPTCHA, for example.
Here's a good article explaining different methods of securing a form. I've used these methods with good results.
http://www.campaignmonitor.com/blog/post/3817/stopping-spambots-with-two-simple-captcha-alternatives

How to protect processing files

So I've a php form processing file; say a file name process.php with the codes as
<?php
$value = $_POST['string']; //Assume string is safe for database insertion
$result = mysql_query("INSERT into table values {$value}");
if($result) {
return true;
} else {
return false;
}
?>
Ideally, only someone who's logged in to my website shall be allowed to send that POST request to perform that insertion. But here, anyone who know this processing file's path and the request being sent can send any spoof POST request from any domain (if I'm not wrong). This will lead to insertion of unwanted data into the database.
One thing I did is, before the insertion, I checked whether a user is logged in or not. If not, I ignore the POST request. But how exactly should I secure my processing files from exploits?
As it stands this is vulnerable to SQL Injection. Make sure you use a parametrized query library like PDO for inserting the file and the mysql "blob" or "long blob" type. You should never use mysql_query().
You should also keep track of the user's id for user access control. It doesn't look like you have taken this into consideration.

Categories