Forum Moderators: coopster

Message Too Old, No Replies

mysql query inserts duplicate rows because of HTML or JS

mysql_query inserts duplicate rows because of HTML or JS

         

Shabba

2:04 pm on Apr 13, 2007 (gmt 0)

10+ Year Member



After 2 days wrestling with this one and finally fining the answer is the least expected place, I thought I'd try and save others some of my pain. IN the end it was this post that led me to the answer:

[webmasterworld.com...]

I had a common function that was running an insert query against a mysql table with an autoincremented primary key. in 90% of cases the function worked fine, but from one particular page it did not and instead inserted 2 identical rows into the db, despite the fact that PHP only actually ran mysql_query once. I put some microtime stamps in to confirm that the queries were different, and indeed they were.

I found references to background colours and image sources causing the same problem, but my page didn't have either. I found that removing an IFRAME from the source of the page fixed the problem. In fact my db insert function was called as a result of an AJAX request, and I managed to fix it by removing some js that dynamically wrote to the contents of the iframe.

Of course the implication is that there is a pretty serious bug somewhere in the LAMP stack, if client side code can affect the way that queries are executed. It doesn't take much imagination to think of the vulnerabilities this could open up.

Utterly weird. Relieved to be able to think about something else, but if you ever have ghost inserts check all your HTML line by line!

Shabba

cameraman

11:26 pm on Apr 13, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Welcome to WebmasterWorld, Shabba.

I don't believe this is a 'bug' in any of the LAMP components, it's just the way the browser works. The browser does a request for the page, then tries to do another request for the unspecified images.

I set up the two files below. The first one counts the hits in a session variable, and the second serves an AJAX request to either report or clear the counter. FF2 and SeaMonkey do two requests per page load. IE6 does 1 request for the page load, then apparently tries a request for the default document - if the first file is named for the default document it gets 2 hits, if it's named something else it gets 1 hit. It doesn't look like any of the three browsers did requests for the unspecified iframe.

I verified that the second request is for the image by adding some code at the end of the script in the first file; I check for HTTP_REFERER and if it's not empty I used gd functions to return a gif and exit instead of falling into the 'static' document. In all three browsers, the img elements got filled with the gif I served. Interestingly, all elements were filled with the same image; further requests were not made - I added logic to serve a different image if the counter was above 2, but it didn't happen and the counter didn't advance.

I also tried changing the successive requests to send back different text. In all three browsers the image elements stayed blank and the iframe didn't receive anything. If I give sources to all of the images and leave the iframe blank as it is, each browser only made one request for the document.

I don't see this as a bug; the server is doing what it's told to do. If a page load or an iframe's source causes script to execute, then it executes. The simple solution is to only process form data when the form has in fact been submitted, by checking an active control like a submit button:
if(isset($_GET['submit'])) {

I didn't get 'thwapped' in any of the scenarios I ran.

Here's the test page:

<?php
session_start();
if(!isset($_SESSION['hit']))
$_SESSION['hit'] = 1;
else
$_SESSION['hit']++;
session_write_close();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Count Hits This Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>

<body bgcolor="#FFFFFF" text="#000000">
<img src="/images/Logo.gif" border="1" alt="1" />
<img src="" border="1" alt="2" />
<img src="" border="1" alt="3" />
<iframe src="" width="80" height="30"></iframe>
<div>hits:
<div id="hits"></div>
</div>
<button type="button" name="get" id="get" onclick="gethit('s');">Get</button>
<button type="button" name="reset" id="reset" onclick="gethit('r');">Reset</button>
<form method="get" action="<?php echo $_SERVER['PHP_SELF'];?>">
<input type="text" name="text" value="something" />
<input type="submit" name="Submit" value="Submit" />
</form>
<?php if(isset($_GET['Submit'])) echo "<div>THWAP</div>\n";?>
<script type="text/javascript">
var xmlhttp;

function gethit(arg) {
document.getElementById('hits').innerHTML='Retrieving';
var url = 'gethit.php';
if(arg == 'r')
url = url + '?r=1';
if(xmlhttp == null) {
if (window.XMLHttpRequest)
xmlhttp=new XMLHttpRequest();
else if (window.ActiveXObject)
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}// EndIf xmlhttp object not created

if (xmlhttp!=null) {
xmlhttp.onreadystatechange=ghstate;
xmlhttp.open("GET",url,true);
xmlhttp.send(null)
}// EndIf xmlhttp object created
}// End gethit()
function ghstate() {
// if xmlhttp shows "loaded"
if (xmlhttp.readyState==4) {
// if "OK"
try {
if (xmlhttp.status==200) {
document.getElementById('hits').innerHTML=xmlhttp.responseText;
}// EndIf status = 200
else {
document.getElementById('hits').innerHTML="Problem retrieving XML data, status code is "+xmlhttp.status;
}// EndElse status <> 200
}// Endtry
catch(e) {
document.getElementById('hits').innerHTML='Error performing request: ' + e.name
}// Endcatch
if(window.ActiveXObject)
xmlhttp=null;
}// EndIf readystate 4
}// End ghstate()
</script>
</body>
</html>

Here's gethit.php:

<?php
session_start();
if(isset($_GET['r']))
unset($_SESSION['hit']);
if(isset($_SESSION['hit']))
echo $_SESSION['hit'];
else
echo "0";
session_write_close();
?>