Preventing form resubmission - forms

Page one contains an HTML form. Page two - the code that handles the submitted data.
The form in page one gets submitted. The browser gets redirected to page two. Page two handles the submitted data.
At this point, if page two gets refreshed, a "Confirm Form Resubmission" alert pops up.
Can this be prevented?

There are 2 approaches people used to take here:
Method 1: Use AJAX + Redirect
This way you post your form in the background using JQuery or something similar to Page2, while the user still sees page1 displayed. Upon successful posting, you redirect the browser to Page2.
Method 2: Post + Redirect to self
This is a common technique on forums. Form on Page1 posts the data to Page2, Page2 processes the data and does what needs to be done, and then it does a HTTP redirect on itself. This way the last "action" the browser remembers is a simple GET on page2, so the form is not being resubmitted upon F5.

You need to use PRG - Post/Redirect/Get pattern and you have just implemented the P of PRG. You need to Redirect. (Now days you do not need redirection at all. See this)
PRG is a web development design pattern that prevents some duplicate form submissions which means, Submit form (Post Request 1) -> Redirect -> Get (Request 2)
Under the hood
Redirect status code - HTTP 1.0 with HTTP 302 or HTTP 1.1 with HTTP 303
An HTTP response with redirect status code will additionally provide a URL in the location header field. The user agent (e.g. a web browser) is invited by a response with this code to make a second, otherwise identical, request to the new URL specified in the location field.
The redirect status code is to ensure that in this situation, the web user's browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.
Double Submit Problem
Post/Redirect/Get Solution
Source

Directly, you can't, and that's a good thing. The browser's alert is there for a reason. This thread should answer your question:
Prevent Back button from showing POST confirmation alert
Two key workarounds suggested were the PRG pattern, and an AJAX submit followed by a scripting relocation.
Note that if your method allows for a GET and not a POST submission method, then that would both solve the problem and better fit with convention. Those solutions are provided on the assumption you want/need to POST data.

The only way to be 100% sure the same form never gets submitted twice is to embed a unique identifier in each one you issue and track which ones have been submitted at the server. The pitfall there is that if the user backs up to the page where the form was and enters new data, the same form won't work.

There are two parts to the answer:
Ensure duplicate posts don't mess with your data on the server side. To do this, embed a unique identifier in the post so that you can reject subsequent requests server side. This pattern is called Idempotent Receiver in messaging terms.
Ensure the user isn't bothered by the possibility of duplicate submits by both
redirecting to a GET after the POST (POST redirect GET pattern)
disabling the button using javascript
Nothing you do under 2. will totally prevent duplicate submits. People can click very fast and hackers can post anyway. You always need 1. if you want to be absolutely sure there are no duplicates.

You can use replaceState method of JQuery:
<script>
$(document).ready(function(){
window.history.replaceState('','',window.location.href)
});
</script>
This is the most elegant way to prevent data again after submission due to post back.
Hope this helps.

If you refresh a page with POST data, the browser will confirm your resubmission. If you use GET data, the message will not be displayed. You could also have the second page, after saving the submission, redirect to a third page with no data.

Well I found nobody mentioned this trick.
Without redirection, you can still prevent the form confirmation when refresh.
By default, form code is like this:
<form method="post" action="test.php">
now, change it to
<form method="post" action="test.php?nonsense=1">
You will see the magic.
I guess its because browsers won't trigger the confirmation alert popup if it gets a GET method (query string) in the url.

The PRG pattern can only prevent the resubmission caused by page refreshing. This is not a 100% safe measure.
Usually, I will take actions below to prevent resubmission:
Client Side - Use javascript to prevent duplicate clicks on a button which will trigger form submission. You can just disable the button after the first click.
Server Side - I will calculate a hash on the submitted parameters and save that hash in session or database, so when the duplicated submission was received we can detect the duplication then proper response to the client. However, you can manage to generate a hash at the client side.
In most of the occasions, these measures can help to prevent resubmission.

