Client-Side Timestamp Manipulation and Trust Issues
Applications that accept and trust client-provided timestamps for business logic decisions allow attackers to manipulate time values to bypass restrictions, access features outside permitted time windows, or exploit time-sensitive functionality.
Preview example – JAVASCRIPT
// VULNERABLE: Client-side timestamp manipulation vulnerabilities
const express = require('express');
const app = express();
// PROBLEM: Trusting client-provided timestamps for business logic
app.post('/api/schedule-payment', async (req, res) => {
const {
amount,
recipientId,
scheduledTime, // DANGEROUS: Client-provided timestamp
timezone // DANGEROUS: Client-provided timezone
} = req.body;
try {
// PROBLEM 1: Using client timestamp without validation
const scheduledDate = new Date(scheduledTime);
// VULNERABLE: Basic validation allows past dates
if (scheduledDate < new Date()) {
return res.status(400).json({ error: 'Cannot schedule payment in the past' });
}
// PROBLEM 2: Not validating reasonable future limits
const payment = await ScheduledPayment.create({
userId: req.user.id,
amount,
recipientId,
scheduledTime: scheduledDate, // Using client time
timezone, // Using client timezone
status: 'scheduled',
createdAt: new Date()
});
res.json({
success: true,
paymentId: payment.id,
scheduledFor: scheduledDate
});
} catch (error) {
res.status(500).json({ error: 'Payment scheduling failed' });
}
});
// VULNERABLE: Promotional access based on client time
app.post('/api/apply-promotion', async (req, res) => {
const {
promoCode,
currentTime, // DANGEROUS: Client-provided current time
userTimezone // DANGEROUS: Client-provided timezone
} = req.body;
try {
const promotion = await Promotion.findOne({ code: promoCode });
if (!promotion) {
return res.status(404).json({ error: 'Promotion not found' });
}
// PROBLEM: Using client-provided time for validation
const clientCurrentTime = new Date(currentTime);
// VULNERABLE: Time window validation using client time
if (clientCurrentTime < promotion.startTime ||
clientCurrentTime > promotion.endTime) {
return res.status(400).json({ error: 'Promotion not currently active' });
}
// PROBLEM: Timezone manipulation opportunities
const userLocalTime = this.convertToTimezone(clientCurrentTime, userTimezone);
// Apply promotion without server-side time verification
const result = await applyPromotionToUser(req.user.id, promotion.id);
res.json({
success: true,
promotion: promotion.name,
appliedAt: clientCurrentTime
});
} catch (error) {
res.status(500).json({ error: 'Promotion application failed' });
}
});
// PROBLEM 3: Time-based feature access with client control
app.get('/api/daily-bonus', async (req, res) => {
const { requestTime, timezone } = req.query;
try {
const user = await User.findById(req.user.id);
// VULNERABLE: Using client-provided request time
const clientRequestTime = new Date(requestTime || Date.now());
// Get last bonus claim time
const lastBonusTime = user.lastBonusClaimedAt;
if (lastBonusTime) {
// PROBLEM: Time difference calculation using client time
const timeDiff = clientRequestTime - lastBonusTime;
const hoursSinceLastBonus = timeDiff / (1000 * 60 * 60);
// VULNERABLE: 24-hour cooldown based on client time
if (hoursSinceLastBonus < 24) {
return res.status(429).json({
error: 'Daily bonus already claimed',
nextAvailableIn: (24 - hoursSinceLastBonus) * 60 * 60 * 1000
});
}
}
// Grant daily bonus
user.dailyBonusPoints += 100;
user.lastBonusClaimedAt = clientRequestTime; // DANGEROUS: Using client time
await user.save();
res.json({
success: true,
bonusPoints: 100,
claimedAt: clientRequestTime
});
} catch (error) {
res.status(500).json({ error: 'Bonus claim failed' });
}
});
// VULNERABLE: Event scheduling with time manipulation
app.post('/api/events/:eventId/register', async (req, res) => {
const { eventId } = req.params;
const { registrationTime } = req.body;
try {
const event = await Event.findById(eventId);
if (!event) {
return res.status(404).json({ error: 'Event not found' });
}
// PROBLEM: Using client-provided registration time
const regTime = new Date(registrationTime || Date.now());
// VULNERABLE: Early bird pricing based on client time
const isEarlyBird = regTime < event.earlyBirdDeadline;
const price = isEarlyBird ? event.earlyBirdPrice : event.regularPrice;
// PROBLEM: Registration deadline check using client time
if (regTime > event.registrationDeadline) {
return res.status(400).json({ error: 'Registration deadline has passed' });
}
const registration = await EventRegistration.create({
userId: req.user.id,
eventId,
price,
isEarlyBird,
registeredAt: regTime // DANGEROUS: Client-controlled timestamp
});
res.json({
success: true,
registrationId: registration.id,
price,
isEarlyBird
});
} catch (error) {
res.status(500).json({ error: 'Event registration failed' });
}
});
// Attack scenarios:
// 1. Set scheduledTime far in future to bypass payment limits
// 2. Manipulate currentTime to access expired promotions
// 3. Set requestTime in past to bypass daily bonus cooldowns
// 4. Use registrationTime before early bird deadline
// 5. Timezone manipulation to extend access windows