Click to See Complete Forum and Search --> : Javascript submits form twice


Debbie-Leigh
February 12th, 2008, 05:44 PM
Hi,

I've written some javascript to stop right-clicking on the page that is displayed by my software as it is re-directing to a browser to a payment processor. However it seems to be having a weird side-effect. It took me days to narrow it down to this script, but it seems to be causing my order page form to be submitted twice when the Submit button is clicked.

I have been scratching my head over this for a very long while, but I still can't see how this code could possibly cause a form submission, but through a lot of trial and error testing, I've found that when this code is present, the form is submitted a second time, but when it's absent, the form is submitted only once.

The process that happens is that my order page is submitted to a PHP script, which adds the order to my database and sends the dopay.php page back to the browser. This page then forwards the browser to the payment processor's page. Sometimes, it may take a few seconds to get the processor's page, so I added the js script to stop opportunists from looking at the sensitive information in the form that is submitted on the page. Obviously, the determined can always use the others ways, but as the page is only displayed for a few seconds, if that, it's very rarely there long enough to be able to use the other methods.

However, when this script is present, I get two orders being added to my database and my test display messages show that it's because my order page php script has been triggered twice.

These are the pages involved:

The dopay.php page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<SCRIPT><!--
//***************************************************************************
//* This work is the copyright and intellectual property of Deborah Figg. *
//* Reproduction by any means is strictly prohibited unless prior written *
//* permission is obtained from the copyright holder. *
//***************************************************************************
function funGetEvent(evt) {
// Return the appropriate event object

if (evt == null) {
return event;
} else {
return evt;
}
}
function funKeyPress(evt) {
var objEvt = funGetEvent(evt);

if (objEvt.which) {
var keyChar = String.fromCharCode(objEvt.which);
} else {
var keyChar = String.fromCharCode(objEvt.keyCode);
}

if (keyChar == 'U' || keyChar == 'u' || keyChar == 'R' || keyChar == 'r') {
alert('Page not available');
return false;
}
}
function funMouseDown(evt) {
if (navigator.userAgent.indexOf('Firefox') == -1) {
var objEvt = funGetEvent(evt);

if (objEvt.which) {
var button = objEvt.which;
} else {
var button = objEvt.button;
}

if (button > 1) {
alert('Page not available');
return false;
}
}
}
function funMouseClick(evt) {
var objEvt = funGetEvent(evt);

if (objEvt.which) {
var button = objEvt.which;
} else {
var button = objEvt.button;
}

if (button > 1) {
alert('Page not available');
return false;
}
}
if (document.captureEvents) {
if (Event.KEYPRESS) {
document.captureEvents(Event.KEYPRESS|Event.MOUSEDOWN|Event.CLICK);
} else {
document.captureEvents(1024|1|64);
}
}
document.onkeypress = funKeyPress;
document.onmousedown = funMouseDown;
document.onclick = funMouseClick;

--></SCRIPT>
</head>
<body onLoad="javascript:document.frmPay.submit()">
<form action="https://www.alertpay.com/payprocess.aspx" method="post" name="frmPay" id="frmPay">
<input name="ap_merchant" type="hidden" value="myid" />
<input name="ap_returnurl" type="hidden" value="myreturnpage" />
<input name="ap_cancelurl" type="hidden" value="mycancelpage" />
<input name="ap_description" type="hidden" value="My description." />
<input name="apc_1" type="hidden" value="myorderid" />
<input name="ap_currency" type="hidden" value="mycurrency" />
<input name="ap_purchasetype" type="hidden" value="item" />
<input name="ap_quantity" type="hidden" value="1" />
<input name="ap_itemname" type="hidden" value="My Product" />
<input name="ap_amount" type="hidden" value="37.00" />
<input name="ap_totalamount" type="hidden" value="37.00" />
</form>
</body>
</html>

As you can see, the form on this page goes to Alertpay, so how on earth can my own order page be submitted twice?

The order page form:

<form action="order.php" method="post" name="frmOrder" id="frmOrder">
<table width="80%" align="center" cellspacing="0" cellpadding="0">
<tr><td>
<fieldset class="ordfrm-fieldset"><legend class="ordfrm-legend">Enter Your Details</legend>
<table width="97%" align="center" cellspacing="3" cellpadding="3" class="ordfrm-text-norm">
<tr class="ordfrm-tbl-row ordfrm-tbl-row-even">
<td width="40%" class="ordfrm-tbl-row-hdg"><span class="ordfrm-frm-rqd">* </span>First Name:</td>
<td width="60%">
<input name="FirstName" id="FirstName" type="text" size="30" maxlength="50" title="Max length: 50" value="" tabindex="1" class="ordfrm-input" />
</td>
</tr>
<tr class="ordfrm-tbl-row ordfrm-tbl-row-odd">