I really like #Angelin's answer. But if you're dealing with some legacy code where this is not practical, this technique might work for you.
At the top of the file
// Protect against resubmits
if (empty($_POST)) {
$_POST['last_pos_sub'] = time();
} else {
if (isset($_POST['last_pos_sub'])){
if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
redirect back to the file so POST data is not preserved
}
$_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
}
}
Then at the end of the form, stick in last_pos_sub as follows:
<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>

Try tris:
function prevent_multi_submit($excl = "validator") {
$string = "";
foreach ($_POST as $key => $val) {
// this test is to exclude a single variable, f.e. a captcha value
if ($key != $excl) {
$string .= $key . $val;
}
}
if (isset($_SESSION['last'])) {
if ($_SESSION['last'] === md5($string)) {
return false;
} else {
$_SESSION['last'] = md5($string);
return true;
}
} else {
$_SESSION['last'] = md5($string);
return true;
}
}
How to use / example:
if (isset($_POST)) {
if ($_POST['field'] != "") { // place here the form validation and other controls
if (prevent_multi_submit()) { // use the function before you call the database or etc
mysql_query("INSERT INTO table..."); // or send a mail like...
mail($mailto, $sub, $body); // etc
} else {
echo "The form is already processed";
}
} else {
// your error about invalid fields
}
}
Font: https://www.tutdepot.com/prevent-multiple-form-submission/

use js to prevent add data:
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}

Related

How to add the CSRF token to a form submission in ApostropheCMS

In the documentation is described how to exclude routes from CSRF protection, but I would prefer to include them. Is there a way to add the CSRF token into a hidden field, what name should it have?
Scanning through source code I only see a header that is added to AJAX calls, does this mean than in the current version regular POST calls aren't possible to make safe?
We could have allowed a hidden field to be used, but a hidden field is less safe than a header because a third party phishing site is permitted to submit regular POST forms, but it is not permitted to add HTTP headers to requests. So the standard protection covers you in two ways: (1) the attacker can't access the CSRF cookie, and (2) the attacker can't add a header either because only JavaScript can add headers and JavaScript is subject to the same-origin policy.
Thus using a jQuery form submission rather than "plain vanilla" POST is best practice for ApostropheCMS.
However jQuery form submissions are very easy anyway and they work better; you don't have to render an entirely new page for the user, for one thing.
Here is a simple example of jQuery code to enhance an ordinary HTML form:
$(function() {
var $myForm = $('.my-form');
$myForm.on('submit', function() {
$.post($myForm.attr('action'), $myForm.serialize(), function() {
// It worked, now display a thank you message, or navigate somewhere
}).fail(function() {
// An error was received, show the user a message etc.
});
// IMPORTANT: prevent the traditional submission
return false;
});
});
Here I am pulling the URL to POST the form to from the action attribute but you don't have to do that, you can use any URL that is set up to receive a POST form submission.
Note that I did not do anything special to get AJAX protection here. As long as you use jQuery's AJAX mechanisms (including $.post), it is automatic with ApostropheCMS.
You can fill in my success and fail functions here with code to show or hide messages already embedded in your page, etc.
Edit: here is a possible workaround to make it work the way you want it to.
// Browser-side JavaScript
$('form').each(function() {
$(this).append('<input type="hidden" name="xsrf-token" value="' + $.cookie(apos.csrfCookieName) + '" />');
});
Now, in lib/modules/your-module/index.js, you can supply middleware to put this hidden field where Apostrophe expects it:
self.expressMiddleware = {
when: 'beforeRequired',
middleware: function(req, res, next) {
if (!(req.body && req.body['xsrf-token'])) {
return next();
}
req.headers['X-XSRF-TOKEN'] = req.body['xsrf-token'];
return next();
}
};

How to stop re submitting a form after clicking back button [duplicate]

