I am new to this, so far i was just using html form, click the submit button, the page was refreshing and the data was sented to the server (mySQL). But i learned Ajax (AJAX is a developer's dream) they are saying cause you can:
Read data from a web server - after the page has loaded
Update a web page without reloading the page
Send data to a web server - in the background
So i did a simple example. Let's say that i have set the sqlConnection.php
let input = document.getElementById("inputField");
document.getElementById("submitBtn").addEventListener("click", function (){
if(input.value == ""){
alert("empty field");
}else {
$(document).ready(function (){
$.ajax({
url: 'insert.php',
method: 'POST',
dataType: 'text',
data: { comment: input.value },
success: function (){
input.value = "";
}
});
});
}
});
function selectQuestions(){
let data = "true";
$("#comments").load("select.php");
}
setInterval(selectQuestions, 3000);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax text</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
</head>
<body>
<div class="container py-3">
<input id="inputField" type="text" class="form-control text-left" aria-label="">
<div class="py-2 my-1">
<button id="submitBtn" value="addNew" class="btn btn-success">Submit</button>
</div>
</div>
<div class="container" id="comments">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="main.js"></script>
</body>
</html>
select.php has this:
$sql = "SELECT * FROM data";
$result = $conn->query($sql);
if (mysqli_num_rows($result) > 0) {
while ($row = mysqli_fetch_assoc($result)) {
echo "<p>{$row['question']}</p>";
}
} else {
echo "No comments";
}
So my Question: Is this correct in order to see new results that came from the server ? I call the setInterval method every 3 seconds as you can see. Is this bad for the server ? For example if i upload this project to a server and 10 users are using it.. am i exhausting the server - consuming more space ?
Assuming your code is in main.js, you don't need the $(document).ready(), since at the bottom of the <body>, your code will only run after everything is ready anyway. Not to mention it's in the wrong place.
At first I thought you were using .load() wrong, since its a shorthand for the event handler .on('load'). But it turns out in jquery 3.0 they've added an ajax method also called .load(). This is dumb and going to cause alot of confusion, but I digress, use $.get() or $.getJson to get things from a server.
Hitting the server with an interval isn't necessarily bad, it's called polling and it's how the internet did full duplex communication for many years. The problem comes when you poll too often or want to update too much data or have too many users all polling at once. How much is too much all depends on the machine your server is running on. Websockets is definitely a better option, but quite a bit more complex to configure.
I took the liberty to re-write some things, since if you're using jquery, might as well use jquery.
let $input = $('#inputField');
$('#submitBtn').on('click', function(e) {
e.preventDefault(); //assuming this is in a <form>, default action is to reload the page
if ($input.val()) {
$.post('insert.php', { comment: $input.val() })
.done(function() {
$input.val('');
})
.fail(function(error) {
//error handling code
});
} else {
alert("empty field"); //alerts are annoying and thread blocking,
} //maybe use a different method to communicate with the user.
});
setInterval(function() {
$.get('select.php')
.done(function(select) {
//whatever you want to do with the response from this
})
.fail(function(error) {
//error handeling code
});
}, 3000);
On the php side, maybe just clean things up a bit so that it's more readable. You can use the ->fetch_all(MYSQLI_ASSOC) method instead of a while loop to get an associated array for the result. Then just array_map() and implode() that to build you html response.
<?php
$conn = new mysqli("configs and what not");
$statement = $conn->query("SELECT * FROM data");
$result = $statement->fetch_all(MYSQLI_ASSOC);
if (count($result) > 0) {
echo implode("", array_map(fn($row) => "<p>{$row["question"]}</p>", $result));
} else {
echo "No comments";
}
$conn->close();
Related
I'm building a photography portfolio. Some of my images have nudity, so I want to hide those by default until the user clicks a "Toggle Worksafe Mode" button.
I can do it with a standard form post (and sessions), but that causes "confirm form resubmission" errors when the user backs or reloads. I'm trying to figure out an AJAX post instead to avoid that.
UPDATE: This is the working code. Please note that this does NOT work with the "slim" jQuery distro; that's one of the main reasons I was having trouble.
Image Index Page:
<?php
session_start();
if (!isset($_SESSION['Worksafe_Mode'] {
$_SESSION['Worksafe_Mode'] = 1;
}
?>
<!-- other page content -->
<script src="scripts/jquery-3.2.1.min.js"></script>
<!-- other page content -->
<button type="button" id="Worksafe_Button" name="Worksafe_Button">
Toggle Worksafe Mode
</button>
<script>
$('#Worksafe_Button').click(function() {
$.post("worksafe_mode_toggle.php")
.done(function(data) {
window.location.href = window.location.href;
});
});
</script>
<!-- other page content -->
<?php
$Connection = Connect();
$query = mysqli_query($Connection, 'SELECT uri, name, nsfw FROM images ORDER BY uri');
while($row = mysqli_fetch_assoc($image)) {
if ($_SESSION['Worksafe_Mode'] == 1 && $row['nsfw'] == 1) {
echo 'If you are over 18, toggle Worksafe Mode to view this image';
}
else {
echo '<img alt="'.$row['title'].'" src="../'.$row['uri'].'/s.jpg" srcset="../'.$row['uri'].'/m.jpg 2x">';
}
}
?>
worksafe_mode_script:
session_start();
if (isset($_SESSION['Worksafe_Mode'])) {
if ($_SESSION['Worksafe_Mode'] == 1) {
$_SESSION['Worksafe_Mode'] = 0;
}
else {
$_SESSION['Worksafe_Mode'] = 1;
}
}
I think ajax is a good approach in your case.
I might do something like display a page of SFW images as the default, along with the toggle button.
When they click the button it triggers an ajax request to the back-end that sets/un-sets the session value in toggleWorksafe.php. Finally it triggers a page refresh.
During the page refresh the PHP code checks whether the session variable is set and shows either the filtered or unfiltered set of images, and changes the button's text to match.
To implement:
Include jQuery in the <head> section (jQuery simplifies the ajax call):
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
</head>
<body>
<?php
session_start();
if (!isset($_SESSION['Worksafe_Mode'])) {
$_SESSION['Worksafe_Mode'] = 'yes';
}
?>
<button id="workSafe" type="button" name="Worksafe_Toggle_Button">
<?php
if ($_SESSION['Worksafe_Mode'] == 'no') {
echo 'Hide NSFW images';
}
else {
echo 'Include NSFW images';
}
?>
</button>
<!-- display safe images by default -->
<?php
if ($_SESSION['Worksafe_Mode'] == 'no') {
echo '<br/><br/>Showing NSFW images';
}
else {
echo '<br/><br/>Showing safe images only';
}
?>
<!-- any other page content here -->
<script>
$('#workSafe').click(function() {
// ajax request to page toggling session value
$.post("/toggleWorksafe.php")
.done(function(data) {
window.location.href = window.location.href; // trigger a page refresh
});
});
</script>
</body>
</html>
toggleWorksafe.php:
<?php
session_start();
if (isset($_SESSION['Worksafe_Mode'])) {
if ($_SESSION['Worksafe_Mode'] == 'yes') {
$_SESSION['Worksafe_Mode'] = 'no';
}
else {
$_SESSION['Worksafe_Mode'] = 'yes';
}
}
else {
$_SESSION['Worksafe_Mode'] = 'yes';
}
?>
there are a couple of ways to do this and it related to how you hide or load you images.
1. simple method
if you don't care about the user's age, and just need to toggle, then you can do it with just a js variable, a cookie, and two version of link. with this, you don't hide images, but loads them. the filtering is done in the server, where you can use database query or a simple folder separation. for example:
var nsfw = read_cookie('nsfw', false); // not an actual js function, search for how to read cookie in js --- read cookie value, default to false
function loadImage(nsfw){
if (nsfw){
$.get('nsfw-image-list-url', function(resp){
// the url should return a json with list of image urls
var list = resp; // jQuery automatically parse json with the right MIME
list.forEach(function(val){
// insert image to page
$('#container').append('<img src="' + val + '/>');
});
});
} else {
$.get('sfw-image-list-url', function(resp){
// the url should return a json with list of image urls
var list = resp; // jQuery automatically parse json with the right MIME
list.forEach(function(val){
// insert image to page
$('#container').append('<img src="' + val + '/>');
});
});
}
}
and in you button click event:
nsfw = !nsfw;
// clear the image first if needed
$('#container').empty();
loadImage(nsfw);
2. another simple method, but not as simple as the #1
you can also do it with only one link that returns a list of images with the type of it, such as nsfw or other things.
note: this method still uses cookie
for example the returned list is like this:
[
{"url": "some-image-1.jpg", "nsfw": "true"},
{"url": "some-image-2.jpg", "nsfw": "false"},
{"url": "some-image-3.jpg", "nsfw": "true"},
{"url": "some-image-4.jpg", "nsfw": "false"},
{"url": "some-image-5.jpg", "nsfw": "false"},
{"url": "some-image-6.jpg", "nsfw": "true"}
]
then you just render it when the conditions are met.
function renderImage(nsfw){
$.get('image-list-url', function(resp){
list.forEach(function(val, key){
if (nsfw || !val.nsfw){
$('#container').append('<img src="' + val.url + '/>');
}
});
});
}
and many other methods that are too long to explain, such as using Angular, React, or Vue
still uses cookie for between reloads or backs, and does not regard user's age.
as for the session based approach, you only need that if you need to verify your users age
that is if you have a membership functionality with DOB (date of birth) data in your site, if so, you can use #KScandrett 's answer
Confirm form resubmission happens because you do not perform a redirect after a successful form submission.
Take a look at this wiki page to see how to do it right. https://en.wikipedia.org/wiki/Post/Redirect/Get
I would like to recreate several like button that saves count in a file.txt but that doesn't work :/
<?php
function getClickCount()
{
return (int)file_get_contents("counter.txt");
}
function incrementClickCount()
{
$counter = getClickCount() + 1;
file_put_contents("counter.txt", $counter);
}
?>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<script type="text/javascript">
var clicks = 0;
function onClick() {
clicks = 1;
document.getElementById("clicks").innerHTML = clicks;
};
</script>
<button type="button" onClick="onClick()" title="Vous aimez la couverture?" class="btn"><img id="heart" src="https://trello-attachments.s3.amazonaws.com/568304b85fa72dcb958a1edf/584acfc48b82595af77f2030/6257bf1efec79d5baf22309f8f327ce5/favorite.png" /></button>
<p><a id="clicks"><?php echo getClickCount(); ?></a></p>
DEMO HERE
Thanks in advance for your help, I am looking since days on the web to find it but I don't...
Alexander
counter.php
<?php
function getClickCount() {
return (int)file_get_contents("counter.txt");
}
function incrementClickCount() {
$counter = getClickCount() + 1;
file_put_contents("counter.txt", $counter);
}
if(!empty($_POST)) {
if($_POST['click'] == 'true') {
incrementClickCount();
echo getClickCount();
} else {
echo getClickCount();
}
}
?>
counter.txt
0
index.php
<html>
<head>
<title>Click Counter</title>
</head>
<body>
<button type="button" onClick="onClick()" title="Vous aimez la couverture?" class="btn"><img id="heart" src="https://trello-attachments.s3.amazonaws.com/568304b85fa72dcb958a1edf/584acfc48b82595af77f2030/6257bf1efec79d5baf22309f8f327ce5/favorite.png" /></button>
<p><a id="clicks"></a></p>
<script>
function onClick() {
loadClicks(true);
}
function loadClicks(isClicked) {
var click = isClicked === true ? true : false;
$.ajax({
url: 'counter.php',
type: 'POST',
data: {
'click': click
},
success: function(response) {
$('#clicks').text(response);
}
});
}
loadClicks(false);
</script>
</body>
</html>
Code Explanation
Whenever you click the button, there is an ajax request sent asynchronously in the background to counter.php. This PHP file receives request and process accordingly.
Here in the code, we send a single data to the PHP file in the ajax POST request which is a boolean data that is set based on the condition like if the button is clicked.
In PHP file, you will check a condition if the request is happened by button click or else other. If it is by button, you will increment the click and send the click counter value in response else you will only send the value.
You will notice I've called loadClicks function with the parameter true in onClick function and false outside the function meaning that I first call the loadClicks(false) as soon as the script is started its execution to load only the clicks value and later when I click the button loadClicks(true) is invoked meaning increment and fetch the value.
You will understand the code when you go through them carefully.
At first glance, I see 3 problems with your script.
1) You are mixing JavaScript and PHP. JavaScript runs on browsers and PHP runs on servers. If you want to exchange data between those parts of your script you need to make a server call from the JS part to the server, e.g. by using AJAX. A simple HTML request in JavaScript to a PHP script will work too.
2) Also your <button> tag needs to be embedded in a <form> should point to a script to be executed (can be the same script).
3) You never seem to call incrementClickCount(), at least not in the part shown here.
Suggestions
The would code everything in PHP and then address the other two points. Or you need to implement some form of client / server communication.
I'm trying to make a page which will generate a result-set from a complex database query & php parsing... but that's mainly beside the point... The main point is that this takes a minute or two to complete, and I'm hoping to display a progress bar rather then a generic gif animation "loading..." picture.
A breakdown would be...
User opens Page A.
Page A requests data from Page B (Most likely AJAX).
Page B processes the 100000+ or so entries in the database and parses them.
Page A shows a progress bar which shows roughly how far through the process is
Page B returns the result set.
Page A displays the result set.
I know how to return data to the ajax query, but my issue is I don't know how to continuously return data to show the status of the process (Eg. % of rows scanned).
I've looked into EventSource / Server-Sent-Events, which shows promise, I'm just not too sure how to get it working properly, or if there is a better way to do it.
I've tried making a quick little mock-up page, using just EventSource works fine, but when I split it up into an eventSource call (page which monitors a session variable for change), and an ajax request (the actual data sending/return) it falls apart.
I'm probably missing something obvious, or doing something stupidly wrong, but this is most of what I have anyway... Any help, suggestions, tips, or even suggestions of completely other ways to do it would be awesome :)
User page:
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Progress Bar Example</title>
<script src="script.js"></script>
</head>
<body>
<input type="button" value="Submit" onclick="connect()" />
<progress id='progressor' value="0" max='100' style=""></progress>
</body>
</html>
Javascript
var es;
function connect() {
startListener();
$.ajax({
url: "server.php",
success: function() {
alert("Success");
},
error: function() {
alert("Error");
}
});
}
function startListener() {
es = new EventSource('monitor.php');
//a message is received
es.addEventListener('message', function(e) {
var result = JSON.parse(e.data);
if (e.lastEventId == 'CLOSE') {
alert("Finished!");
es.close();
} else {
var pBar = document.getElementById('progressor');
pBar.value = result;
}
});
es.addEventListener('error', function(e) {
alert('Error occurred');
es.close();
});
}
function stopListener() {
es.close();
alert('Interrupted');
}
function addLog(message) {
var r = document.getElementById('results');
r.innerHTML += message + '<br>';
r.scrollTop = r.scrollHeight;
}
Monitor PHP
<?php
SESSION_START();
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');
function send_message($id, $data) {
$d = $data;
if (!is_array($d)){
$d = array($d);
}
echo "id: $id" . PHP_EOL;
echo "data: " . json_encode($d) . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$run = true;
$time = time();
$last = -10;
while($run){
// Timeout kill checks
if (time()-$time > 360){
file_put_contents("test.txt", "DEBUG: Timeout Kill", FILE_APPEND);
$run = false;
}
// Only update if it's changed
if ($last != $_SESSION['progress']['percent']){
file_put_contents("test.txt", "DEBUG: Changed", FILE_APPEND);
$p = $_SESSION['progress']['percent'];
send_message(1, $p);
$last = $p;
}
sleep(2);
}
?>
EDIT:
I've tried a different approach, where:
Page A AJAX calls page B, which runs the request, and saves the progress to a SESSION variable
Page A AJAX calls page C every 2 seconds, which simply returns the value of the session variable. This loop is terminated when it reaches 100
However, this is not quite working either. It seems that the two AJAX requests, or the two scripts server-side are not running simultaneously.
Looking at debug output: Both AJAX calls are executed at about the same time, but then the page B script runs to completion by itself, and -then- the page C script runs. Is this some limitation of PHP I'm missing???
more code!
Server (Page B) PHP
<?PHP
SESSION_START();
file_put_contents("log.log", "Job Started\n", FILE_APPEND);
$job = isset($_POST['job']) ? $_POST['job'] : 'err_unknown';
$_SESSION['progress']['job'] = $job;
$_SESSION['progress']['percent'] = 0;
$max = 10;
for ($i=0; $i<=$max;$i++){
$_SESSION['progress']['percent'] = floor(($i/$max)*100);
file_put_contents("log.log", "Progress now at " . floor(($i/$max)*100) . "\n", FILE_APPEND);
sleep(2);
}
file_put_contents("log.log", "Job Finished", FILE_APPEND);
echo json_encode("Success. We are done.");
?>
Progress (Page C) PHP
<?php
SESSION_START();
file_put_contents("log.log", "PR: Request Made", FILE_APPEND);
if (isset($_SESSION['progress'])){
echo json_encode(array("job"=>$_SESSION['progress']['job'],"progress"=>$_SESSION['progress']['percent']));
} else {
echo json_encode(array("job"=>"","progress"=>"error"));
}
?>
Index (Page A) JS/HTML
<!DOCTYPE html>
<html>
<head>
<title>Progress Bar Test</title>
</head>
<body>
<input type="button" value="Start Process" onclick="start('test', 'pg');"/><br />
<progress id="pg" max="100" value="0"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript">
var progress = 0;
var job = "";
function start(jobName, barName){
startProgress(jobName, barName);
getData(jobName);
}
function getData(jobName){
console.log("Process Started");
$.ajax({
url: "server.php",
data: {job: jobName},
method: "POST",
cache: false,
dataType: "JSON",
timeout: 300,
success: function(data){
console.log("SUCCESS: " + data)
alert(data);
},
error: function(xhr,status,err){
console.log("ERROR: " + err);
alert("ERROR");
}
});
}
function startProgress(jobName, barName){
console.log("PG Process Started");
progressLoop(jobName, barName);
}
function progressLoop(jobName, barName){
console.log("Progress Called");
$.ajax({
url: "progress.php",
cache: false,
dataType: "JSON",
success: function(data){
console.log("pSUCCESS: " . data);
document.getElementById(barName).value = data.progress;
if (data.progress < 100 && !isNaN(data.progress)){
setTimeout(progressLoop(jobName, barName), (1000*2));
}
},
error: function(xhr,status,err){
console.log("pERROR: " + err);
alert("PROGRESS ERROR");
}
});
}
</script>
</body>
</html>
Debug: log.log output
PR: Request Made
Job Started
Progress now at 0
Progress now at 10
Progress now at 20
Progress now at 30
Progress now at 40
Progress now at 50
Progress now at 60
Progress now at 70
Progress now at 80
Progress now at 90
Progress now at 100
Job Finished
PR: Request Made
In similar cases, I usually do it this way:
Client sends AJAX request to Page B.
Important: On success, client sends the same request again.
On the initial request, Page B says: OK, THERE ARE 54555 RECORDS.. I use this count to initiate the progress bar.
On each of next requests, Page B returns a chunk of data. Client counts the size of chunk and updates progress bar. Also it collects chunks in one list.
On last request, when all data is sent, Page B says: THAT'S ALL and client renders the data.
I think, you've gotten the idea.
NOTE: you can request all chunks in parallel, but it is a complex way. Server (Page B) should also return a fixed chunksize in the initial response, then client sends TOTAL_COUNT / CHUNK_SIZE requests concurrently and combines the responses till the last request is completed. So it is much faster. You can use https://github.com/caolan/async in this case to do the code much more readable.
suppose I work with some kind of API and my file server.php handles the connection to the API service. on my client side I use AJAX call like this:
$http({
url : 'server/server.php',
method : 'GET',
data : { getContent : true }
});
in my server.php I handle it like this:
if(isset($_GET['getContent'])){
$content = get_content();
}
function get_content(){...}
i just wonder what prevents any one send AJAX call with the same getContent parameter and get all my data? how can i secure it and make sure only calls from my application will get the relevant data back?
thank you!
I guess you are concerned about CSRF attacks. Read more about this here: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet
One of the mostly used option to secure your request will be:
- Generate a token and send it with the request for a session. This token can be identified by your WebServer as originating from a specific client for a specific session
2022 Update
This is a 7 year old post and the link in the link-only "accepted" answer is broken.
So, I'm going to offer a basic walkthrough and a complete model.
Remember, the $_SESSION will be preserved even in the AJAX handler, if it's all from the same domain. So, you can use that to check things.
Use $_POST
I presume you're using $_POST and not $_GET since you're concerned about security. If not, then much of this might not be important anyway.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_method = true;
}
Ensure the $_SERVER['HTTP_REFERER'] is from your own site
if ( (!empty($_SERVER['HTTP_REFERER']))
&& ($_SERVER['HTTP_REFERER'] === "https://example.tld/my_sending_page.php") ) {
$from_my_server = true;
}
If you're not sure what this should be, run a test on your own server to see what this should be:
echo $_SERVER['HTTP_REFERER'];
Verify XMLHTTP/AJAX request via $_SERVER array
if ( (!empty($_SERVER['HTTP_X_REQUESTED_WITH']))
&& ( strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') ) {
$ajax = true;
} else {
$ajax = false;
}
Use a token
This is the hard part, but not too hard.
Create the token
Set the token in $_SESSION
Put the token in the AJAX header
AJAX responder: confirm the AJAX header token with the $_SESSION token
send_from_me.php
// Create the token
//$token = md5(rand(10000,99999)); // Not recommended, but possible
$token = bin2hex(random_bytes(64));
// Store in SESSION
$_SESSION["token"] = $token;
// Assuming your AJAX is this
const AJAX = new XMLHttpRequest();
// This goes inside your AJAX function somewhere before AJAX.send
//
AJAX.setRequestHeader("ajax-token", "<?php echo $_SESSION["token"]; ?>");
//
// That creates $_SERVER['HTTP_AJAX_TOKEN'] which we can use later
ajax_responder.php
session_start(); // Must have
if ($_SERVER['HTTP_AJAX_TOKEN'] === $_SESSION["token"]) {
$token_match = true;
} else {
echo "No script kiddies!";
exit();
}
// Now it's safe for your AJAX responder to proceed
Let's put all of this into a working example
sending_from.php
<?php
session_start();
$token = bin2hex(random_bytes(64));
$_SESSION["token"] = $token;
?>
<!DOCTYPE html>
<html>
<head>
<title>My AJAX Sender</title>
</head>
<body>
<script>
function ajaxFormData(formID, postTo, ajaxUpdate) {
// Bind a new event listener every time the <form> is changed:
const FORM = document.getElementById(formID); // <form> by ID
const FD = new FormData(FORM); // Bind to-send data to form element
const AJAX = new XMLHttpRequest(); // AJAX handler
// This runs when AJAX responds
AJAX.addEventListener( "load", function(event) {
document.getElementById(ajaxUpdate).innerHTML = event.target.responseText;
} );
// This runs if AJAX fails
AJAX.addEventListener( "error", function(event) {
document.getElementById(ajaxUpdate).innerHTML = 'Oops! Something went wrong.';
} );
// Add your token header
AJAX.setRequestHeader("ajax-token", "<?php echo $_SESSION["token"]; ?>");
// Open the POST connection
AJAX.open("POST", postTo);
// Data sent is from the form
AJAX.send(FD);
}
</script>
<div id="ajax_changes">Replace me with AJAX</div>
<form id="ajaxForm">
<input type="text" name="the_simple_response">
<button type="button" onclick="ajaxFormData('ajaxForm', 'ajax_responder.php', 'ajax_changes');">Send my Secure AJAX</button>
</form>
</body>
</html>
ajaxcheck.inc.php
<?php
$mysite = 'https://example.tld';
// All in one test
if (($_SERVER['REQUEST_METHOD'] == 'POST')
&& ((!empty($_SERVER['HTTP_REFERER'])) && ($_SERVER['HTTP_REFERER'] === "$mysite/my_sending_page.php"))
&& ((!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) && ( strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'))
&& ($_SERVER['HTTP_AJAX_TOKEN'] === $_SESSION["token"])) {
$ajax_legit = true;
} else {
echo "No script kiddies!";
exit();
}
?>
ajax_responder.php
<?php
session_start();
// Do all that checking we're learning about by neatly including the file above
require_once('ajaxcheck.inc.php');
// Process your AJAX
echo $_POST['the_simple_response'];
?>
i just wonder what prevents any one send AJAX call with the same getContent parameter and get all my data?
Nothing. This URL is public thus anyone can make requests to it.
how can i secure it and make sure only calls from my application will get the relevant data back?
You can pass additional data (for example, some hashed value) that is verified on the server side.
$http({
url : 'server/server.php',
method : 'GET',
data : { getContent : true, hash : '0800fc577294c34e0b28ad2839435945' }
});
and
if(isset($_GET['getContent']))
{
if(isset($_GET['hash']) && validateHash($_GET['hash']))
{
$content = get_content();
}
}
function get_content(){...}
i just wonder what prevents any one send AJAX call with the same getContent parameter and get all my data?
The same way you would protect the data in any other request (e.g. with user authentication). There's nothing special about Ajax in regards to HTTP as far as the server is concerned.
how can i secure it and make sure only calls from my application will get the relevant data back?
You can't. The user can always inspect what their browser is asking the server for and replicate it.
Generally, people authenticate users rather than applications.
I have a javascript variable called "list". I need to send it as a POST data to another page and open that page in a new tab (with the POST data present).
This code:
jQuery.post('datadestination.php', list);
sends the data all right, but ofcourse it opens the page in the same tab.
I saw some solutions to similar problems using invisible form and things like that, but I could not get them to work. Is there any simple solution?
You can send a form using the target="_blank" attribute.
<form action="datadestination.php" method="POST" target="_blank" id="myform">
<input type="hidden" name="list" id="list-data"/>
<input type="submit" value="Submit">
</form>
Then in JS:
jQuery('#list-data').val(list);
jQuery('#myform').submit();
This is an implementation of Sergey's solution.
<?php // this is save.php
session_start();
// DO NOT just copy from _POST to _SESSION,
// as it could allow a malicious user to override security.
// Use a disposable variable key, such as "data" here.
// So even if someone passed _POST[isAdmin]=true, all that he would do
// is populate _SESSION[data][isAuthenticated], which nobody reads,
// not the all-important _SESSION[isAuthenticated] key.
if (array_key_exists('data', $_POST)) {
$_SESSION['data'] = $_POST['data'];
$_SESSION['data.timestamp'] = time();
// Let us let the client know what happened
$msg = 'OK';
} else {
$msg = 'No data was supplied';
}
Header('Content-Type: application/json; charset=utf8');
die(json_encode(array('status' => $msg)));
?>
In the first page:
$.post('save.php', { data: list }, function(response){
if (!response.status) {
alert("Error calling save");
return;
}
if (response.status !== 'OK') {
alert(response.status);
return;
}
// We had a response and it was "OK". We're good.
window.open('datadestination.php');
});
And in datadestination.php add the fix:
if (!array_key_exists('data', $_SESSION)) {
die("Problems? Did you perchance attempt to reload the page and resubmit?");
// For if he did, then yes, $_SESSION would have been cleared.
// Same if he is operating on more than one window or browser tab.
}
// Do something to validate data. For example we can use data.timestamp
// to assure data isn't stale.
$age = time();
if (array_key_exists($ts = 'data.timestamp', $_SESSION)) {
$age -= $_SESSION[$ts];
}
if ($age > 3600) {
die("Data is more than one hour old. Did someone change server time?!?");
// I actually had ${PFY} do that to me using NTP + --hctosys, once.
// My own time zone is (most of the year) exactly one hour past GMT.
}
// This is safe (we move unsecurity-ward):
$_POST = $_SESSION['data'];
unset($_SESSION['data'], $_SESSION['data.timestamp']);
// keep things clean.
// From here on, the script behaves "as if" it got a _POST.
Update
You can actually merge save.php and datadestination.php and use a "saving stub" savepost.php that you can recycle in other pages:
<?php
session_start();
// DO NOT just copy from _POST to _SESSION,
// as it could allow a malicious user to override security.
// Use a disposable variable key, such as "data" here.
if (array_key_exists('data', $_POST)) {
// Timestamp sent by AJAX
if (array_key_exists('ts', $_POST)) {
// TODO: verify ts, but beware of time zones!
$_SESSION['data'] = $_POST['data'];
Header("Content-Type: application/json;charset=UTF-8");
die(json_encode(array('status' => 'OK')));
}
die("Error");
}
// This is safe (we move unsecurity-ward):
$_POST = $_SESSION['data'];
unset($_SESSION['data']); // keep things clean.
?>
Now your call becomes
$.post('datadestination.php', { data: list, ts: Date.now() }, function(){
window.open('datadestination.php');
});
and in your datadestination.php (or anywhere else) you add
require 'savepost.php';
I suggest:
Pass that list with the jquery.post() function and save it in the SESSION array.
Open a new tab with the same file/address/URL with the window.open() function.
Retrieve saved data from the SESSION array.
This seems straightforward and clean to me.