I'm developing a PHP-MySQL app that enables registered users to enter text comments. Problem is:
User sign-in into the web site - OKAY
User presented with a form to submit text comment - OKAY
User enters text comment and submits - OKAY
I have a routine that sanitize the comment & save it into MySQL (with userid, textcomment, datetime stamp) & present back the user that his/her comment is entered - OKAY
User decides to refresh browser - a duplicate comment is entered - BAD!
I was thinking 3 options:
OPTION 1: Routine that checks: last time user posted comment, and if so, check if this is a duplicate. If duplicate then display error message.
OPTION 2: Routine that does not allow a user to post too quickly. So basically do not allow postings of comments within 1 minute or so. So if browser is refreshed the comment will be ignored.
OPTION 3: Manipulate the browser cache to clear out its contents so when refreshed no duplicate will be entered.
Now in the context of my application, my concerns with OPTION 1 and OPTION 2 is performance PHP-MySQL since I already have various queries within the same page that push/get data from databases. So OPTION 3 may target the issue differently.
Questions is: If I go for OPTION 3 can this be considered a Best Practice? meaning clearing the browser cache is the best most effective solution? I have read that there are consequences too? your thoughts are appreciated!
Just do a redirect after submitting data to the database. It's a common practise.
An http redirect instructs the browser to issue an http GET for the url specified (as opposed to the http POST that is used to submit the form) . If you do this right after you have inserted data into the database, when the user refreshes his browser nothing will happen other than him seeing the same page again.
This question on SO tells how you redirect with php.
Just unset the posted variable after you have inserted it in the database
<?php
if(isset($_POST["comment"])){
//store in the database
//on successful storage
unset($_POST["comment"]);
}
?>
Thus the value won't be posted back when user refreshes the page...
You need to implement the Post/Redirect/Get pattern.
I would use Option 4: Use a one-time form token that authenticates the form request. If the request was successful, invalidate the token.
When doing this, even returning to the form after a redirection won’t allow it to send the form again. Additionally, this will also make CSRF attacks harder.
After entering data into database redirect the page using header or location.href
i have found new way try this.
function PreventResendData($invalidpage='index.php'){
if( $_POST && !is_array( $_SESSION['post_var'] ) ) {
$_SESSION['post_var'] = $_POST;
header('location:'.$_SERVER['PHP_SELF']);
}
else if($_SESSION['post_var'])
{
$_POST = $_SESSION['post_var'];
$_SESSION['post_var'] = '';
}
else
{
header("location:".$invalidpage);
}
}
Related
I have been googling a lot but i am still without an answer so i decided to ask this question here:
Is there a way in PHP how to check that the data i am receiving in some script are from the specific form on the page? I am asking because everyone can see the name of the script i am using for saving the data to my database, so if somebody is able to find out the whole URL, he is also able to send some fake data to the script and i need a condition, that the saving process is triggered only when the data comes from the proper form on my page.
I am using jQuery to call AJAX function, so basically if i click on the button "send", the $.POST() method is triggered to call the script for saving the data.
Thanks,
Tomas
Use tokens to check if request is valid
You could always add some kind of security token when submitting data:
Tokens can be easily extended for many different uses and covers wide area when it comes to checking if some request is valid, for example you could let your non critical forms open for public, ask users to get their secret keys from some page (forcing them to open that page) and then use those keys to identify them when submitting data.
Of course all of this can be completely transparent to user as you could give keys from front page via cookies (or session cookies, it does not matter here, no more or less security as server keys should change after use and invalidate within specified time or when user's identity changes).In this example of use, only user that opened front page can submit data to server.
Another case is when cookies is given away at same page which contains form for submitting data to server. Every user that open page will have their keys to submit data straight away, however if someone tries to make request from outside it will fail.
See OWASP Cross Site Request Forgery
and codinghorror.com Blog CSRF and You
Only with AJAX?
Here is my answer to another question, this answer covers different methods for inserting additional data to ajax request: Liftweb: create a form that can be submitted both traditionally and with AJAX (take a closer look at
$.ajax({
...
data: /* here */
...
Currently I am using tokens this way:
Form used to submit
This hidden input can be added to form, it is not requirement as you can use methods described earlier at another answer.
<input type="hidden" name="formid" value="<?php echo generateFormId(); ?>" />
Function generateFormId()
Simply generate random string and save it to session storage
function generateFormId() {
// Insert some random string: base64_encode is not really needed here
$_SESSION['FORM_ID'] = 'FormID'.base64_encode( uniqid() );
// If you want longer random string mixed with some other method just add them:
//$_SESSION['FORM_ID'] = 'FormID'.base64_encode( crypt(uniqid()).uniqid('',true) );
return $_SESSION['FORM_ID'];
}
Processing submitted form data
if (!isset($_SESSION['FORM_ID']) || $_SESSION['FORM_ID'] != $_POST['formid']) {
// You can use these if you want to redirect user back to form, preserving values:
//$_SESSION['RELOAD_POST'] = $_POST;
//$_SESSION['RELOAD_ID'] = uniqid('re');
echo 'Form expired, cannot submit values.';
//echo 'Go back and try again';
exit(1); // <== Stop processing in case of error.
}
If you need to check which form is submitting data
Then you could just add prefix when generating id's and check for that prefix when processing form data.
This is case when one php script deals with many different forms.
Remember that only ultimate answer to prevent evil users is to pull off all wires from your server...
This is an interesting topic, but you are missing an important point: A spam robot / bad user could also bomb your database by using that specific form page (!).
So, the question is not how to check if the request comes from that page or not, the question is how to check if he's a regular user or a robot/spammer/bot.
Do it with a captcha, like http://www.recaptcha.net
In case i slightly misunderstood the question: If you want to be sure that the request comes from a "real" user you will have to work with a login system / user system and check for users id / password (via db or sessions) every time you want to do a db request.
You can verify that the page is being requested via AJAX with checking against this:
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest'
You could also check the HTTP_REFERER.
But it sounds like you're trying to protect a page that processes vulnerable data. This requires more than just the two things above. I'd recommend googling 'CSRF Forgery' to get more information on this.
These are something that you should take a look at.
Capthcas.
Referer check.
Use POST than GET. [Still Curl can automate it.]
Is there a way to avoid reprocessing forms when I refresh php pages? I'd like to prevent resending forms when refreshing links to php files with an insert function in them. For example, I am processing a series of notes written by users at the top of each page for a new note. Besides the obvious creating a separate php file with a header function is there another way to do it?
Use the Post-Redirect-Get Pattern.
Accept a Post request
Process the data
Issue a redirect response
Accept a Get request
Issue a 200 response
If you need to display data from the submitted stuff, then include a row id or similar in (for example) the query string of the URL you redirect to.
The best way would be to do a header("location: form.php"); call after you process the form. That would redirect you back to the form page, and if you refresh, the browser wont resend the form data.
Alternatively, you could check to see if you already processed the data received, but that would still give you the browser warning message that you are going to resend the data.
You might do both, just in case someone uses the back button and accidentally clicks Submit again.
Just set some flag when you process the form first time so you could check for it and abort reprocessing later on. Session variable or cookie will work fine.
You could put a nonce into the page that is only allowed to be used once so that if you see the same nonce come in you don't do the insert of the page.
I redirect users to a new page after processing of the form.
The form is a POST-request to do-something.php. I check the input data and if it validates I process the data and perform a redirect to do-something.php?somethingdone. So the user can hit F5 w/o resending the POST request.
I tried to use header("Location:..."), $_POST = array(), unset($_POST), but (idk why) they didn't work in my php page.
So, what I did, I just used
echo '< script>window.location.replace("http://.../this.php")</script>'
😂 it works very good! Maybe it is not a good idea, I am learning PHP for the 4th week.
I have this bit of script:
if (isset($_POST['comment_posted'])) {
$user_comment = mysql_real_escape_string($_POST['user_comment']);
$add_user_comment = Event::addUserComment($id,$user->user_id,$user_comment);
}
After a user submits his comment, and refreshes the page, he is being presented with the "you are going to resend the post data" warning. And if the user accepts, it will re-insert the user comment.
I understand that I can prevent that by adding using the header function and redirect the member to the same page. Is it possible to solve this issue without redirecting the member?
No. You'll either do a post-redirect-get or subsequent refreshes will present this dialog to the user.
In case you chose not to do a PRG, you need to somehow detect that the submission is duplicate. One easy way is to have injected a hidden parameter with a random hash/number (e.g called token). Upon submission you'll have to check that the token you expect (which you'll have probably stored in the http session) is being sent together with the other POST parameters. On valid submission you'll remove/invalidate this token. That way when a POST comes which a non recognised token then it's most probably a duplicate or out of date request.
If you implement this correctly then you'll also make your application proof to csrf attacks.
You could set some session variable after successful submission. For each submission you check whether the variable is set or not, on you make an insertion of data.
When posting a form to the same PHP page, what is the correct method to find if the page was accidentally refreshed instead of submitted again?
Here's what I'm using right now:
$tmp = implode('',$_POST);
$myHash = md5($tmp);
if(isset($_SESSION["myHash"]) && $_SESSION["myHash"] == $myHash)
{
header("Location: index.php"); // page refreshed, send user somewhere else
die();
}
else
{
$_SESSION["myHash"] = $myHash;
}
// continue processing...
Is there anything wrong with this solution?
UPDATE: An example for this scenario would be inserting a row into a registration table. You would only want this operation executing once.
Let's start with the point that you can't distinguish accidental refreshes from purposeful refreshes. So you can only decide to not allow refreshes at all, or in other words require that each form submit must be unique. The best way to do that is to insert a random token into the form.
<?php
$token = /* a randomly generated string */;
$_SESSION['_token'] = $token;
?>
<input type="hidden" name="_token" value="<?php echo $token; ?>" />
After each submit you invalidate the session token. If the token in the session differs from the one submitted with the form, you can discard the POST.
Re comment: You could do this on a per-item basis. For example, here on SO, you may have several question windows open and answer several questions at once. You can make the token on a per-question basis, so the user could have several valid token at any one time, but only one per question.
An example for this scenario would be inserting a row into a registration table. You would only want this operation executing once.
In this case you probably shouldn't be too concerned about the actual POST, but about data consistency as such. You should have some form of unique identification for each user, like his email address. If the address is already registered in the database, you do not register the user again. This is independent of why the user tried to register twice (double submit or actual attempt to register again).
I generally prefer having the POST handler do whatever it needs to do and then redirecting the user to a new page with
header("Location: new-page.php");
so they can refresh without messing anything up
Using tokens in conjunction with the POST/REDIRECT/GET design pattern seems to be the best available solution.
Setting a single-use token would prevent the user from hitting the back button and trying to submit the form again.
Redirecting the user and using a view to display the input allows them to hit refresh if they please.
What is the best way to stop a form from being reprocessed when a user hits a Back button?
I'm following the Post/Redirect/Get pattern, so I don't have a problem if F5 is pushed, but the back button still provides the opportunity to resubmit the form. If this form is a credit card processing page, this is bad.
This question has been asked somewhat here, but the phrasing and answers were of poor quality and not specific to this problem exactly.
I have form.php which submits to itself. If there were no errors in input data upon submission, the user is redirected to form_thanks.php. Hitting back (and "Send" or "Resubmit") once resubmits form.php (BAD!) and then brings them back to form_thanks.php.
Please also include solutions that do not involve using Sessions, if possible.
I would do it a different way. Put up a hidden input with a random string as the value, and when it's submitted store that random string in a session. Set up a simple check to see if they've already posted it and if they have don't accept the input.
This should be done with a single-use token, or a nonce. The php/server should include this token in a hidden form field.
It should store a list of all the recent tokens and each time a form is submitted, it should check to see if the token is in the list of recent valid tokens.
If it's not in the list, then don't reprocess the form.
If it is in the list, then process the form and remove the token from the list.
The tokens can be handled within sessions or just a simple database table without sessions. The tokens should be user-specific though.
This is also the recommended way to avoid CSRF attacks.
Just thinking out loud here. But what about a variation on post/redirect/get where the final get is not actually the final get ;) But rather, it in turn always automatically forwards to the truly final page, so that should the user hit the back button, they return right back whence they came?
EDIT:
Ok, taking into consideration the OP's comment, here's another idea. The URL for the form submission could be made to require a parameter that is good for only one use. That token would be generated (using MD5 or some such) before the form was submitted and could be stored in a database (in response to somebody else's suggestion you requested a solution without using sessions). After the form is processed, this token would then be flagged in the database as having already been used. So that when the page is returned to with the same token, steps can be taken to prevent the resubmission of the form data to the backend.
Late answer, but one could avoid the processing altogether by using an AJAX-based solution; there wouldn't be an issue with including a nonce with this processing scheme, but by using an asynchronous query which, on success, redirects the user, the requests are not repeated by refreshing, pressing back, or anything other than clicking the button.
It is also easy to implement a mechanism that prevents the button from either being pressed twice or being "locked up" if something happened during the request by embedding into the handler for the request (whether high level with PrototypeJS or jQuery or low level with your handrolled function) the mechanisms to enable and disable the button when the request completes and first fires, respectively.
I find that back will bring the form to the state it was before the page was redirected, if that is the case, have a hidden input/variable or something which starts with value say true, then once the form is submitted, and if the value is true, change it to false and then submit, else return false
Try this :
<SCRIPT type="text/javascript">
window.history.forward();
</SCRIPT>