This question already has answers here:
Prevent user from seeing previously visited secured page after logout
(7 answers)
Closed 6 years ago.
I have a JSP page with a form where you fill up certain details. On submitting the form i call a servlet which then updates details in a database after that i redirect to a page i display a confirmation message to the user.
The problem i have here is when the user clicks back he goes to the form page again where if he clicks a submit button, it creates a new record in the database.
Consider this similar to a shopping cart example where in this case he would buy the same item twice. But the only problem here is i cannot handle this in backend, i.e. the database can never know a redundant operation is taking place.
I need to handle this from the client side.Bit weird but i have to do it this way.
Basically when the user clicks the back button i don't want him to be able to go to the form page and may be just to some other page.
This is a typical HTML form issue, you can solve it through any one of following methods
1) Server side (page expiration): Since you've mentioned that the page refreshes and goes to the confirmation. You can set no-cache and add a page expiration time as -1 to the page you have that form.
So that when user presses the back button, he will see that the page has expired. He will be forced to load the page freshly. This is the behavior that I see in most banking websites.
Response.Buffer = True
Response.ExpiresAbsolute = Now() - 1
Response.Expires = 0
Response.CacheControl = "no-cache"
2) Using turn Key method: When the form loads, you can generate a random key in session memory and also embed it as a hidden value in the page.
During form submission, check the submitted hidden key against the value in session. If exists, then add the entry to database otherwise you can reload the page with a fresh key for the user (who might have done that unintentionally).
In load balanced or web farms, consider persisting the turn key in Database against the current user.
3) Client Side (Not recommended) : You can restrict the user from using the browser back button by removing the page from the history. (One side effect is that it will remove the entire history for that browser tab/window)
history.pushState(null, null, document.title);
window.addEventListener('popstate', function () {
history.pushState(null, null, document.title);
});
If you need the same support for older browsers where push and pop states are not supported, you can use following snippet.
<script>
function preventBack() {
window.history.forward();
}
setTimeout("preventBack()", 0);
window.onunload = function() {
null
};
</script>
Before redirecting to the JSP page send these headers with the response from the controller so that the page is not stored in cache and the browser will always request a new page from the server.
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0);
So every time you go back to that page a new page will be requested from the server and will be displayed with the cleared input fields.
You could implement a PRG-Pattern (https://en.wikipedia.org/wiki/Post/Redirect/Get) using php.
Basically, after successfully submitting the form you redirect to a confirmation page which informs the user that their submission was successful/accepted/etc. and also set a variable which you can later use to check if said submission has been submitted yet.
When the user tries to reload said page or go back you can check the variable and display either the form again or the confirmation page based on if the submission has been submitted already.
I think following flow is the best:
Client submits data to server
Servlet processes it
It returns HTTP 303 redirect to client
Client redirects to success page
After this flow, refresh, back button will work properly.
For more information read Simple Post-Redirect-Get code example

Simplest example for sending post data via links in Zend Framework

Starting with Zend and I´d like to know what is the simplest way of sending POST data to another page, not by forms, but by some link in my view instead. Thanks :)
You can't send POST data through a link. At least not through a normal link. Link can only carry GET data.
If you need to send POST over a link it's most certainly a design flaw.
If you're 100% sure, that you need it, you can do that using jQuery and onclick event. It`s not possible to do it without javascript. Other option would be to send it using form with hidden fields with single submit button visible - that would even work without javascript.
Normal hyperlinks in HTML are sent with GET requests and are not supposed to change the state of the resource being accessed. This is known as being idempotent. You can repeat the request over and over, and the result of each succeeding request to the same URL is the same as the first one.
POST requests don't have this restriction and are intended for when the user needs to change something (such as creating a new resource.)
It's not possible to send a POST request via a normal HTML link. And even if you find a way, it breaks an almost universal expectation that web users have. What are you trying to accomplish? Maybe there's a better way.
But to answer your question, you could use something like jQuery to capture the "click" event and make it do a POST request:
$('.my-link').click(function() {
var url = $(this).attr('href');
var data = {};
$.post(url, data, function() {
window.alert('success!');
});
return false;
});
If your URL has any query parameters, i.e. "?foo=bar&baz=bum", then you'd probably need to strip them off of the URL and pass them as a second parameter to the $.post() function. This is left as an exercise for the reader. ;-)

