I m working on an html page that contains a form allowing users to enter their informations and upload files. all informations will be inserted in Mysql database.
in Javascript, im using XMLHttpRequest to send the files to the server and "upload.php" to rename (to avoid dupplicated names) and move them in the upload directory.
For better user experience, this will be done before submitting the whole form.
My question is : How can i store the new filenames (defined in upload.php)to use them in the form submission "submit.php"?
the reason for this is that in "submit.php", i insert first the user informations in "user" table and then select the "user_id" (auto increment) that will be inserted with filenames in the "files" table.
Could php sessions be an approach to do this ? is there another way? Thanks for your help
html:
<form action="submit.php" method="post" id="submitform">
<div>
<--!user info part1-->
</div>
<div id="filesContainer" class="eltContainer">
<input type="file" id="filesList" multiple>
</div>
<div>
<--!user info part2-->
</div>
javascript:
var fd = new FormData()
var xhr = new XMLHttpRequest()
for (var i=0,nb = fichiers.length; i<nb; i++) {
var fichier = fichiers[i]
fd.append(fichier.name,fichier)
}
xhr.open('POST', 'upload.php', true)
upload.php :
<?php
foreach($_FILES as $file){
$filename = date('Y') . date('m') . date('d') . date('H') . date('i') . basename($_FILES[$file]['name']);
move_uploaded_file( $file['tmp_name'],"../upload_dir/" .$filename);
}
exit;
I would consider using something like md5 for unique filenames.
Nevertheless you can push filenames into some array, and than return those filenames, as a result of post request, and put them back into some input field.
To retrieve the response simply add this lines to your code below open
xhr.onreadystatechange = function {
// If the request completed and status is OK
if (req.readyState == 4 && req.status == 200) {
// keep in mind that fileNames here are JSON string
// as you should call json_encode($arrayOfFilenames)
// in your php script (upload.php)
var fileNames = xhr.responseText;
}
}
If you'd like consider using a simple library for AJAX requests, like axios. It's promise based HTTP client for the browser, really simple to use and saves you some time and effort cause you don't have to memorize all this stuff you and I have just written.
This is one approach, but I think you can use $_SESSION as well, and it's perfectly valid. My guess is you don't have logged in user at this point, so my idea is as follows:
put filenames into the $_SESSION
use db transactions - as #Marc B suggested - to connect files with
user
if there were no errors just remove filenames from $_SESSION, if there was some, just redirect the user back to the form (possibly with some info what went wrong), and this way he doesn't have to reupload files, cause you have filenames still in $_SESSION
Related
I am working on a form set for a client. In a nutshell:
The forms are filled out by my client’s customers by selecting different options on each form.
Each form can have multiple instances, depending on the customer.
At the end of the process, the customer can opt to either sign one or all the forms digitally or decline to sign them digitally and at the end of the process the forms are printed out and signed manually.
To accomplish this, I’ve created a signature plugin written in jQuery. Once the customer fills out the forms, they are presented each form separately. To sign the form digitally they simply tap (click) the signature block, a dialog with a canvas element appears, they sign the form and save it, the signature appears in the form, and they move on to the next form.
Here is the portion of the code that stores the completed signature and adds the image to the form:
$.sig = {
signatures: {},
}
function signatureSave() {
var canvas = document.getElementById("sigcanvas"),
dataURL = canvas.toDataURL("image/png");
document.getElementById($.sig.target).src = dataURL;
$.sig.signatures[$.sig.target].url = dataURL;
$.sig.signatures[$.sig.target].hasSignature = true;
};
The function is only called if the signature is saved, if there is no signature, the $.sig.signatures[$.sig.target].hasSignature remains false and the system skips the object.
This all works as intended, almost.
My problem lies in the process used to save the form information. If the customer does not sign any forms digitally the form information is simply saved and the forms are printed out, no need to save any signatures.
If the customer signs at least one form, though, the signatures must be sent to the server using the FormData() object.
I’ve used the FormData object in other projects for the client successfully, but only when the customer uploads one or more images to the browser using the input file element. It’s a pretty simple process because the resulting images have a img.file property that I send to the server.
Not so with a canvas object. All I get is the .src property, an any attempt to use anything from the resulting .png image that is created in the function above results in either a “cannot use a blob” or some other error.
Now I know if I have a single image, I can send it using AJAX with the following:
$.ajax({
type: "POST",
url: "script.php",
data: {
imgBase64: dataURL
}
})
Problem is that I am sending from one to x number of signatures.
Edit: I forgot to add this in. This is the function that is supposed to create the FormData object used to send the signatures to the server (and where my problem lies):
function getUploadData() {
var upl = new FormData();
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
var im = new Image();
im.src = u.url;
upl.append(u.target, im, u.target + '.png');
}
})
return upl;
}
I've tried all the tricks and nothing is working. The var im = new Image(); as well as the following line are just my latest ill fated attempt.
Picture perfect would be the ability to save the image information in the $.sig.signatures object so I can simply loop through any signatures that are signed, add them as elements of the FormData object, and then send the FormData object as the data for the AJAX call. As stated before, I use this method in other projects and works fine.
Does anyone know a way to do this?
Please note:
The server-side AJAX processor functions correctly.
The signature process works correctly (customer signs canvas, signature is displayed, signature information is stored).
All I need is how to send multiple images created using the canvas element in a FormData object to the server.
I know the answer is staring me right in the face, but I am just not getting it. Any hints or suggestions would be greatly appreciated!
Edit: Just a note. I've searched the entire afternoon for this and have found entries that either deal with sending multiple files using FormData and AJAX - but the files are uploaded to the browser (not created using Canvas), or single files sent to the server that are created using Canvas, but nothing about sending multiple files sent using FormData and AJAX that are created using Canvas. Oje!
As stated, the answer was staring me in the face, but I didn't see it because was looking behind the wrong door. FormData has nothing to do with it (Homer Dope Slap!).
Since I already have the data stored in $.sig.signature for each signature, I just need to send the information to the server as the data in the AJAX function. I updated my function above as shown:
function getUploadData() {
var upl = {};
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
upl[e] = u.url;
}
})
return upl;
}
Since the form information is sent as JSON I just add the signature info to the object that contains the form information, JSON.stringify it and send it on its way. This should work because the information retrieved above are strings.
Server side will look something like this:
$info = json_decode( $_POST['info'] );
// Various validation routines and checks
foreach( $info->signatures as $sig=>$data ):
$data = str_replace('data:image/png;base64,', '', $data);
$data = str_replace(' ', '+', $data);
$img = base64_decode($data);
// Do some processing, file naming, database saving and other general dodads
$success = file_put_contents( $file, $img );
endforeach;
The above function is still concept, I am reworking some of the code but this should work.
Credit is given to this post for opening my eyes:
post sending base64 image with ajaxpost sending base64 image with ajax
So question answered and yeah, I deserve a dope slap, but all comes out right in the end.
CAVEAT: Works like a charm.
I am trying to read the post request parameters from my HTML. I can read the get request parameters using the following code in JavaScript.
$wnd.location.search
But it does not work for post request. Can anyone tell me how to read the post request parameter values in my HTML using JavaScript?
POST data is data that is handled server side. And Javascript is on client side. So there is no way you can read a post data using JavaScript.
A little piece of PHP to get the server to populate a JavaScript variable is quick and easy:
var my_javascript_variable = <?php echo json_encode($_POST['my_post'] ?? null) ?>;
Then just access the JavaScript variable in the normal way.
Note there is no guarantee any given data or kind of data will be posted unless you check - all input fields are suggestions, not guarantees.
JavaScript is a client-side scripting language, which means all of the code is executed on the web user's machine. The POST variables, on the other hand, go to the server and reside there. Browsers do not provide those variables to the JavaScript environment, nor should any developer expect them to magically be there.
Since the browser disallows JavaScript from accessing POST data, it's pretty much impossible to read the POST variables without an outside actor like PHP echoing the POST values into a script variable or an extension/addon that captures the POST values in transit. The GET variables are available via a workaround because they're in the URL which can be parsed by the client machine.
Use sessionStorage!
$(function(){
$('form').submit{
document.sessionStorage["form-data"] = $('this').serialize();
document.location.href = 'another-page.html';
}
});
At another-page.html:
var formData = document.sessionStorage["form-data"];
Reference link - https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
Why not use localStorage or any other way to set the value that you
would like to pass?
That way you have access to it from anywhere!
By anywhere I mean within the given domain/context
If you're working with a Java / REST API, a workaround is easy. In the JSP page you can do the following:
<%
String action = request.getParameter("action");
String postData = request.getParameter("dataInput");
%>
<script>
var doAction = "<% out.print(action); %>";
var postData = "<% out.print(postData); %>";
window.alert(doAction + " " + postData);
</script>
You can read the post request parameter with jQuery-PostCapture(#ssut/jQuery-PostCapture).
PostCapture plugin is consisted of some tricks.
When you are click the submit button, the onsubmit event will be dispatched.
At the time, PostCapture will be serialize form data and save to html5 localStorage(if available) or cookie storage.
I have a simple code to make it:
In your index.php :
<input id="first_post_data" type="hidden" value="<?= $_POST['first_param']; ?>"/>
In your main.js :
let my_first_post_param = $("#first_post_data").val();
So when you will include main.js in index.php (<script type="text/javascript" src="./main.js"></script>) you could get the value of your hidden input which contains your post data.
POST is what browser sends from client(your broswer) to the web server. Post data is send to server via http headers, and it is available only at the server end or in between the path (example: a proxy server) from client (your browser) to web-server. So it cannot be handled from client side scripts like JavaScript. You need to handle it via server side scripts like CGI, PHP, Java etc. If you still need to write in JavaScript you need to have a web-server which understands and executes JavaScript in your server like Node.js
<script>
<?php
if($_POST) { // Check to make sure params have been sent via POST
foreach($_POST as $field => $value) { // Go through each POST param and output as JavaScript variable
$val = json_encode($value); // Escape value
$vars .= "var $field = $val;\n";
}
echo "<script>\n$vars</script>\n";
}
?>
</script>
Or use it to put them in an dictionary that a function could retrieve:
<script>
<?php
if($_POST) {
$vars = array();
foreach($_POST as $field => $value) {
array_push($vars,"$field:".json_encode($value)); // Push to $vars array so we can just implode() it, escape value
}
echo "<script>var post = {".implode(", ",$vars)."}</script>\n"; // Implode array, javascript will interpret as dictionary
}
?>
</script>
Then in JavaScript:
var myText = post['text'];
// Or use a function instead if you want to do stuff to it first
function Post(variable) {
// do stuff to variable before returning...
var thisVar = post[variable];
return thisVar;
}
This is just an example and shouldn't be used for any sensitive data like a password, etc. The POST method exists for a reason; to send data securely to the backend, so that would defeat the purpose.
But if you just need a bunch of non-sensitive form data to go to your next page without /page?blah=value&bleh=value&blahbleh=value in your url, this would make for a cleaner url and your JavaScript can immediately interact with your POST data.
You can 'json_encode' to first encode your post variables via PHP.
Then create a JS object (array) from the JSON encoded post variables.
Then use a JavaScript loop to manipulate those variables... Like - in this example below - to populate an HTML form form:
<script>
<?php $post_vars_json_encode = json_encode($this->input->post()); ?>
// SET POST VALUES OBJECT/ARRAY
var post_value_Arr = <?php echo $post_vars_json_encode; ?>;// creates a JS object with your post variables
console.log(post_value_Arr);
// POPULATE FIELDS BASED ON POST VALUES
for(var key in post_value_Arr){// Loop post variables array
if(document.getElementById(key)){// Field Exists
console.log("found post_value_Arr key form field = "+key);
document.getElementById(key).value = post_value_Arr[key];
}
}
</script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var formObj = document.getElementById("pageID");
formObj.response_order_id.value = getParameterByName("name");
One option is to set a cookie in PHP.
For example: a cookie named invalid with the value of $invalid expiring in 1 day:
setcookie('invalid', $invalid, time() + 60 * 60 * 24);
Then read it back out in JS (using the JS Cookie plugin):
var invalid = Cookies.get('invalid');
if(invalid !== undefined) {
Cookies.remove('invalid');
}
You can now access the value from the invalid variable in JavaScript.
It depends of what you define as JavaScript. Nowdays we actually have JS at server side programs such as NodeJS. It is exacly the same JavaScript that you code in your browser, exept as a server language.
So you can do something like this: (Code by Casey Chu: https://stackoverflow.com/a/4310087/5698805)
var qs = require('querystring');
function (request, response) {
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
// Too much POST data, kill the connection!
// 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
if (body.length > 1e6)
request.connection.destroy();
});
request.on('end', function () {
var post = qs.parse(body);
// use post['blah'], etc.
});
}
}
And therefrom use post['key'] = newVal; etc...
POST variables are only available to the browser if that same browser sent them in the first place. If another website form submits via POST to another URL, the browser will not see the POST data come in.
SITE A: has a form submit to an external URL (site B) using POST
SITE B: will receive the visitor but with only GET variables
$(function(){
$('form').sumbit{
$('this').serialize();
}
});
In jQuery, the above code would give you the URL string with POST parameters in the URL.
It's not impossible to extract the POST parameters.
To use jQuery, you need to include the jQuery library. Use the following for that:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" type="text/javascript"></script>
We can collect the form params submitted using POST with using serialize concept.
Try this:
$('form').serialize();
Just enclose it alert, it displays all the parameters including hidden.
<head><script>var xxx = ${params.xxx}</script></head>
Using EL expression ${param.xxx} in <head> to get params from a post method, and make sure the js file is included after <head> so that you can handle a param like 'xxx' directly in your js file.
I am using JavaScript to take the info from a form completed by the user, then sending this data to PHP and generate a PDF with FPDF. The problem is I want the browser to ask the user to save the PDF or view it online but
I cannot figure out how. The PDF generates correctly but only when I save it directly to a certain path specified by me.
The question is how do you guys send data from JavaScript to PHP to generate a PDF file then the browser asks the user to open or download, Or how can I make a function where the user can retrieve this PDF.
The JavaScript:
function senddata() {//this activates when i push a button not a submit
var peticion = new XMLHttpRequest();
peticion.open('POST', 'generatepdf.php');
var nueva2 = {};
var key;
for (i = 0; i < 6; i++) {
key = document.forms[0].elements[i].id;
nueva2[key] = document.forms[0].elements[i].value;
}//here i take my data from the form and make an object
var json = JSON.stringify(nueva2);//here i tranform my object to json string so i can send it to my php
var parametros = "json_string=" + json;//here I do this so later I can easily transform the $_POST to an array in the form json_decode($_POST["json_string"],true);
peticion.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
peticion.send(parametros);//it sends ok
}
The PHP with the FPDF class and things
<?php
require('fpdf/fpdf.php');
require('functions.php');
if($_SERVER['REQUEST_METHOD']=='POST'){
$datos=json_decode($_POST["json_string"],true); //here i have my data in an array format so i can use the parameters to fill my pdf fields
//...lots of pdf things here...//
$pdf->Output('F',"pdfs/Cotizacion ".$datos["nombres"]." ".$datos["apellidos"].".pdf");//this works but never ask the user
//$pdf->Output('D',"pdfs/Cotizacion ".$datos["nombres"]." ".$datos["apellidos"].".pdf");//this should force a dowload or send to the browser but doesnt work
//$pdf->Output();//this should send to the browser but doesnt work
}
To view your PDF inline to the browser, you should use the I variable instead. View full documentation here.
Also I don't think outputting the file in two methods at the same time works. It might conflict each other. The best way to do that is to test each method and if it does conflict each other just simply add a condition for the user to decide whether he/she wants to download or view it in the browser. Hope this helps.
I have a HTML webpage with a form to upload a file. When you click submit I want to send the form to a php file where it will store it in my database.
Currently I use the reqular xmlHTTPRequest to send a GET request to my php files. This all works perfectly so I would prefer not to restructure my program.
Could someone give me some guidance as to how to do that? I tried to send the file as I sent other inputs (Text etc) but got a null value in the php.
Code:
The HTML is a simple input tag (this won't show it when I copy it in).
JavaScript:
var picture = document.getElementById("file").value;
generateAjaxRequest("type=insert&picutre="+picture")
AJAX:
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange= function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById(elementName).innerHTML = xmlhttp.responseText;
}
}
xmlhttp.open("GET", "/Controller.php);
xmlhttp.send();
PHP:
$file = #$_GET["file"];
$query = mysqli_prepare($conn, "INSERT INTO table (image) VALUES (?)");
mysqli_stmt_bind_param($query, 's',$file);
stmtGetInfo($query);
When a file is uploaded a user will click submit which will call the Javascript. The javascript sends the file to the ajax which then sends it to the php file. I have only put up some of the code to demonstrate what is happening. All of the methods work for text entry etc.
If you are trying to upload files using xmlHTTPRequest, these links can be usefull.
https://gist.github.com/ebidel/2410898
https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
I have two subsequent forms on my website with POST method.
The first page of my website first.php contains this code:
<form action="a.php" method="POST" target="_blank">
<input name="value" type="hidden" value="foo"/>
<div class="button"><label><span class="icon"></span>
<input type="submit" class="button-graphic ajax" value="Click Here"></label></div></form>
a.php can be accessed only via this POST request (otherwise user will get method not allowed 405 error)
Once submitted, this form opens a.php with an AJAX modal window.
a.php contains another form:
<form action="b.php" method="POST" target="_blank">
<input name="bar" type="hidden" value="none"/>
<div class="border"><label><input type="submit" class="button-graphic2 tracking" value="Continue"></label></div></form>
When a user clicks Submit in the second form, it will open b.php,
which can also be accessed only via POST request (otherwise - 405 error).
The only difference I can think about between these forms is that the second one contains a tracking js class (opening an iframe). this is the js code:
$(document).ready(function() {
$(".tracking").click(function(){
var iframe = document.createElement('iframe');
iframe.style.width = '0px';
iframe.style.height = '0px';
iframe.style.display = 'block';
document.body.appendChild(iframe);
iframe.src = '/track.htm';
});
This is done in order to track a conversion using a third party script which is being execuated from track.htm
I noticed that I am having a problem with about 5% of my iPad visitors.
they open a.php properly with a POST request, but when they go ahead to continue and open b.php as well, about 5% sends out a GET request instead of the desired POST request, causing them to get an 405 error and leave the website.
I know that these are real human users as I can see some of them trying several times to open b.php and keep getting these 405 errors.
Could this be caused because simultaneously their device is using a GET request to obtain track.htm? and this is some glitch?
How can this be solved?
EDIT 4.4.2015:
Since there's a chance that firing the tracking script is causing this, I would like to know if there's another fire to fire it (or track that adwords conversion), without causing these iPad user to use "GET" requests for the form as well.
EDIT 10.4.2015:
This is the jquery code of the ajax class, that effects both first.php and perhaps a.php, as first.php is the parent frame:
$(document).ready(function() {
$(".ajax").click(function(t) {
t.preventDefault();
var e = $(this).closest("form");
return $.colorbox({
href: e.attr("action"),
transition: "elastic",
overlayClose: !1,
maxWidth: $("html").hasClass("ie7") ? "45%" : "false",
opacity: .7,
data: {
value: e.find('input[name="value"]').val(),
}
}), !1
})
}),
Technically, it shouldn't happen. The iframe created by your tracking script pointed to /track.htm, so there shouldn't be any GET request to your b.php page.
On the other hand, just thinking out loud here, there're a few scenario that could happen because of "real world" user.
The users happen to have bookmark the b.php page, thus causing them to open it using GET when they try to re-open the page using their bookmark.
The users tried to refresh the page b.php, then get warned about "Form re-submission". Being clueless as most real user are, they canceled the form re-submission, then click on the address bar and click GO on their browser with the sole intention of reloading the page. This could also cause the GET request to send to the b.php page.
Considering the best practice when designing the page flow for form submission, it might be better for you to only "process" your form data in b.php and then return a 302 Redirect to another page that show the result using a GET request. This will allow users to "refresh" the page without double submitting the form, and also allow user to bookmark the result page too.
This doesn't answer your question but as it entails to the GET glitch but as things stand, ~5% of your iPad visitors can't sign up because the code only accepts POST and so far no one can figure this out. So I propose a change of strategy, at least in the mean time.
Preventing CSRF by only accepting POST requests is already known to not work. Your choice of accepting only this request method as a means of security is what ultimately results in the 405. There are better ways.
One example of is using a CSRF token, specifically the Synchronizer Token Pattern.
The idea behind a CSRF token is that when you generate the form, you also generate a "key" which you tie to the form. When that form is submitted, if it doesn't have the key or the key isn't the right one, you don't bother processing the form. The Syncronizer Token Pattern gets fancy in that it changes the expect key each time (in the form field implementation, giving the <input type="hidden"> field a new name attribute each time) in addition to the value.
Have your code in a.php generate a random token and
store it as a session variable on the server. Output the token in the form as a hidden field.
Before processing the request in b.php, ensure the token value is in the request data and ensure it has the expected value.
You can first check for $_POST data and if it is missing, check for $_GET data. Regardless of which array contains the data, if the data does not have a valid CSRF token, respond with a 4xx error.
If the token is good, consume the token and process the request.
If the token is missing or is invalid, return a 4xx response code.
Another way would be to set your field names to random values each time the form is generated. So instead of <input name="value" type="hidden" value="foo"/> or <input name="bar" type="hidden" value="none"/>.
// ... in an importable file somewhere ...
// Generate our tokens
function token($len = 13) {
$chrs = 'abcdefghijklmnopqrstuvwxyz0123456789_';
$str = '';
$upper_lim = strlen($chrs) - 1;
for ($i = 0; $i < $len; $i++) {
$idx = rand(0, $upper_lim);
$str .= rand(0, 1) ? strtoupper($chrs[$idx]) : $chrs[$idx];
}
return $str;
}
function magic_set_function($key, $value) {
$_SESSION[$key] = $value;
}
function magic_get_function($key) {
return (array_key_exists($key, $_SESSION) ? $_SESSION[$key] : NULL)
}
function validate_request() {
$data = !empty($_POST) ? $_POST : $_GET;
if ( empty($data) ) { return false; }
// Ensure the tokens exist (hopefully not too costly)
$field_tokens = magic_get_function('field_tokens');
if ( $field_tokens) === NULL ) { return false; }
$csrf_token_name = $field_tokens['token'];
$given_csrf_token = $data[$csrf_token_name];
// Get our CSRF token
$expected_csrf_token = magic_get_function('csrf_token');
// ensure we're expecting a request / that we have generated a CSRF
if ( $expected_csrf_token === NULL ||
$expected_csrf_token !== $given_csrf_token) {
return FALSE;
}
// After whatever other checks you want...
return TRUE;
}
function fetch_data() {
$data = empty($_POST) == FALSE ? $_POST : $_GET;
if (empty($data ) { throw new DataLoadException(); }
// Ensure the tokens exist (hopefully not too costly)
$field_tokens = magic_get_function('field_tokens');
if ( $field_tokens) === NULL ) { throw new TokenLoadException(); }
foreach ($field_tokens as $field_name => $token_name) {
if ( isset($data[$token_name]) ) {
$data[$field_name] = $data[$token_name];
unset($data[$token_name]);
}
}
return $data;
}
// first.php/a.php/b.php (wherever necessary)
// ...
$tokens = array();
// our csrf token
$csrf_token = token();
$field_names = array('value', 'bar', 'token');
$field_values = array('value'=>'foo', 'bar' => 'none', 'token' => $csrf_token);
// Tokenize errthing...
foreach ($field_names as $k => $field_name) {
// and generate random strings
$tokens[$field_name] = token();
}
// You NEED TO STORE THESE TOKENS otherwise submissions lose context
magic_set_function('field_tokens', $tokens);
magic_set_function('csrf_token', $csrf_token); // dup, but j.i.c.
// first.php
printf('<input type="hidden" name="%s" value="%s"/>', $tokens['value'], $field_values['value']);
// ...
// a.php
// Get the data... (POST/GET)
if (ensure_valid_request() !== TRUE) { handle_invalid_request(); }
$data = fetch_data();
// ...
// Tokenize errthing, generate a csrf, store the values, etc.
// ...
printf('<input type="hidden" name="%s" value="%s"/>', $tokens['bar'], $field_values['bar']);
// ...
// b.php
// ... You get the idea ...
It doesn't answer your question of why 5% are sending GET Requests but it does solve your overall problem on both a security and user level.
EDIT:
To specifically answer OPs questions in comments:
"(1) does this require using cookies? (a session means cookies right?)"
Read up on PHP Sessions and look for a session library. Plenty out there, one heavyweight being Zend(http://framework.zend.com/manual/1.12/en/zend.session.html). You can save to a database instead for protected server-side sessions. I made one similar to Kohana's.
(2) I didn't understand the "another way" part - how does it differ from the method you described at first?
First method is to just add a token to your form and look for the token to have the expected value upon submission. If the form doesn't have it, you throw an error complaining.
Second method dynamically sets the field names upon form generation AND adds a token field. Submitting the proper form data from a program, bot, or outside source now first requires fetching the form since they wont know what field names to use (instead of just posting data with set field names).
"(3) most important, I am less worried about CSRF attacks, I just don't want bots/crawler to crawl into my forms, would this method prevent it from them, as opposed to humans? why? and is there an easier method to achieve that?"
If you mean bots like Google/SEO/respectful web-crawlers, robots.txt exists
for this purpose. robots.txt is a very simple text file that is placed in your site's root directory. You'll see requests in your webserver's access logs for a /robots.txt. This file tells search engine and other robots which areas of your site they are allowed to visit and index. You can read more on the (Robot Exclusion Standard)4 on many (websites)5.
As the second link notes, don't use robots.txt to hide information. It is a public file and visible to anyone. Also, malicious bots wont respect the file.
I'm not sure if when you say bots you mean just crawlers or spambots (bots trying to submit data) and such. If it's crawlers, robots.txt takes care of them. If it's spambots, you can add a hidden field (hidden with CSS not html) with a common name that when filled out you know is invalid, you can add a captcha, etc, etc, etc.
Try doing the tracking on the callback of the original request to ensure its loaded?
Also you could look into something like ajaxFormPlugin by malsup
i would like to suggest to check the permission of your "b.php" page. Please make sure the page has "w" permission for all users. this is a chance for not making a "POST" request.
I know it's a workaround but if, as I suppose, you have a bunch of checks for the $_POST variables, if you receive a GET request you could try replace the POST with the GET:
if (empty($_POST) && !empty($_GET)) $_POST = $_GET;
//here the check of $_POST
//...
since we don't know why this ipads (...apple -.-) have the issue, and between GET and POST there isn't so much difference - at least if you don't need to upload files...
The only way a post form can be sent as get is using script (changing the method attribute directly, or replacing the form behavior for example with an ajax request, binding to the event "submit" another function), so I suggest you to check every script that run in the parent and the children pages.
your ajax call doesn't contain method: "POST". This can be the cause.