I have an app that displays data to users based on the date. I send an array from the server to the client, on load, which contains 3 objects with some data - effectively an object for 'yesterday', 'today' and 'tomorrow' in UTC, since at most there can be 3 different dates across the world. Each object contains some data for that date.
E.g:
[
{dateStamp: '2022-05-31', data: 'some data for 31 May'},
{dateStamp: '2022-06-01', data: 'some data for 1 Jun'},
{dateStamp: '2022-06-02', data: 'some data for 2 Jun'}
]
In JS, I then decide what timezone the client is in and pick the appropriate object to show them (UTC-1 day, UTC today, UTC+1 day). I do this based on a simple Date object. From that object I generate a timestamp string (YYYY-MM-DD) and then match it against the 3 objects' timestamp strings (generated in PHP with the same format).
But I'm noticing in a really odd error in my activity logs. (This is data I post from clients via AJAX and store on the server for monitoring/debugging). I first spotted it when some users were getting shown data for tomorrow.
Some users (not many) are reporting UNIX timestamps (from Date.now()
) that are in the future (whilst appearing to report a UTC offset that is correct).
The reported UTC offsets are generated in JS at the client and sent via AJAX as such:
let nowDate = new Date();
let utcOffset = nowDate.getTimezoneOffset();
if (!isEmpty(utcOffset) && !isNaN(utcOffset)) { // isEmpty is my own function to match the PHP empty() function
utcOffset = (-1 * utcOffset) / 60;
}
else {
utcOffset = undefined;
}
Now obviously this data is coming from client, so a malicious user could simply use the console to manipulate this to anything they like as it's being sent.
But it's a small app with relatively few users and that seems extremely unlikely to me.
So, excluding a malicious user, some clients apparently have their local clock set wrong (set to the future). Why?? Or how?? How are they in the right timezone but the wrong clock time by several days? This is not an app that attracts 1000s of users - I have 100s and I pretty much know they're all accessing via fairly normal means, primarily via mobiles - I know it's an assumption but I would have expected their clocks to be (approximately) correct.
What's the best way of checking what the actual time in their timezone is? I'm thinking I send the UTC offset to the server via AJAX and my server reports back what the date is in that timezone?
For completeness, here is what/how I'm sending to the server from the client:
function getDateStamp() { // Aware this function is verbose, haven't had time to improve
let now = new Date();
let todayDay = now.getDate().toString();
let todayMonth = (now.getMonth() + 1).toString();
let todayYear = now.getFullYear().toString();
if (!isEmpty(todayDay) && todayDay.length < 2) {
todayDay = "0" + todayDay;
}
if (!isEmpty(todayMonth) && todayMonth.length < 2) {
todayMonth = "0" + todayMonth;
}
let fullDateStamp = todayYear + "-" + todayMonth + "-" + todayDay;
if (!isEmpty(todayYear) && !isEmpty(todayMonth) && !isEmpty(todayDay)) {
return fullDateStamp;
}
else {
return false;
}
}
let clientDateStamp = getDateStamp();
let clientUnixTimestamp = Math.floor(Date.now() / 1000);
let objectToSend = {dateStamp: clientDateStamp, unixTimestamp: clientUnixTimestamp};
var dataTransfer = $.ajax({ // Using JQuery for AJAX post
url: serverEndpointURL, // Where serverEndpointURL is an HTTPS url to my server script
type: "POST",
dataType: "json",
data: {json: objectToSend}
});
In PHP, on the server I decode that JSON:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$json = $_POST["json"];
$postData = json_decode($json, true);
}
error_log($postData["unixTimestamp"] . "\r\n",3, $errorLogFile); // $errorLogFile points to a txt file
In $errorLogFile
I can see timestamps in the future. I.e. $postData["unixTimestamp"] > time()
would have been true at the time of logging.