how to redirect a page through controller in zend

When I click on a submit button i want the page to redirect to the following page?
header('Location: /pdp/policy-info.phtml');
I wrote the above code in the controller code but I am not able to redirect to the above page. it stays on the same page.
the filename is called policy-info.phtml in the view.
Also once I redirect, would I be able access my form values through $_POST?
Or is there an alternative.
ok it sounds to me like you may be missing a few concepts:
You will never redirect to a phtml file. (unless you have written some custom rewrite/route rules) Zend uses the MVC architecture, urls exist in this fashion: /module/controller/view/key1/value1/keyx/valuex/
generally zend urls don't terminate with file extensions. Also you will never directly call a view file from your browser.
In your form tag, you specify where the form submits to with the action attribute. For your url i'm assuming the pdp controller and policy-info action
action="/pdp/policy-info/"
If you want to redirect after a form submit from with your controller you would use:
$this->_redirect('/pdp/policy-info/');
# maybe you want to execute some code and then execute
# additional code in another controller without re-bootstrapping
$this->_forward('policy-info', 'pdp');
http://framework.zend.com/manual/en/zend.controller.action.html#zend.controller.action.utilmethods
If you redirect you will not have access to your POST unless you saved those values elsewhere (like in your session). If you forward, I believe the values will still be available in the second action.
actually there maybe a few ways to do what you want to do. I haven't tried this first method yet but it should work.
Render a new veiw script from your controller/action if isPost():
public function myAction(){
$form = My_Form();
$this->view->form = $form;
//if form is posted and submit = Submit
if ($this_request->isPost() && $this_request->getPost()->submit == 'Submit') {
if ($form->isValid($this->_request->getPost()) {
//this is where you want to capture form data
$data = $form->getValues();
//render a new viewscript or _forward to a new action and perform your processing there.
$this->render('path to phtml file');
//if user needs to press a button to accept submit = accept
...do some more stuff...
}
}
}
I think this or some variation will work.Note: I don't think _forward resets the request object, so your $_POST data should not be affected.
Also if this policy-info does not require additional input from the user and is just informational you could easily just _forward('action') to a blank action and the router will display the view script.

Jquery ajax post headers question (I think)

I think this is easy, but I'm not sure what the right syntax is. I am posting a form via JQuery ajax - you know
$j.ajax({
type: "post", etc.....
But I need to make sure the form is capable of being processed if Java is disabled. Is there a "header" in the ajax send that can be used to identify it as aposed to a normal post so that on completion whether ajaxed or normal php post I can return to the sending page. If not ajaxed I can use if($update): header('location: ...'); endif; but if I use that with the ajax request it stops the ajax success function. Hope makes sence
Yes, jQuery sets a custom header when doing an AJAX request:
X-Requested-With : XMLHttpRequest
EDIT russp's server side PHP code:
define('IS_AJAX_REQUEST', isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if( IS_AJAX_REQUEST )
{ //This is an AJAX request, do AJAX specific stuff }
else
{ //This is not an AJAX request }
Don't really know about the headers, but maybe the easier solution is to simply call the url of the processing page with an extra parameter when you're doing it in an ajax context?
The page can then simply check if the parameter is present or not and take appropriate action, depending on it.
You'll have to do a lot or working around, but you can use a combination of <noscript> and javascript code to get what you need.
See WWW FAQs: How do I detect JavaScript in the user's browser?
Hope that helps you get started.
The usual way to handle ajax form requests is to write the form to work without any kind of JS so if you are in a basic php file you would start with
<?php
if(count($_POST) > 0) {
//process form entries
} else {
//output form
}
?>
Then you would add some jQuery to hijack the submit click so
$('input[type=submit]').live("click", function(e) {
e.preventDefault();
//ajax post here
//IE
return false;
});