<td class="ordfrm-tbl-row-hdg"><span class="ordfrm-frm-rqd">* </span>Last Name:</td>
<td>
<input name="LastName" id="LastName" type="text" size="30" maxlength="50" title="Max length: 50" value="" tabindex="1" class="ordfrm-input" />
</td>
</tr>
<tr class="ordfrm-tbl-row ordfrm-tbl-row-even">
<td class="ordfrm-tbl-row-hdg"><span class="ordfrm-frm-rqd">* </span>Email Address:</td>

<td>
<input name="EmailAddress" id="EmailAddress" type="text" size="30" maxlength="100" title="Max length: 100" value="" tabindex="1" class="ordfrm-input" />
</td>
</tr>
</table>
<table width="97%" align="center" cellspacing="3" cellpadding="3" class="ordfrm-text-norm">
<tr><td><img src="images/spacer.gif" width="1" height="1" /></td></tr>
<tr class="ordfrm-tbl-row ordfrm-tbl-row-odd">
<td align="center">

<input name="Submit" id="Submit" type="image" src="images/alertpay-sm-1.gif" title="Click here to order securely using **AlertPay**. It may take several moments, so please be patient." tabindex="1" valign="absmiddle" class="image-button" onclick="return funDisable(this, '', true, document.frmOrder)" />
</td>
</tr>
<tr><td align="center" class="ordfrm-text-small ordfrm-tbl-row-odd">
<img src="images/spacer.gif" width="1" height="5" /><br />
(It may take several moments to take you to our secure payment page, so please be patient. We always protect your privacy and never share your email address with anybody. Also, to ensure you receive our emails, we don't allow free email addresses e.g. yahoo, hotmail, msn.)
</td></tr></table>
</fieldset>
</td></tr></table>
</form>

funDisable disables the image button and submits the form. I've thoroughly tested this script with Firebug and it only ever does one submit.

The order page php script is far too big and complex to include here, but all it does with the dopay.php page is to echo it back to the browser. But anyway, I've eliminated it from the equation, as my testing has all pointed to the presence of the js script on the dopay.php page.

It's an obscure one, but does anyone have any suggestions as to how the js script could be causing the order page form to be submitted a second time? Or does anyone know of a better script to disable code viewing that I could possibly use?

Debbie

PeejAvery
February 13th, 2008, 07:00 AM
There are only a few instances when a form can be submitted more than once. In your code's case, there is only 2 possible solutions.

1. The form is submitted, and before the page has time to switch to the processing page, the form was submitted again. This would be a millesecond window depending on connection and computer speed.

2. The server code is somehow prompted by the effects of the client-side to process the form twice.

Both of these seem ridiculous in your case.


Or does anyone know of a better script to disable code viewing that I could possibly use?
It is impossible to hide client-side code completely. Any browser has menu options that view the source code. Also, with JavaScript disabled, your page is even more vulnerable.

I do not see the point in your dopay page. Why are you sending a form full of hidden inputs back to the client just to be sent back to the server? That is a huge security risk by itself. Why cannot the first form process the whole thing for you?

If you want to disable the right-click, I would suggest viewing this example (http://www.peejavery.com/coding/javascript/rightclick.php) from my website.

Debbie-Leigh
February 13th, 2008, 07:47 PM
Hi PeejAvery,

Thank you for your example. I'll give it a whirl and cross my fingers that it works with my set up. :)

I do not see the point in your dopay page ... Why cannot the first form process the whole thing for you?

That's the first method that I considered, but that wouldn't work here, as I need to send the customer to the processor's site, so they can type in their credit card details, go through several other pages, before being sent back to my thank you page, which has to be a different script to the order page one.

Hence, you can't call the processor's page in the background, as the customer has to interact with the processor's page - you have to use a redirect page to get the customer's browser to go to the processor's page.

Re. your comment:

It is impossible to hide client-side code completely. Any browser has menu options that view the source code.

Yes, I think everyone knows that, but to quote my post:

... so I added the js script to stop opportunists from looking at the sensitive information in the form that is submitted on the page. Obviously, the determined can always use the others ways, but as the page is only displayed for a few seconds, if that, it's very rarely there long enough to be able to use the other methods.


Debbie

Debbie-Leigh
February 16th, 2008, 06:36 PM
Hi,

I've now found the solution, which is in this bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=236858, so I thought I'd share what I did with everyone.

According to the bug report, the absence of a "content-type text/html; charset=iso-8859-1" header or <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> meta tag in the page causes the browser to use it's default charset, which it may decide is wrong once it gets to process the actual data on the page. This is what causes the browser to re-request the page to be able to process it using the correct charset, hence submitting the form twice. Not the most desirable result, I grant you, but that how it's been designed to work, currently.

So, I tried the suggested fix and found that putting both or just the meta tag did solve the problem, but not consistently enough, because it still happened every so often.

So, I put in a bit of code to not use the javascript when the browser is Firefox, as that is the only browser in which the problem occurs.

<?php
if (USER_BROWSER_AGENT <> "FIREFOX"):
?>
<script language="JavaScript1.2" type="text/javascript"><!--
<?php
require(JS_STOP_THIEF);
?>
--></script>
<?php
endif;
?>

USER_BROWSER_AGENT is determined by this code, which I thought might be useful for anyone who doesn't already have something similar:

if (!empty($_SERVER["HTTP_USER_AGENT"])):
define("USER_AGENT", $_SERVER["HTTP_USER_AGENT"]);
elseif (!empty($HTTP_SERVER_VARS["HTTP_USER_AGENT"])):
define("USER_AGENT", $HTTP_SERVER_VARS["HTTP_USER_AGENT"]);
else:
define("USER_AGENT", "");
endif;

if (strstr(USER_AGENT, "Win")):
define("USER_OS", "Win");
elseif (strstr(USER_AGENT, "Mac")):
define("USER_OS", "Mac");
elseif (strstr(USER_AGENT, "Linux")):
define("USER_OS", "Linux");
elseif (strstr(USER_AGENT, "Unix")):
define("USER_OS", "Unix");
elseif (strstr(USER_AGENT, "OS/2")):
define("USER_OS", "OS/2");
else:
define("USER_OS", "Other");
endif;

if (ereg("Opera(/| )([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)):
define("USER_BROWSER_VER", $aMatches[2]);
define("USER_BROWSER_AGENT", "OPERA");
elseif (ereg("MSIE ([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)):
define("USER_BROWSER_VER", $aMatches[1]);
define("USER_BROWSER_AGENT", "IE");
elseif (ereg("OmniWeb/([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)):
define("USER_BROWSER_VER", $aMatches[1]);
define("USER_BROWSER_AGENT", "OMNIWEB");
elseif (ereg("(Konqueror/)(.*)(;)", USER_AGENT, $aMatches)):
define("USER_BROWSER_VER", $aMatches[2]);
define("USER_BROWSER_AGENT", "KONQUEROR");
elseif (ereg("Mozilla/([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)
&& ereg("Firefox/([0-9]*)", USER_AGENT, $aMatches2)
):
define("USER_BROWSER_VER", $aMatches[1] . "." . $aMatches2[1]);
define("USER_BROWSER_AGENT", "FIREFOX");
elseif (ereg("Mozilla/([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)
&& ereg("Safari/([0-9]*)", USER_AGENT, $aMatches2)
):
define("USER_BROWSER_VER", $aMatches[1] . "." . $aMatches2[1]);
define("USER_BROWSER_AGENT", "SAFARI");
elseif (ereg("Mozilla/([0-9].[0-9]{1,2})", USER_AGENT, $aMatches)):
define("USER_BROWSER_VER", $aMatches[1]);
define("USER_BROWSER_AGENT", "MOZILLA");
else:
define("USER_BROWSER_VER", 0);
define("USER_BROWSER_AGENT", "OTHER");
endif;

Hope that helps.

Debbie

PeejAvery
February 17th, 2008, 03:14 PM
Glad you solved it! I too have written a similar browser detection tool (http://www.peejavery.com/coding/php/browser.php). However, I don't understand why you want to bulk up your code. You can just use the following for efficiency and save on code.

if (!eregi('Firefox', $_SERVER['HTTP_USER_AGENT'])) {

Debbie-Leigh
February 17th, 2008, 07:20 PM
Hi PeejAvery,

I've put this code into a separate constants file that I only include on pages that have to use them. The places that need to use them, usually need to allow for more than just Firefox and I have some process flows that use them in more than one place in the same flow. Hence I regarded it as a bit more efficient to put the definition in one place at the beginning of the flow, whenever they needed to be used.

Also, I prefer them to be constants, as it makes the code simpler than having to globalise them in or pass them to the functions that need to use them.

Debbie